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
Rank: 58/189
Findings: 3
Award: $192.67
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Toshii
Also found by: 0x3b, 0xDING99YA, 0xmystery, Cosine, Jiamin, Juntao, Matin, Qeew, Topmark, catwhiskeys, circlelooper, crunch, deadrxsezzz, eeshenggoh, lsaudit, peakbolt, pep7siup, piyushshukla, qpzm, visualbits
96.3292 USDC - $96.33
Option strike price is incorrectly rounded up and option will be created with incorrect strike price.
When creating an put option, the option strike price is supposed to be 25% below the current price.
uint256 strike = roundUp(currentPrice - (currentPrice / 4)); // 25% below the current price
If currentPrice - (currentPrice / 4)
is less than roundingPrecision, then it will be rounded up:
function roundUp(uint256 _strike) public view returns (uint256 strike) { uint256 remainder = _strike % roundingPrecision; if (remainder == 0) { return _strike; } else { return _strike - remainder + roundingPrecision; } }
If currentPrice
(denominated in ETH) is 0.01e8, then the strike price is expected to be 0.0075e8, however, as the price is in 8 decimals and the roundingPrecision
is 1e6, so the minimum price will be (1e6 / 1e8) * ETH price
, which is 0.01e8, as an result the option is created with strike price 0.01e8, not 25% below the current price.
Manual Review
Please consider to set roundingPrecision
to an much smaller value so that it's less likely option strike price will be rounded up to be higher than 25% below the current price.
Math
#0 - c4-pre-sort
2023-09-09T05:29:36Z
bytes032 marked the issue as duplicate of #2083
#1 - c4-pre-sort
2023-09-12T04:43:44Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T14:17:48Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: klau5
Also found by: 0x3b, 0xCiphky, 0xDING99YA, 0xWaitress, 0xbranded, 0xc0ffEE, 0xklh, 0xsurena, 0xvj, ABA, AkshaySrivastav, Anirruth, Aymen0909, Baki, Blockian, BugzyVonBuggernaut, DanielArmstrong, Evo, GangsOfBrahmin, HChang26, Inspex, Jiamin, Juntao, Kow, Krace, KrisApostolov, LFGSecurity, LokiThe5th, Mike_Bello90, Norah, Nyx, QiuhaoLi, RED-LOTUS-REACH, SBSecurity, Snow24, SpicyMeatball, T1MOH, Tendency, Toshii, Udsen, Yanchuan, __141345__, ak1, asui, auditsea, ayden, bart1e, bin2chen, blutorque, carrotsmuggler, chaduke, chainsnake, circlelooper, clash, codegpt, crunch, degensec, dirk_y, ge6a, gjaldon, grearlake, jasonxiale, juancito, ke1caM, kodyvim, kutugu, ladboy233, lanrebayode77, mahdikarimi, max10afternoon, mert_eren, nirlin, nobody2018, oakcobalt, parsely, peakbolt, pks_, pontifex, ravikiranweb3, rokinot, rvierdiiev, said, savi0ur, sces60107, sh1v, sl1, spidy730, tapir, tnquanghuy0512, ubermensch, visualbits, volodya, wintermute
0.0098 USDC - $0.01
Attacker can prevent options from being settled by transferring collateral tokens.
subtractLoss(uint256 loss) function is called when an option is settled, in this function, protocol will check if the collateral tokens balance of PerpetualAtlanticVaultLP is the same as _totalCollateral - loss
.
require( collateral.balanceOf(address(this)) == _totalCollateral - loss, "Not enough collateral was sent out" );
Because _totalCollateral is not updated when options are settled, so an attacker can DOS attack by transferring small amount of collateral tokens to PerpetualAtlanticVaultLP, then the require statement will revert due to collateral.balanceOf(address(this)) != _totalCollateral - loss
, thus preventing the option from being settled.
Manual Review
Update _totalCollateral when an options is settled.
DoS
#0 - c4-pre-sort
2023-09-09T09:57:51Z
bytes032 marked the issue as duplicate of #619
#1 - c4-pre-sort
2023-09-11T16:15:05Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T19:34:30Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: LokiThe5th
Also found by: 0xPsuedoPandit, 0xTiwa, 0xnev, 0xvj, Evo, Jiamin, Juntao, QiuhaoLi, T1MOH, Udsen, circlelooper, crunch, eeshenggoh, gjaldon, hals, josephdara, kutugu, minhtrng, niki, umarkhatab_465
96.3292 USDC - $96.33
Protocol wrongly assumes oracle returns price data in 8 decimals, results in price data being wrongly used.
getRdpxPrice() returns the price of rDPX against ETH, and the price data is directly fetched from oracle.
function getRdpxPrice() public view returns (uint256) { return IRdpxEthOracle(pricingOracleAddresses.rdpxPriceOracle) .getRdpxPriceInEth(); }
Protocol assumes the price data is in 8 decimals, and uses the prices in multiple functions. For example, When calculates the delegate amounts, the price data is divided by 1e8:
uint256 rdpxRequiredInWeth = (_rdpxRequired * getRdpxPrice()) / 1e8;
However, when we check the implementation of IRdpxEthOracle, we can find oracle returns the price data in 18 decimals:
/// @notice Returns the price of rDPX in ETH /// @return price price of rDPX in ETH in 1e18 decimals function getRdpxPriceInEth() external view override returns (uint price) { require( blockTimestampLast + timePeriod + nonUpdateTolerance > block.timestamp, "RdpxEthOracle: UPDATE_TOLERANCE_EXCEEDED" ); price = consult(token0, 1e18); require(price > 0, "RdpxEthOracle: PRICE_ZERO"); }
Manual Review
Please consider to convert oracle price data to 8 decimals before use.
Decimal
#0 - c4-pre-sort
2023-09-09T05:16:20Z
bytes032 marked the issue as duplicate of #549
#1 - c4-pre-sort
2023-09-12T05:19:46Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T18:27:55Z
GalloDaSballo marked the issue as satisfactory
#3 - c4-judge
2023-10-20T18:28:12Z
GalloDaSballo changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-10-20T18:28:21Z
GalloDaSballo changed the severity to 3 (High Risk)