Dopex - 0xmystery's results

A rebate system for option writers in the Dopex Protocol.

General Information

Platform: Code4rena

Start Date: 21/08/2023

Pot Size: $125,000 USDC

Total HM: 26

Participants: 189

Period: 16 days

Judge: GalloDaSballo

Total Solo HM: 3

Id: 278

League: ETH

Dopex

Findings Distribution

Researcher Performance

Rank: 89/189

Findings: 1

Award: $96.33

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

96.3292 USDC - $96.33

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
sufficient quality report
edited-by-warden
duplicate-2083

External Links

Lines of code

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1189-L1190 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1160 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1233-L1242 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVault.sol#L104 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVault.sol#L571-L583

Vulnerability details

Impact

The rounding mechanism implemented in PerpetualAtlanticVault.roundUp can lead to significantly inflated values in specific scenarios. When dealing with lower value strikes, the rounding can introduce substantial inaccuracies. This can particularly affect calculations involving the price determination of rDPX against ETH. Such inaccuracies can lead to potential economic imbalances in the ecosystem, making users/protocol either overpay or under-receive assets.

The inflated strike will specifically impact wethRequired when PerpetualAtlanticVault.calculatePremium is externally called:

https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1196-L1197

      wethRequired += IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
        .calculatePremium(strike, rdpxRequired, timeToExpiry, 0);

Proof of Concept

The protocol has repeatedly made this assumption: ETH @ USD 2,000, rDPX @ USD 20 in the full product spec.

But here's the scenario:

  1. Let's say the price of rDPX against ETH has dropped to USD 0.20 while the price of ETH still remains at USD 2,000. In ETH, it's 0.20 / 2000 = 0.0001. With 1e8 precision, it's 0.0001 * 1e8 = 10,000.
  2. Taking 25% off this value: rdpxPrice - (rdpxPrice / 4) = 10,000 - 2,500 = 7,500 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L1189-L1190
    uint256 strike = IPerpetualAtlanticVault(addresses.perpetualAtlanticVault)
      .roundUp(rdpxPrice - (rdpxPrice / 4)); // 25% below the current price
  1. This rounds up to the nearest 1e6 where 7,500 would indeed be rounded up to 1,000,000. https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVault.sol#L571-L583
  /**
   * @dev Function to round up a value to the roundingPrecision.
   * @param _strike the strike
   * @return strike rounded up to the nearest roundingPrecision
   **/
  function roundUp(uint256 _strike) public view returns (uint256 strike) {
    uint256 remainder = _strike % roundingPrecision;
    if (remainder == 0) {
      return _strike;
    } else {
      return _strike - remainder + roundingPrecision;
    }
  }
  1. This results in a 1,000,000 / 7,500 = 133.3 times inflation of the original value.

This is a substantial inflation, and depending on your application and context, such rounding could introduce significant inaccuracies or imbalances. You'd need to evaluate whether such a significant rounding mechanism is appropriate for your specific use case. If not, you might need to rethink the rounding logic or the precision constants you're using.

Tools Used

Manual

Here's one suggestion by having PerpetualAtlanticVault.roundUp refactored as follows:

/**
 * @dev Function to round up a value to the roundingPrecision.
 * @param _strike the strike
- * @return strike rounded up to the nearest roundingPrecision
+ * @return strike rounded up to the nearest roundingPrecision if the remainder exceeds half of the roundingPrecision
 **/
function roundUp(uint256 _strike) public view returns (uint256 strike) {
    uint256 remainder = _strike % roundingPrecision;
    if (remainder == 0) {
        return _strike;
-    } else {
-      return _strike - remainder + roundingPrecision;
+    } else if (remainder > roundingPrecision / 2) {
+        return _strike - remainder + roundingPrecision;
+    } else {
+        return _strike;
    }
}

With this approach, the rounding only happens if the _strike is more than halfway to the next rounding level. This can significantly reduce the inflation effect seen in the previous example.

Assessed type

Context

#0 - c4-pre-sort

2023-09-09T10:14:16Z

bytes032 marked the issue as duplicate of #2083

#1 - c4-pre-sort

2023-09-12T04:43:51Z

bytes032 marked the issue as sufficient quality report

#2 - c4-judge

2023-10-20T14:12:06Z

GalloDaSballo marked the issue as satisfactory

#3 - c4-judge

2023-10-21T07:54:11Z

GalloDaSballo changed the severity to 3 (High Risk)

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter