Platform: Code4rena
Start Date: 11/05/2022
Pot Size: $150,000 USDC
Total HM: 23
Participants: 93
Period: 14 days
Judge: LSDan
Total Solo HM: 18
Id: 123
League: ETH
Rank: 45/93
Findings: 2
Award: $233.39
π Selected for report: 0
π Solo Findings: 0
π Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xNazgul, 0xNineDec, 0xf15ers, 0xkatana, 242, AlleyCat, BouSalman, BowTiedWardens, CertoraInc, Chom, Cityscape, FSchmoede, Funen, GimelSec, Hawkeye, JC, JDeryl, Kaiziron, Kthere, Kumpa, MaratCerby, MiloTruck, Nethermind, NoamYakov, PPrieditis, QuantumBrief, Rolezn, Ruhum, SmartSek, SooYa, Tadashi, TerrierLover, WatchPug, Waze, _Adam, asutorufos, berndartmueller, bobirichman, c3phas, catchup, cccz, ch13fd357r0y3r, cryptphi, csanuragjain, cthulhu_cult, defsec, delfin454000, ellahi, fatherOfBlocks, hansfriese, hubble, hyh, jayjonah8, joestakey, kenta, kenzo, kirk-baird, mics, oyc_109, p_crypt0, reassor, robee, sach1r0, samruna, sashik_eth, sikorico, simon135, sorrynotsorry, sseefried, tintin, unforgiven, z3s, zmj
149.8713 USDC - $149.87
Contracts implementing access control's, e.g. owner
, should consider implementing a Two-Step Transfer pattern.
Otherwise it's possible that the role mistakenly transfers ownership to the wrong address, resulting in a loss of the role
AuraVestedEscrow.sol::setAdmin()
, Booster.sol::setOwner()
, Booster.sol::setFeeManager()
, Booster.sol::setPoolManager()
, Booster.sol::setVoteDelegate()
Consider adding a two-step approach when assigning new controllers.
Example
address owner; address pendingOwner; // ... function setPendingOwner(address newPendingOwner) external { require(msg.sender == owner, "!owner"); emit NewPendingOwner(newPendingOwner); pendingOwner = newPendingOwner; } function acceptOwnership() external { require(msg.sender == pendingOwner, "!pendingOwner"); emit NewOwner(pendingOwner); owner = pendingOwner; pendingOwner = address(0); }
The usage of deprecated library functions should be discouraged.
contracts/AuraBalRewardPool.sol::75 => rewardToken.safeApprove(_auraLocker, type(uint256).max); contracts/AuraClaimZap.sol::98 => IERC20(crv).safeApprove(crvDepositWrapper, 0); contracts/AuraClaimZap.sol::99 => IERC20(crv).safeApprove(crvDepositWrapper, type(uint256).max); contracts/AuraClaimZap.sol::101 => IERC20(cvxCrv).safeApprove(cvxCrvRewards, 0); contracts/AuraClaimZap.sol::102 => IERC20(cvxCrv).safeApprove(cvxCrvRewards, type(uint256).max); contracts/AuraClaimZap.sol::104 => IERC20(cvx).safeApprove(locker, 0); contracts/AuraClaimZap.sol::105 => IERC20(cvx).safeApprove(locker, type(uint256).max); contracts/AuraLocker.sol::240 => IERC20(cvxCrv).safeApprove(cvxcrvStaking, 0); contracts/AuraLocker.sol::241 => IERC20(cvxCrv).safeApprove(cvxcrvStaking, type(uint256).max); contracts/AuraMerkleDrop.sol::131 => aura.safeApprove(address(auraLocker), 0); contracts/AuraMerkleDrop.sol::132 => aura.safeApprove(address(auraLocker), _amount); contracts/AuraPenaltyForwarder.sol::41 => token.safeApprove(address(distributor), type(uint256).max); contracts/AuraStakingProxy.sol::147 => IERC20(crv).safeApprove(crvDepositorWrapper, 0); contracts/AuraStakingProxy.sol::148 => IERC20(crv).safeApprove(crvDepositorWrapper, type(uint256).max); contracts/AuraStakingProxy.sol::150 => IERC20(cvxCrv).safeApprove(rewards, 0); contracts/AuraStakingProxy.sol::151 => IERC20(cvxCrv).safeApprove(rewards, type(uint256).max); contracts/AuraStakingProxy.sol::215 => _token.safeApprove(rewards, 0); contracts/AuraStakingProxy.sol::216 => _token.safeApprove(rewards, type(uint256).max); contracts/AuraVestedEscrow.sol::186 => rewardToken.safeApprove(address(auraLocker), claimable); contracts/BalLiquidityProvider.sol::59 => tkn.safeApprove(address(bVault), 0); contracts/BalLiquidityProvider.sol::60 => tkn.safeApprove(address(bVault), bal); contracts/CrvDepositorWrapper.sol::52 => IERC20(WETH).safeApprove(address(BALANCER_VAULT), type(uint256).max); contracts/CrvDepositorWrapper.sol::53 => IERC20(BAL).safeApprove(address(BALANCER_VAULT), type(uint256).max); convesx-platform/BaseRewardPool4626.sol::40 => IERC20(asset).safeApprove(operator_, type(uint256).max); convesx-platform/Booster.sol::422 => IERC20(token).safeApprove(rewardContract,0); convesx-platform/Booster.sol::423 => IERC20(token).safeApprove(rewardContract,_amount); convesx-platform/CrvDepositor.sol::199 => IERC20(minter).safeApprove(_stakeAddress,0); convesx-platform/CrvDepositor.sol::200 => IERC20(minter).safeApprove(_stakeAddress,_amount); convesx-platform/VoterProxy.sol::176 => IERC20(_token).safeApprove(_gauge, 0); convesx-platform/VoterProxy.sol::177 => IERC20(_token).safeApprove(_gauge, balance); convesx-platform/VoterProxy.sol::193 => _asset.safeApprove(rewardDeposit, 0); convesx-platform/VoterProxy.sol::194 => _asset.safeApprove(rewardDeposit, balance); convesx-platform/VoterProxy.sol::244 => IERC20(crvBpt).safeApprove(escrow, 0); convesx-platform/VoterProxy.sol::245 => IERC20(crvBpt).safeApprove(escrow, _value); convesx-platform/VoterProxy.sol::255 => IERC20(crvBpt).safeApprove(escrow, 0); convesx-platform/VoterProxy.sol::256 => IERC20(crvBpt).safeApprove(escrow, _value);
Use safeIncreaseAllowance
/ safeDecreaseAllowance
instead of safeApprove
.
manual, slither
π Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x4non, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, BowTiedWardens, CertoraInc, DavidGialdi, FSchmoede, Fitraldys, Funen, GimelSec, Hawkeye, JC, Kaiziron, Kthere, MaratCerby, MiloTruck, NoamYakov, QuantumBrief, Randyyy, Ruhum, SmartSek, SooYa, Tadashi, TerrierLover, Tomio, UnusualTurtle, WatchPug, Waze, _Adam, antonttc, asutorufos, bobirichman, c3phas, catchup, csanuragjain, cthulhu_cult, defsec, delfin454000, ellahi, fatherOfBlocks, hansfriese, hyh, jayjonah8, joestakey, kenta, marcopaladin, mics, minhquanym, orion, oyc_109, reassor, rfa, robee, sach1r0, samruna, sashik_eth, sikorico, simon135, unforgiven, z3s, zmj
83.5222 USDC - $83.52
!= 0
instead of > 0
for Unsigned Integer Comparison in require statements.!= 0
is cheapear than > 0
when comparing unsigned integers in require statements.
contracts/Aura.sol::68 => require(_amount > 0, "Must mint something"); contracts/AuraBalRewardPool.sol::121 => require(_amount > 0, "RewardPool : Cannot stake 0"); contracts/AuraBalRewardPool.sol::139 => require(_amount > 0, "RewardPool : Cannot stake 0"); contracts/AuraBalRewardPool.sol::157 => require(amount > 0, "RewardPool : Cannot withdraw 0"); contracts/AuraBalRewardPool.sol::210 => require(rewardsAvailable > 0, "!balance"); contracts/AuraLocker.sol::210 => require(rewardData[_rewardsToken].lastUpdateTime > 0, "Reward does not exist"); contracts/AuraLocker.sol::259 => require(_amount > 0, "Cannot stake 0"); contracts/AuraLocker.sol::359 => require(amt > 0, "Nothing locked"); contracts/AuraLocker.sol::385 => require(length > 0, "no locks"); contracts/AuraLocker.sol::431 => require(locked > 0, "no exp locks"); contracts/AuraLocker.sol::471 => require(len > 0, "Nothing to delegate"); contracts/AuraLocker.sol::822 => require(_rewards > 0, "No reward"); contracts/AuraLocker.sol::851 => require(_reward > 0, "No reward"); contracts/AuraMerkleDrop.sol::122 => require(_amount > 0, "!amount"); contracts/AuraPenaltyForwarder.sol::52 => require(bal > 0, "!empty"); contracts/AuraVestedEscrow.sol::118 => require(totalLocked[_recipient] > 0, "!funding"); contracts/BalLiquidityProvider.sol::57 => require(bal > 0 && bal == _request.maxAmountsIn[i], "!bal"); contracts/BalLiquidityProvider.sol::70 => require(balAfter > 0, "!mint"); contracts/ExtraRewardsDistributor.sol::171 => require(_index > 0 && _index < rewardEpochs[_token].length - 1, "!past"); convesx-platform/BaseRewardPool.sol::211 => require(_amount > 0, 'RewardPool : Cannot stake 0'); convesx-platform/BaseRewardPool.sol::227 => require(amount > 0, 'RewardPool : Cannot withdraw 0'); convesx-platform/Booster.sol::223 => require(IFeeDistributor(_feeDistro).getTokenTimeCursor(_feeToken) > 0, "!distro"); convesx-platform/CrvDepositor.sol::169 => require(_amount > 0,"!>0"); convesx-platform/PoolManagerSecondaryProxy.sol::104 => require(weight > 0, "must have weight"); convesx-platform/interfaces/BoringMath.sol::20 => require(b > 0, "BoringMath: division by zero"); convesx-platform/interfaces/BoringMath.sol::102 => require(b > 0, "BoringMath: division by zero"); convesx-platform/interfaces/BoringMath.sol::123 => require(b > 0, "BoringMath: division by zero"); convesx-platform/interfaces/BoringMath.sol::143 => require(b > 0, "BoringMath: division by zero");
Use != 0
instead of > 0
.
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack. Caching the array length in the stack saves around 3 gas per iteration.
contracts/AuraClaimZap.sol::143 => for (uint256 i = 0; i < rewardContracts.length; i++) { contracts/AuraClaimZap.sol::147 => for (uint256 i = 0; i < extraRewardContracts.length; i++) { contracts/AuraClaimZap.sol::151 => for (uint256 i = 0; i < tokenRewardContracts.length; i++) { contracts/AuraLocker.sol::410 => for (uint256 i = nextUnlockIndex; i < length; i++) { contracts/AuraLocker.sol::696 => for (uint256 i = nextUnlockIndex; i < locks.length; i++) { contracts/AuraVestedEscrow.sol::100 => for (uint256 i = 0; i < _recipient.length; i++) { convesx-platform/ArbitartorVault.sol::49 => for(uint256 i = 0; i < _toPids.length; i++){ convesx-platform/BaseRewardPool.sol::214 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::230 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::262 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::296 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/Booster.sol::379 => for(uint i=0; i < poolInfo.length; i++){ convesx-platform/Booster.sol::538 => for(uint256 i = 0; i < _gauge.length; i++){ convesx-platform/ConvexMasterChef.sol::180 => for (uint256 pid = 0; pid < length; ++pid) { convesx-platform/PoolManagerSecondaryProxy.sol::69 => for(uint i=0; i < usedList.length; i++){
Store the arrayβs length in a variable before the for-loop.
++i
costs less gas compared to i++
or i += 1
++i
costs less gas compared to i++
or i += 1
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
contracts/AuraClaimZap.sol::143 => for (uint256 i = 0; i < rewardContracts.length; i++) { contracts/AuraClaimZap.sol::147 => for (uint256 i = 0; i < extraRewardContracts.length; i++) { contracts/AuraClaimZap.sol::151 => for (uint256 i = 0; i < tokenRewardContracts.length; i++) { contracts/AuraLocker.sol::410 => for (uint256 i = nextUnlockIndex; i < length; i++) { contracts/AuraLocker.sol::696 => for (uint256 i = nextUnlockIndex; i < locks.length; i++) { contracts/AuraVestedEscrow.sol::100 => for (uint256 i = 0; i < _recipient.length; i++) { convesx-platform/ArbitartorVault.sol::49 => for(uint256 i = 0; i < _toPids.length; i++){ convesx-platform/BaseRewardPool.sol::214 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::230 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::262 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::296 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/Booster.sol::379 => for(uint i=0; i < poolInfo.length; i++){ convesx-platform/Booster.sol::538 => for(uint256 i = 0; i < _gauge.length; i++){ convesx-platform/PoolManagerSecondaryProxy.sol::69 => for(uint i=0; i < usedList.length; i++){
Use ++i
instead of i++
to increment the value of an uint variable.
Same thing for --i
and i--
.
If a variable is not set/initialized, it is assumed to have the default value (0, false, 0x0 etc depending on the data type). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
contracts/AuraClaimZap.sol::143 => for (uint256 i = 0; i < rewardContracts.length; i++) { contracts/AuraClaimZap.sol::147 => for (uint256 i = 0; i < extraRewardContracts.length; i++) { contracts/AuraClaimZap.sol::151 => for (uint256 i = 0; i < tokenRewardContracts.length; i++) { contracts/AuraLocker.sol::174 => for (uint256 i = 0; i < rewardTokensLength; i++) { contracts/AuraLocker.sol::381 => uint256 reward = 0; contracts/AuraLocker.sol::485 => uint256 futureUnlocksSum = 0; contracts/AuraLocker.sol::540 => uint256 unlocksSinceLatestCkpt = 0; contracts/AuraLocker.sol::630 => uint256 low = 0; contracts/AuraLocker.sol::773 => for (uint256 i = 0; i < userRewardsLength; i++) { contracts/AuraVestedEscrow.sol::33 => bool public initialised = false; contracts/AuraVestedEscrow.sol::99 => uint256 totalAmount = 0; contracts/AuraVestedEscrow.sol::100 => for (uint256 i = 0; i < _recipient.length; i++) { contracts/BalLiquidityProvider.sol::51 => for (uint256 i = 0; i < 2; i++) { contracts/ExtraRewardsDistributor.sol::231 => uint256 claimableTokens = 0; convesx-platform/ArbitartorVault.sol::49 => for(uint256 i = 0; i < _toPids.length; i++){ convesx-platform/BaseRewardPool.sol::214 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::230 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::262 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/BaseRewardPool.sol::296 => for(uint i=0; i < extraRewards.length; i++){ convesx-platform/Booster.sol::379 => for(uint i=0; i < poolInfo.length; i++){ convesx-platform/Booster.sol::538 => for(uint256 i = 0; i < _gauge.length; i++){ convesx-platform/BoosterOwner.sol::144 => for(uint256 i = 0; i < poolCount; i++){ convesx-platform/ConvexMasterChef.sol::180 => for (uint256 pid = 0; pid < length; ++pid) { convesx-platform/ExtraRewardStashV3.sol::125 => for(uint256 i = 0; i < maxRewards; i++){ convesx-platform/ExtraRewardStashV3.sol::199 => for(uint i=0; i < tCount; i++){ convesx-platform/PoolManagerSecondaryProxy.sol::69 => for(uint i=0; i < usedList.length; i++){
Remove explicit default initializations.
Strict inequalities add a check of non equality which costs around 3 gas.
Throughout codebase.
Use >=
or <=
instead of >
and <
when possible.
c4udit, manual, slither