Platform: Code4rena
Start Date: 26/05/2022
Pot Size: $75,000 USDT
Total HM: 31
Participants: 71
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 18
Id: 126
League: ETH
Rank: 47/71
Findings: 2
Award: $152.48
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xDjango, 0xNazgul, 0xf15ers, BouSalman, Chom, Deivitto, Dravee, ElKu, FSchmoede, Funen, GimelSec, Hawkeye, MiloTruck, Picodes, SecureZeroX, SmartSek, TerrierLover, WatchPug, _Adam, asutorufos, berndartmueller, c3phas, catchup, cccz, cogitoergosumsw, cryptphi, csanuragjain, delfin454000, dipp, ellahi, gzeon, hansfriese, horsefacts, hyh, kirk-baird, minhquanym, oyc_109, pauliax, reassor, robee, sashik_eth, shenwilly, simon135, sorrynotsorry, sseefried, unforgiven, xiaoming90, z3s
100.0277 USDT - $100.03
safeApprove
of openZeppelin's SafeERC20safeApprove()
is deprecated as described in commentssafeIncreaseAllowance()
and safeDecreaseAllowance()
is recommended.
It appears in following codebase:./contracts/VoterProxy.sol:101: IERC20(_token).safeApprove(_gauge, 0); ./contracts/VoterProxy.sol:102: IERC20(_token).safeApprove(_gauge, balance); ./contracts/VoterProxy.sol:153: IERC20(veAsset).safeApprove(escrow, 0); ./contracts/VoterProxy.sol:154: IERC20(veAsset).safeApprove(escrow, _value); ./contracts/VoterProxy.sol:161: IERC20(veAsset).safeApprove(escrow, 0); ./contracts/VoterProxy.sol:162: IERC20(veAsset).safeApprove(escrow, _value); ./contracts/Booster.sol:375: IERC20(token).safeApprove(rewardContract, _amount); ./contracts/VE3DRewardPool.sol:289: IERC20(_rewardToken).safeApprove(rewardTokenInfo[_rewardToken].veAssetDeposits, 0); ./contracts/VE3DRewardPool.sol:290: IERC20(_rewardToken).safeApprove( ./contracts/VE3DRewardPool.sol:303: IERC20(rewardTokenInfo[_rewardToken].ve3Token).safeApprove( ./contracts/VE3DRewardPool.sol:307: IERC20(rewardTokenInfo[_rewardToken].ve3Token).safeApprove( ./contracts/VeAssetDepositor.sol:178: IERC20(minter).safeApprove(_stakeAddress, _amount); ./contracts/VE3DLocker.sol:211: IERC20(rewardData[_rewardsToken].ve3Token).safeApprove( ./contracts/VE3DLocker.sol:218: IERC20(rewardData[_rewardsToken].ve3Token).safeApprove( ./contracts/VE3DLocker.sol:224: IERC20(_rewardsToken).safeApprove(rewardData[_rewardsToken].veAssetDeposits, 0); ./contracts/VE3DLocker.sol:225: IERC20(_rewardsToken).safeApprove(
setPoolManager()
of Booster.sol
, accidentally setting to zero address can create unintended consequences.Consider adding zero-address checks i.e. require(newAddress != address(0))
To protect from setting to dead/inactive addresses and other unintended consequences, use of two-step procedure is recommended for critical functions.
For example: for setting owner
, following pattern can be used
address owner; address temporaryOwner; function setOwner(address owner_) external { require(msg.sender == owner); temporaryOwner = owner_; } function claimOwnership() external { require(msg.sender == temporaryOwner; owner = temporaryOwner; temporaryOwner = address(0); }
Examples: setOwner()
, setFeeManager()
, setPoolManager()
, setArbitrator()
in Booster.sol
#0 - GalloDaSballo
2022-07-06T23:25:01Z
NC for this codebase
Valid Low
Valid NC
Nice short report
1L, 2NC
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, Cityscape, Dravee, ElKu, FSchmoede, Funen, GalloDaSballo, Hawkeye, Kaiziron, MiloTruck, Randyyy, RoiEvenHaim, Ruhum, SecureZeroX, SmartSek, TerrierLover, TomJ, Tomio, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, cogitoergosumsw, delfin454000, ellahi, fatherOfBlocks, gzeon, hansfriese, horsefacts, jonatascm, minhquanym, oyc_109, pauliax, reassor, robee, sach1r0, saian, sashik_eth, simon135, z3s
52.4549 USDT - $52.45
!=0
is cheaper than >0
!=0
is cheaper than using >0
for uints> grep -rn "./contracts" -e "require(.* > 0" ./contracts/VE3DRewardPool.sol:210: require(_amount > 0, "RewardPool : Cannot stake 0"); ./contracts/VE3DRewardPool.sol:234: require(_amount > 0, "RewardPool : Cannot stake 0"); ./contracts/VE3DRewardPool.sol:253: require(_amount > 0, "RewardPool : Cannot withdraw 0"); ./contracts/VeAssetDepositor.sol:132: require(_amount > 0, "!>0"); ./contracts/VirtualBalanceRewardPool.sol:137: // require(amount > 0, 'VirtualDepositRewardPool: Cannot stake 0'); ./contracts/VirtualBalanceRewardPool.sol:143: //require(amount > 0, 'VirtualDepositRewardPool : Cannot withdraw 0'); ./contracts/VE3DLocker.sol:180: require(rewardData[_rewardsToken].lastUpdateTime > 0); ./contracts/VE3DLocker.sol:520: require(_amount > 0, "Cannot stake 0"); ./contracts/VE3DLocker.sol:670: require(locked > 0, "no exp locks"); ./contracts/VE3DLocker.sol:781: require(_reward > 0, "No reward"); ./contracts/BaseRewardPool.sol:173: require(_amount > 0, "RewardPool : Cannot stake 0"); ./contracts/BaseRewardPool.sol:196: require(_amount > 0, "RewardPool : Cannot stake 0"); ./contracts/BaseRewardPool.sol:215: require(amount > 0, "RewardPool : Cannot withdraw 0");
uint variables are set to zero by default, initializing them to zero (for storage variables) causes extra gas.
solidity 0.8.4 introduces custom errors which are cheaper than using revert strings in terms of gas Use the custom error patterns to reduce gas cost.
for eg.
// Before require(condition, "Revert strings"); // After error CustomError(); if (!condition) { revert CustomError(); }
more details can be found here
Prefix increment operation(++i) is more gas effiecient than postfix increment(i++), while few instance of postfix increment doesn't makes much difference in overall gas, in loops these increments are called a large number of times resulting in significant gas savings.
// Before for (uint i = 0; i < _prices.length; i++) { } // After for (uint i = 0; i < _prices.length; ++i) { }
Also, since the loop increment starts from zero and only increases, there is no chance of overflow. So the increment can also be added inside a unchecked block so save further gas
for (uint i = 0; i < _prices.length;) { unchecked { ++i; } }
The instances of prefix increment can be found as follows
> grep -rn './contracts' -e 'for.*i++' ./contracts/VoterProxy.sol:217: for (uint256 i = 0; i < _tokenVote.length; i++) { ./contracts/Booster.sol:329: for (uint256 i = 0; i < poolInfo.length; i++) { ./contracts/ExtraRewardStashV3.sol:84: for (uint256 i = 0; i < maxRewards; i++) { ./contracts/ExtraRewardStashV3.sol:126: for (uint256 i = 0; i < tokenCount; i++) { ./contracts/VE3DRewardPool.sol:148: for (uint256 i = 0; i < rewardTokens.length(); i++) { ./contracts/VE3DRewardPool.sol:214: for (uint256 i = 0; i < length; i++) { ./contracts/VE3DRewardPool.sol:238: for (uint256 i = 0; i < length; i++) { ./contracts/VE3DRewardPool.sol:257: for (uint256 i = 0; i < length; i++) { ./contracts/VE3DRewardPool.sol:281: for (uint256 i = 0; i < rewardTokens.length(); i++) { ./contracts/VE3DRewardPool.sol:326: for (uint256 i = 0; i < length; i++) { ./contracts/ExtraRewardStashV2.sol:71: for (uint256 i = 0; i < length; i++) { ./contracts/ExtraRewardStashV2.sol:78: for (uint256 i = 0; i < length; i++) { ./contracts/ExtraRewardStashV2.sol:137: for (uint256 i = 0; i < maxRewards; i++) { ./contracts/ExtraRewardStashV2.sol:181: for (uint256 i = 0; i < tokenCount; i++) { ./contracts/ExtraRewardStashV2.sol:213: for (uint256 i = 0; i < tokenCount; i++) { ./contracts/RewardFactory.sol:49: for (uint256 i = 0; i < length; i++) { ./contracts/RewardFactory.sol:66: for (uint256 i = 0; i < length; i++) { ./contracts/VE3DLocker.sol:207: for (uint256 i; i < rewardTokens.length; i++) { ./contracts/VE3DLocker.sol:286: for (uint256 i = 0; i < userRewards.length; i++) { ./contracts/VE3DLocker.sol:425: for (uint256 i = 0; i < 128; i++) { ./contracts/VE3DLocker.sol:457: for (uint256 i = nextUnlockIndex; i < locks.length; i++) { ./contracts/VE3DLocker.sol:640: for (uint256 i = nextUnlockIndex; i < length; i++) { ./contracts/VE3DLocker.sol:720: for (uint256 i; i < rewardTokens.length; i++) { ./contracts/VE3DLocker.sol:803: for (uint256 i = 0; i < rewardTokens.length; i++) { ./contracts/BaseRewardPool.sol:176: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:199: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:218: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:245: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:282: for (uint256 i = 0; i < extraRewards.length; i++) {
The loop bounds are calculated with array.length
which are calculated in every loop iterations which can result in high gas.
The array length can be cached instead of calculating in every loop iteration to save gas.
// Before for (uint i = 0; i < _tokenVote.length; i++) { } // After uint len = _tokenVote.length; for (uint i = 0; i < len; i++) { }
The instances where this pattern can be applied is found as follows
> grep -rn './contracts' -e 'for.*[.]length' ./contracts/VoterProxy.sol:217: for (uint256 i = 0; i < _tokenVote.length; i++) { ./contracts/Booster.sol:329: for (uint256 i = 0; i < poolInfo.length; i++) { ./contracts/VE3DRewardPool.sol:148: for (uint256 i = 0; i < rewardTokens.length(); i++) { ./contracts/VE3DRewardPool.sol:281: for (uint256 i = 0; i < rewardTokens.length(); i++) { ./contracts/VE3DLocker.sol:207: for (uint256 i; i < rewardTokens.length; i++) { ./contracts/VE3DLocker.sol:286: for (uint256 i = 0; i < userRewards.length; i++) { ./contracts/VE3DLocker.sol:315: for (uint256 i = locks.length - 1; i + 1 != 0; i--) { ./contracts/VE3DLocker.sol:360: for (uint256 i = locks.length - 1; i + 1 != 0; i--) { ./contracts/VE3DLocker.sol:457: for (uint256 i = nextUnlockIndex; i < locks.length; i++) { ./contracts/VE3DLocker.sol:720: for (uint256 i; i < rewardTokens.length; i++) { ./contracts/VE3DLocker.sol:803: for (uint256 i = 0; i < rewardTokens.length; i++) { ./contracts/BaseRewardPool.sol:176: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:199: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:218: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:245: for (uint256 i = 0; i < extraRewards.length; i++) { ./contracts/BaseRewardPool.sol:282: for (uint256 i = 0; i < extraRewards.length; i++) {
#0 - GalloDaSballo
2022-07-14T01:44:25Z
Should save at most 1k gas