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: 93/189
Findings: 2
Award: $90.64
π Selected for report: 0
π Solo Findings: 0
π 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
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L758-L783 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L359-L361 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L198-L205
When dpxETH price deppegs from the price of Eth settle can be called by the protocol admin. It will remove passed options/positions and this will bring back the backing of the 2 tokens. If user knows that his option is going to be passed as an argument to the settle function he can send only 1 wei to the vaultLp collateral token (WETH, confirmed by the sponsors) which will make PerpetualAtlanticVaultLp.substractLoss revert, and avoid getting slashed.
Admin going to call settle passing option owned by Bob. Bob knows and beforehand transfers 1 wei to the vaultLp βs collateral which is WETH.
contracts/perp-vault/PerpetualAtlanticVault.sol function settle( uint256[] memory optionIds ) external nonReentrant onlyRole(RDPXV2CORE_ROLE) returns (uint256 ethAmount, uint256 rdpxAmount) { _whenNotPaused(); _isEligibleSender(); updateFunding(); for (uint256 i = 0; i < optionIds.length; i++) { uint256 strike = optionPositions[optionIds[i]].strike; uint256 amount = optionPositions[optionIds[i]].amount; // check if strike is ITM _validate(strike >= getUnderlyingPrice(), 7); ethAmount += (amount * strike) / 1e8; rdpxAmount += amount; optionsPerStrike[strike] -= amount; totalActiveOptions -= amount; // Burn option tokens from user _burn(optionIds[i]); optionPositions[optionIds[i]].strike = 0; } // Transfer collateral token from perpetual vault to rdpx rdpxV2Core collateralToken.safeTransferFrom( addresses.perpetualAtlanticVaultLP, addresses.rdpxV2Core, ethAmount ); // Transfer rdpx from rdpx rdpxV2Core to perpetual vault IERC20WithBurn(addresses.rdpx).safeTransferFrom( addresses.rdpxV2Core, addresses.perpetualAtlanticVaultLP, rdpxAmount ); //@audit problem will occur on this line since substractLoss uses strict equality which will be broken by directly transfering tokens to the underlying contract IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP).subtractLoss( ethAmount ); IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP) .unlockLiquidity(ethAmount); IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP).addRdpx( rdpxAmount ); emit Settle(ethAmount, rdpxAmount, optionIds); }
contracts/perp-vault/PerpetualAtlanticVaultLP.sol function subtractLoss(uint256 loss) public onlyPerpVault { //@audit require will revert because collateral.balanceOf(address(this)) will have 1 wei more require( collateral.balanceOf(address(this)) == _totalCollateral - loss, "Not enough collateral was sent out" ); _totalCollateral -= loss; }
There is a test written to demonstrate how to vulnerability will occur:
function testAuditSettleCantBeCalledIfUserSends1WeiToCollateral() public { weth.mint(address(1), 1 ether); deposit(1 ether, address(1)); vault.purchase(1 ether, address(this)); uint256[] memory ids = new uint256[](1); ids[0] = 0; priceOracle.updateRdpxPrice(0.2 gwei); // initial price * 10 uint256[] memory strikes = new uint256[](1); strikes[0] = 0.015 gwei; priceOracle.updateRdpxPrice(0.01 gwei); //@audit send 1 wei to break accounting weth.transfer(address(vaultLp), 1 wei); vm.expectRevert(); vault.settle(ids); }
Manual
Instead of using strict equality, ensure that the collateral token have enough balance to satisfy the condition.
contracts/perp-vault/PerpetualAtlanticVaultLP.sol function subtractLoss(uint256 loss) public onlyPerpVault { //@audit require will revert because collateral.balanceOf(address(this)) will have 1 wei more require( - collateral.balanceOf(address(this)) == _totalCollateral - loss, + collateral.balanceOf(address(this)) >= _totalCollateral - loss, "Not enough collateral was sent out" ); _totalCollateral -= loss; }
Token-Transfer
#0 - c4-pre-sort
2023-09-09T09:57:56Z
bytes032 marked the issue as duplicate of #619
#1 - c4-pre-sort
2023-09-11T16:15:06Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T20:01:50Z
GalloDaSballo marked the issue as satisfactory
#3 - c4-judge
2023-10-21T07:26:28Z
GalloDaSballo changed the severity to 3 (High Risk)
#4 - c4-judge
2023-10-21T07:26:28Z
GalloDaSballo changed the severity to 3 (High Risk)
#5 - c4-judge
2023-10-21T07:26:29Z
GalloDaSballo changed the severity to 3 (High Risk)
π Selected for report: QiuhaoLi
Also found by: 0xDING99YA, 0xvj, SBSecurity, T1MOH, Toshii, Udsen, bart1e, bin2chen, carrotsmuggler, pep7siup, said, sces60107, wintermute
90.6302 USDC - $90.63
https://github.com/code-423n4/2023-08-dopex/blame/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L515-L558 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L545-L549
Big slippage caused by wrong decimals calculation when using deppeg functions of the RdpxV2Core contract.
This functions is being called everytime RdpxV2Core admin
wants to bring the deppeg of the dpxETH/ETH tokens back by minting and burning tokens based on the current case.
Even though protocol will be deployed on Arbitrum this vulnerability can still be exploited.
We assume _amount is in 1e18 decimals but the slippage will be the same no matter the decimals, because the same decimals will be added to the both sides of the equation.
//@audit left: 18 + 8 - 8 = 18 right: 18 + 8 + 5 = 31 - 16 = 15 uint256 minOut = _ethToDpxEth ? (((_amount * getDpxEthPrice()) / 1e8) - (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16)) : (((_amount * getEthPrice()) / 1e8) - (((_amount * getEthPrice()) * slippageTolerance) / 1e16));
On the left side of the equation we will receive value with 18 decimals, while on the right side there will be 15 decimals. And now when we have let's say 5e18
- 2e15
, result will be 4.99e18
instead of 3e18.
Manual
Modify the code by changing the number by which the whole amount is divided:
// calculate minimum amount out uint256 minOut = _ethToDpxEth - ? (((_amount * getDpxEthPrice()) / 1e8) - (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e16)) - : (((_amount * getEthPrice()) / 1e8) - (((_amount * getEthPrice()) * slippageTolerance) / 1e16)); + ? (((_amount * getDpxEthPrice()) / 1e8) - (((_amount * getDpxEthPrice()) * slippageTolerance) / 1e13)) + : (((_amount * getEthPrice()) / 1e8) - (((_amount * getEthPrice()) * slippageTolerance) / 1e13));
Decimal
#0 - c4-pre-sort
2023-09-10T09:58:10Z
bytes032 marked the issue as duplicate of #2172
#1 - c4-pre-sort
2023-09-12T04:35:19Z
bytes032 marked the issue as not a duplicate
#2 - c4-pre-sort
2023-09-12T04:35:24Z
bytes032 marked the issue as low quality report
#3 - bytes032
2023-09-12T04:35:33Z
The issue is not related to the decimals, but related to using wrong oracle prices
#4 - c4-judge
2023-10-20T19:47:30Z
GalloDaSballo marked the issue as duplicate of #1558
#5 - c4-judge
2023-10-20T19:47:37Z
GalloDaSballo marked the issue as satisfactory