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: 75/189
Findings: 5
Award: $127.19
π 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/PerpetualAtlanticVault.sol#L315 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L201
Any settle call can be reverted, thus disallowing the protocol from settling any options and from releasing the locked tokens.
The protocol admin calls settle()
on an option when its price gets bellow the strike price, at which the option is bought at. The issue arises due to a flawed check in PerpetualAtlanticVaultLP.subtractLoss()
.
require( collateral.balanceOf(address(this)) == _totalCollateral - loss, "Not enough collateral was sent out" );
This check makes front-running the transaction by donating 1 WEI for making it revert possible.
Here is a Foundry PoC demonstrating the issue:
https://gist.github.com/CrisCodesCrap/d1e7af6ffec3abfe500949340c418577
Manual review, Foundry
Consider altering the check in the following way:
require( collateral.balanceOf(address(this)) >= _totalCollateral - loss, "Some revert message here" );
DoS
#0 - c4-pre-sort
2023-09-09T09:55:24Z
bytes032 marked the issue as duplicate of #619
#1 - c4-pre-sort
2023-09-11T16:14:26Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T19:28:19Z
GalloDaSballo marked the issue as satisfactory
π Selected for report: said
Also found by: 0Kage, 0xCiphky, 0xkazim, 836541, AkshaySrivastav, Evo, HChang26, HHK, KrisApostolov, Neon2835, QiuhaoLi, Tendency, Toshii, bart1e, bin2chen, carrotsmuggler, chaduke, etherhood, gjaldon, glcanvas, josephdara, lanrebayode77, mahdikarimi, max10afternoon, nobody2018, peakbolt, qpzm, rvierdiiev, sces60107, tapir, ubermensch, volodya
17.313 USDC - $17.31
Users can get more shares than they should.
Users can get more shares than they paid for because vaultLp.deposit()
's share calculation is done before the contract updates the funding token balances. This makes it so that users can deposit into the vault as if there isn't any new funding, but then update the funding in the line bellow.
// @audit shares get calculated here: require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // @audit Funding gets updated here, effectively allowing users to mint shares at old rates, but instantly get more value than they deposited. perpetualAtlanticVault.updateFunding();
Consider the following PoC demonstrating the issue at hand:
https://gist.github.com/CrisCodesCrap/ab356eb14667dfe83b5ba1155b2ded4c
Manual review + Foundry
Consider calling vault.updateFunding()
before doing the share calculation to fully mitigate the issue.
Here is how it may look like when implemented:
// @audit First: the funding gets updated perpetualAtlanticVault.updateFunding(); // @audit shares get calculated afterwards to ensure the bug is mitigated require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");
Other
#0 - c4-pre-sort
2023-09-08T14:09:52Z
bytes032 marked the issue as duplicate of #867
#1 - c4-pre-sort
2023-09-11T09:08:00Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T19:22:52Z
GalloDaSballo marked the issue as satisfactory
#3 - GalloDaSballo
2023-10-20T19:22:58Z
Best to add POC to finding
π Selected for report: 0xrafaelnicolau
Also found by: 0x111, 0xCiphky, 0xMosh, 0xWaitress, 0xc0ffEE, 0xkazim, 0xnev, 0xvj, ABAIKUNANBAEV, Aymen0909, Baki, ElCid, HChang26, HHK, Inspex, Jorgect, Kow, Krace, KrisApostolov, LFGSecurity, MiniGlome, Nyx, QiuhaoLi, RED-LOTUS-REACH, Talfao, Toshii, Vagner, Viktor_Cortess, Yanchuan, _eperezok, asui, atrixs6, bart1e, bin2chen, carrotsmuggler, chaduke, chainsnake, deadrxsezzz, degensec, dethera, dimulski, dirk_y, ether_sky, gizzy, glcanvas, grearlake, gumgumzum, halden, hals, kodyvim, koo, ladboy233, lanrebayode77, max10afternoon, minhtrng, mussucal, nobody2018, peakbolt, pontifex, qbs, ravikiranweb3, rvierdiiev, said, tapir, ubermensch, volodya, wintermute, yashar, zaevlad, zzebra83
0.0734 USDC - $0.07
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L941-L968 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L975-L990 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L995-L1008
The contract's WETH amount gets permanently bricked.
A user can call addToDelegate()
and give WETH, that other people can use for bonding with their rDPX in exchange for a certain percentage appointed by the delegatee.
Delegate memory delegatePosition = Delegate({ amount: _amount, fee: _fee, owner: msg.sender, activeCollateral: 0 }); delegates.push(delegatePosition); // add amount to total weth delegated totalWethDelegated += _amount; emit LogAddToDelegate(_amount, _fee, delegates.length - 1); return (delegates.length - 1);
There the user gets added to the delegates
array and also the delegated WETH amount gets added to a variable called totalWethDelegated
, which is used for keeping track of the part of WETH in the contract, which is owned by delegates. That variable is also used in sync()
for setting the virtual balance of WETH in the contract.
function sync() external { for (uint256 i = 1; i < reserveAsset.length; i++) { uint256 balance = IERC20WithBurn(reserveAsset[i].tokenAddress).balanceOf( address(this) ); if (weth == reserveAsset[i].tokenAddress) { balance = balance - totalWethDelegated; } reserveAsset[i].tokenBalance = balance; } emit LogSync(); }
The issue arises due to the WETH amount not being removed from totalWethDelegated
upon withdrawal.
function withdraw( uint256 delegateId ) external returns (uint256 amountWithdrawn) { _whenNotPaused(); _validate(delegateId < delegates.length, 14); Delegate storage delegate = delegates[delegateId]; _validate(delegate.owner == msg.sender, 9); amountWithdrawn = delegate.amount - delegate.activeCollateral; _validate(amountWithdrawn > 0, 15); delegate.amount = delegate.activeCollateral; // @audit-issue The delegate entry's amount gets removed, but the amount doesn't get subtracted from totalWethDelegated IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn); emit LogDelegateWithdraw(delegateId, amountWithdrawn); }
Consider the following PoC demonstrating the issue:
https://gist.github.com/CrisCodesCrap/fb5ad3b5a5c95670d2ae44c895b42ab5
Manual review, Foundry
Consider removing the amount upon withdrawing the delegated WETH from the protocol.
function withdraw( uint256 delegateId ) external returns (uint256 amountWithdrawn) { _whenNotPaused(); _validate(delegateId < delegates.length, 14); Delegate storage delegate = delegates[delegateId]; _validate(delegate.owner == msg.sender, 9); amountWithdrawn = delegate.amount - delegate.activeCollateral; _validate(amountWithdrawn > 0, 15); delegate.amount = delegate.activeCollateral; totalWethDelegated -= amountWithdrawn; IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn); emit LogDelegateWithdraw(delegateId, amountWithdrawn); }
Other
#0 - c4-pre-sort
2023-09-07T07:20:00Z
bytes032 marked the issue as high quality report
#1 - c4-pre-sort
2023-09-07T07:20:04Z
bytes032 marked the issue as primary issue
#2 - bytes032
2023-09-07T07:55:42Z
Valid issue. One thing that the report is missing is the potential for DoS through the sync() function.
#3 - bytes032
2023-09-08T13:45:52Z
It has various different nuances:
Breaks the accounting (the amount of WETH deposited for delegated bonding)
Unintentionally DoS multiple functions, for example sync() here:
Can be used by βmaliciousβ actors for intentional dos, e.g. with FlashLoan [1]
Note that it will also break provideFunding() and lowerDepeg()
Note that even if WETH is donated to resolve the block, the accounting of WETH reserves will still be completely wrong
#4 - c4-sponsor
2023-09-25T16:42:16Z
psytama (sponsor) confirmed
#5 - GalloDaSballo
2023-10-10T16:47:04Z
Best to add the POC in the submission, I think I'll change the primary due to that
#6 - c4-judge
2023-10-17T17:24:25Z
GalloDaSballo marked issue #2146 as primary and marked this issue as a duplicate of 2146
#7 - c4-judge
2023-10-20T17:46:51Z
GalloDaSballo marked the issue as satisfactory
#8 - c4-judge
2023-10-20T17:55:32Z
GalloDaSballo changed the severity to 2 (Med Risk)
#9 - c4-judge
2023-10-21T07:38:31Z
GalloDaSballo marked the issue as partial-50
#10 - c4-judge
2023-10-21T07:38:54Z
GalloDaSballo changed the severity to 3 (High Risk)
π Selected for report: juancito
Also found by: 0x3b, 0xmuxyz, 0xnev, ArmedGoose, Bauchibred, KrisApostolov, RED-LOTUS-REACH, Viktor_Cortess, ciphermarco, ginlee, ladboy233, mitko1111, nemveer
90.6302 USDC - $90.63
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/reLP/ReLPContract.sol#L286-L295
The amount being LPd into Uniswap can get stolen trough MEV.
The reLP
contract re-LPs a certain amount of the tokens, that enter after a bond gets bought. The issue arises due to there not being proper minimum liquidity amounts passed when calling addLiquidity()
on a Uniswap V2 pool.
(, , uint256 lp) = IUniswapV2Router(addresses.ammRouter).addLiquidity( addresses.tokenA, addresses.tokenB, tokenAAmountOut, amountB / 2, // @audit-issue lack of slippage checks 0, 0, address(this), block.timestamp + 10 );
Manual review
Consider passing appropriate minimum liquidity amounts to addLiquidity()
.
Uniswap
#0 - c4-pre-sort
2023-09-07T12:38:40Z
bytes032 marked the issue as duplicate of #1259
#1 - c4-pre-sort
2023-09-11T07:50:21Z
bytes032 marked the issue as sufficient quality report
#2 - c4-pre-sort
2023-09-11T07:53:27Z
bytes032 marked the issue as duplicate of #1032
#3 - c4-judge
2023-10-15T19:21:22Z
GalloDaSballo marked the issue as satisfactory
π Selected for report: juancito
Also found by: 0xDING99YA, 0xTiwa, 0xkazim, 0xnev, ABA, ArmedGoose, Aymen0909, Bauchibred, Evo, IceBear, KrisApostolov, MohammedRizwan, Nikki, QiuhaoLi, T1MOH, Toshii, WoolCentaur, Yanchuan, __141345__, asui, bart1e, carrotsmuggler, catellatech, chaduke, codegpt, deadrxsezzz, degensec, dethera, dirk_y, erebus, ether_sky, gjaldon, glcanvas, jasonxiale, josephdara, klau5, kodyvim, ladboy233, lsaudit, minhquanym, parsely, peakbolt, pep7siup, rvierdiiev, said, savi0ur, sces60107, tapir, ubermensch, volodya, zzebra83
19.1724 USDC - $19.17
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/amo/UniV3LiquidityAmo.sol#L289-L299
If the transaction gets stalled in the mempool it can can get executed at an inappropriate moment.
The Uniswap V3 AMO integrates Uniswap to provide liquidity and to execute swaps. The issue arises due to the protocol using an arbitrary timetamp in the future instead of an actual one that will protect the protocol from getting damaged due to precision loss.
ISwapRouter.ExactInputSingleParams memory swap_params = ISwapRouter .ExactInputSingleParams( _tokenA, _tokenB, _fee_tier, address(this), // @audit-issue inappropriate timestamp: 2105300114, // Expiration: a long time from now _amountAtoB, _amountOutMinimum, _sqrtPriceLimitX96 );
Manual review
Consider swapping the timestamp with a value passed as params.
Uniswap
#0 - c4-pre-sort
2023-09-07T08:33:32Z
bytes032 marked the issue as low quality report
#1 - c4-pre-sort
2023-09-07T08:33:37Z
bytes032 marked the issue as primary issue
#2 - c4-pre-sort
2023-09-08T05:52:05Z
bytes032 marked the issue as duplicate of #898
#3 - c4-pre-sort
2023-09-11T08:57:56Z
bytes032 marked the issue as sufficient quality report
#4 - c4-judge
2023-10-17T17:39:18Z
GalloDaSballo changed the severity to QA (Quality Assurance)
#5 - c4-judge
2023-10-20T18:18:20Z
GalloDaSballo marked the issue as grade-b