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: 42/71
Findings: 2
Award: $155.54
🌟 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.4975 USDT - $100.50
In the Booster
: The constructor not emit the OwnerUpdated
, FeeManagerUpdated
, PoolManagerUpdated
and VoteDelegateUpdated
events
The VoterProxy
, VeTokenMinter
only have one event
Emit an event when the storage change or when some important happend
If define a interface like IRewards
, inherit from that, this is helpfull to avoid interface implementations mistake and clarify code
Some tokens (like USDT L199) do not work when changing the allowance from an existing non-zero allowance value. They must first be approved by zero and then the actual allowance must be approved.
Mitigation, change
IERC20(token).safeApprove(rewardContract, _amount);
to:
IERC20(token).safeApprove(rewardContract, 0); IERC20(token).safeApprove(rewardContract, _amount);
Change
IERC20(minter).safeApprove(_stakeAddress, _amount);
to:
IERC20(minter).safeApprove(_stakeAddress, 0); IERC20(minter).safeApprove(_stakeAddress, _amount);
getReward
On VE3DLocker.sol#L719
you use a reentrancy guard.
Consider adding a reentrancy guard also to VE3DRewardPool.sol#L275
#0 - GalloDaSballo
2022-07-06T23:23:09Z
NC
Valid Refactoring
Disagree as the called code transferFrom
s the total amount, bring back allowance to zero
Valid Low
Good short report
#1 - GalloDaSballo
2022-07-06T23:23:22Z
1R, 1L, 1NC
🌟 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
55.0413 USDT - $55.04
There is no need to set safeAppove to 0 if you are gonna set the maximum. Replace;
IERC20(token).safeApprove(_address, 0); IERC20(token).safeApprove(_address, type(uint256).max);
With;
IERC20(token).safeApprove(_address, type(uint256).max);
Recomendation;
diff --git a/contracts/VE3DLocker.sol b/contracts/VE3DLocker.sol index f838e67..79ac845 100644 --- a/contracts/VE3DLocker.sol +++ b/contracts/VE3DLocker.sol @@ -208,17 +208,12 @@ contract VE3DLocker is ReentrancyGuard, Ownable { address _rewardsToken = rewardTokens[i]; if (rewardData[_rewardsToken].isVeAsset) { // set approve for staking VE3Token - IERC20(rewardData[_rewardsToken].ve3Token).safeApprove( - rewardData[_rewardsToken].ve3TokenStaking, - 0 - ); IERC20(rewardData[_rewardsToken].ve3Token).safeApprove( rewardData[_rewardsToken].ve3TokenStaking, type(uint256).max ); // set approve for locking veAsset - IERC20(_rewardsToken).safeApprove(rewardData[_rewardsToken].veAssetDeposits, 0); IERC20(_rewardsToken).safeApprove( rewardData[_rewardsToken].veAssetDeposits, type(uint256).max
> 0
is less efficient than != 0
for unsigned integers (with proof)
!= 0
costs less gas compared to > 0
for unsigned integers in require statements with the optimizer enabled (6 gas)
Recomendation;
diff --git a/contracts/BaseRewardPool.sol b/contracts/BaseRewardPool.sol index 450220b..d41a954 100644 --- a/contracts/BaseRewardPool.sol +++ b/contracts/BaseRewardPool.sol @@ -170,7 +170,7 @@ contract BaseRewardPool { } function stake(uint256 _amount) public updateReward(msg.sender) returns (bool) { - require(_amount > 0, "RewardPool : Cannot stake 0"); + require(_amount != 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards for (uint256 i = 0; i < extraRewards.length; i++) { @@ -193,7 +193,7 @@ contract BaseRewardPool { } function stakeFor(address _for, uint256 _amount) public updateReward(_for) returns (bool) { - require(_amount > 0, "RewardPool : Cannot stake 0"); + require(_amount != 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards for (uint256 i = 0; i < extraRewards.length; i++) { @@ -212,7 +212,7 @@ contract BaseRewardPool { } function withdraw(uint256 amount, bool claim) public updateReward(msg.sender) returns (bool) { - require(amount > 0, "RewardPool : Cannot withdraw 0"); + require(amount != 0, "RewardPool : Cannot withdraw 0"); //also withdraw from linked rewards for (uint256 i = 0; i < extraRewards.length; i++) { diff --git a/contracts/VE3DRewardPool.sol b/contracts/VE3DRewardPool.sol index ebc391c..a23d284 100644 --- a/contracts/VE3DRewardPool.sol +++ b/contracts/VE3DRewardPool.sol @@ -207,7 +207,7 @@ contract VE3DRewardPool is Ownable { } function stake(uint256 _amount) public updateReward(msg.sender) { - require(_amount > 0, "RewardPool : Cannot stake 0"); + require(_amount != 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards uint256 length = extraRewards.length; @@ -231,7 +231,7 @@ contract VE3DRewardPool is Ownable { } function stakeFor(address _for, uint256 _amount) public updateReward(_for) { - require(_amount > 0, "RewardPool : Cannot stake 0"); + require(_amount != 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards uint256 length = extraRewards.length; @@ -250,7 +250,7 @@ contract VE3DRewardPool is Ownable { } function withdraw(uint256 _amount, bool claim) public updateReward(msg.sender) { - require(_amount > 0, "RewardPool : Cannot withdraw 0"); + require(_amount != 0, "RewardPool : Cannot withdraw 0"); //also withdraw from linked rewards uint256 length = extraRewards.length; diff --git a/contracts/VeAssetDepositor.sol b/contracts/VeAssetDepositor.sol index 9bcebe6..d34cd7c 100644 --- a/contracts/VeAssetDepositor.sol +++ b/contracts/VeAssetDepositor.sol @@ -129,7 +129,7 @@ contract VeAssetDepositor { bool _lock, address _stakeAddress ) public { - require(_amount > 0, "!>0"); + require(_amount != 0, "!>0"); if (_lock) { //lock immediately, transfer directly to staker to skip an erc20 transfer
Solidity version 0.8.* already implements overflow and underflow checks by default. Using the BoringMath library (which is more gas expensive than the 0.8.* overflow checks) is therefore redundant.
You could save gas caching variables inside loops and variables that are used more than once.
diff --git a/contracts/BaseRewardPool.sol b/contracts/BaseRewardPool.sol index 450220b..dd216d7 100644 --- a/contracts/BaseRewardPool.sol +++ b/contracts/BaseRewardPool.sol @@ -172,9 +172,11 @@ contract BaseRewardPool { function stake(uint256 _amount) public updateReward(msg.sender) returns (bool) { require(_amount > 0, "RewardPool : Cannot stake 0"); + address[] memory _extraRewards = extraRewards; + uint256 length = _extraRewards.length; //also stake to linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { - IRewards(extraRewards[i]).stake(msg.sender, _amount); + for (uint256 i = 0; i < length; i++) { + IRewards(_extraRewards[i]).stake(msg.sender, _amount); } _totalSupply = _totalSupply.add(_amount); @@ -195,9 +197,12 @@ contract BaseRewardPool { function stakeFor(address _for, uint256 _amount) public updateReward(_for) returns (bool) { require(_amount > 0, "RewardPool : Cannot stake 0"); + address[] memory _extraRewards = extraRewards; + uint256 length = _extraRewards.length; + //also stake to linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { - IRewards(extraRewards[i]).stake(_for, _amount); + for (uint256 i = 0; i < length; i++) { + IRewards(_extraRewards[i]).stake(_for, _amount); } //give to _for @@ -214,9 +219,12 @@ contract BaseRewardPool { function withdraw(uint256 amount, bool claim) public updateReward(msg.sender) returns (bool) { require(amount > 0, "RewardPool : Cannot withdraw 0"); + address[] memory _extraRewards = extraRewards; + uint256 length = _extraRewards.length; + //also withdraw from linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { - IRewards(extraRewards[i]).withdraw(msg.sender, amount); + for (uint256 i = 0; i < length; i++) { + IRewards(_extraRewards[i]).withdraw(msg.sender, amount); } _totalSupply = _totalSupply.sub(amount); @@ -241,9 +249,12 @@ contract BaseRewardPool { updateReward(msg.sender) returns (bool) { + address[] memory _extraRewards = extraRewards; + uint256 length = _extraRewards.length; + //also withdraw from linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { - IRewards(extraRewards[i]).withdraw(msg.sender, amount); + for (uint256 i = 0; i < length; i++) { + IRewards(_extraRewards[i]).withdraw(msg.sender, amount); } _totalSupply = _totalSupply.sub(amount); @@ -279,8 +290,11 @@ contract BaseRewardPool { //also get rewards from linked rewards if (_claimExtras) { - for (uint256 i = 0; i < extraRewards.length; i++) { - IRewards(extraRewards[i]).getReward(_account); + address[] memory _extraRewards = extraRewards; + uint256 length = _extraRewards.length; + + for (uint256 i = 0; i < length; i++) { + IRewards(_extraRewards[i]).getReward(_account); } } return true;
Booster.sol
diff --git a/contracts/Booster.sol b/contracts/Booster.sol index 33f7990..db8ac8e 100644 --- a/contracts/Booster.sol +++ b/contracts/Booster.sol @@ -326,8 +326,11 @@ contract Booster { require(msg.sender == owner, "!auth"); isShutdown = true; - for (uint256 i = 0; i < poolInfo.length; i++) { - PoolInfo storage pool = poolInfo[i]; + PoolInfo[] storage _poolInfo = poolInfo; + uint256 length = _poolInfo.length; + + for (uint256 i = 0; i < length; i++) { + PoolInfo storage pool = _poolInfo[i]; if (pool.shutdown) continue; address token = pool.lptoken; @@ -510,9 +513,10 @@ contract Booster { //process extra rewards IStash(stash).processStash(); } - + + address _veAsset = veAsset; //veAsset balance - uint256 veAssetBal = IERC20(veAsset).balanceOf(address(this)); + uint256 veAssetBal = IERC20(_veAsset).balanceOf(address(this)); if (veAssetBal > 0) { uint256 _lockIncentive = veAssetBal.mul(lockIncentive).div(FEE_DENOMINATOR); @@ -527,7 +531,7 @@ contract Booster { //only subtract after address condition check uint256 _platform = veAssetBal.mul(platformFee).div(FEE_DENOMINATOR); veAssetBal = veAssetBal.sub(_platform); - IERC20(veAsset).safeTransfer(treasury, _platform); + IERC20(_veAsset).safeTransfer(treasury, _platform); } //remove incentives from balance @@ -539,29 +543,29 @@ contract Booster { //send incentives for calling if (_callIncentive > 0) { - IERC20(veAsset).safeTransfer(msg.sender, _callIncentive); + IERC20(_veAsset).safeTransfer(msg.sender, _callIncentive); } //send veAsset to lp provider reward contract address rewardContract = pool.veAssetRewards; - IERC20(veAsset).safeTransfer(rewardContract, veAssetBal); + IERC20(_veAsset).safeTransfer(rewardContract, veAssetBal); IRewards(rewardContract).queueNewRewards(veAssetBal); //send lockers' share of veAsset to reward contract if (_lockIncentive > 0) { - IERC20(veAsset).safeTransfer(lockRewards, _lockIncentive); + IERC20(_veAsset).safeTransfer(lockRewards, _lockIncentive); IRewards(lockRewards).queueNewRewards(_lockIncentive); } //send stakers's share of veAsset to VE3D reward contract if (_stakerIncentive > 0) { - IERC20(veAsset).safeTransfer(stakerRewards, _stakerIncentive); - IRewards(stakerRewards).queueNewRewards(veAsset, _stakerIncentive); + IERC20(_veAsset).safeTransfer(stakerRewards, _stakerIncentive); + IRewards(stakerRewards).queueNewRewards(_veAsset, _stakerIncentive); } //send stakers's lock share of veAsset to VE3D locker reward contract if (_stakerLockIncentive > 0) { - IERC20(veAsset).safeTransfer(stakerLockRewards, _stakerLockIncentive); - IRewards(stakerLockRewards).queueNewRewards(veAsset, _stakerLockIncentive); + IERC20(_veAsset).safeTransfer(stakerLockRewards, _stakerLockIncentive); + IRewards(stakerLockRewards).queueNewRewards(_veAsset, _stakerLockIncentive); } } }
ExtraRewardStashV2.sol
diff --git a/contracts/ExtraRewardStashV2.sol b/contracts/ExtraRewardStashV2.sol index b0dc2ac..9b4e1ad 100644 --- a/contracts/ExtraRewardStashV2.sol +++ b/contracts/ExtraRewardStashV2.sol @@ -65,23 +65,24 @@ contract ExtraRewardStashV2 { checkForNewRewardTokens(); uint256 length = tokenCount; + TokenInfo[] storage _tokenInfo = tokenInfo; if (length > 0) { //get previous balances of all tokens uint256[] memory balances = new uint256[](length); for (uint256 i = 0; i < length; i++) { - balances[i] = IERC20(tokenInfo[i].token).balanceOf(staker); + balances[i] = IERC20(_tokenInfo[i].token).balanceOf(staker); } //claim rewards on gauge for staker //booster will call for future proofing (cant assume anyone will always be able to call) IDeposit(operator).claimRewards(pid, gauge); for (uint256 i = 0; i < length; i++) { - address token = tokenInfo[i].token; + address token = _tokenInfo[i].token; uint256 newbalance = IERC20(token).balanceOf(staker); //stash if balance increased if (newbalance > balances[i]) { IStaker(staker).withdraw(token); - tokenInfo[i].lastActiveTime = block.timestamp; + _tokenInfo[i].lastActiveTime = block.timestamp; //make sure this pool is in active list, IRewardFactory(rewardFactory).addActiveReward(token, pid); @@ -98,7 +99,7 @@ contract ExtraRewardStashV2 { } } else { //check if this reward has been inactive too long - if (block.timestamp > tokenInfo[i].lastActiveTime + WEEK) { + if (block.timestamp > _tokenInfo[i].lastActiveTime + WEEK) { //set as inactive IRewardFactory(rewardFactory).removeActiveReward(token, pid); } else { @@ -210,8 +211,9 @@ contract ExtraRewardStashV2 { function processStash() external returns (bool) { require(msg.sender == operator, "!authorized"); + TokenInfo[] storage _tokenInfo = tokenInfo; for (uint256 i = 0; i < tokenCount; i++) { - TokenInfo storage t = tokenInfo[i]; + TokenInfo storage t = _tokenInfo[i]; address token = t.token; if (token == address(0)) continue;
ExtraRewardStashV3.sol
diff --git a/contracts/ExtraRewardStashV3.sol b/contracts/ExtraRewardStashV3.sol index 8f02bcf..6db0d0a 100644 --- a/contracts/ExtraRewardStashV3.sol +++ b/contracts/ExtraRewardStashV3.sol @@ -123,8 +123,9 @@ contract ExtraRewardStashV3 { function processStash() external returns (bool) { require(msg.sender == operator, "!authorized"); + TokenInfo[] storage _tokenInfo = tokenInfo; for (uint256 i = 0; i < tokenCount; i++) { - TokenInfo storage t = tokenInfo[i]; + TokenInfo storage t = _tokenInfo[i]; address token = t.token; if (token == address(0)) continue;
VE3DLocker.sol
diff --git a/contracts/VE3DLocker.sol b/contracts/VE3DLocker.sol index f838e67..752460c 100644 --- a/contracts/VE3DLocker.sol +++ b/contracts/VE3DLocker.sol @@ -204,8 +204,10 @@ contract VE3DLocker is ReentrancyGuard, Ownable { //set approvals for locking veAsset and staking VE3Token function setApprovals() external { - for (uint256 i; i < rewardTokens.length; i++) { - address _rewardsToken = rewardTokens[i]; + address[] memory _rewardTokens = rewardTokens; + uint256 length = _rewardTokens.length; + for (uint256 i; i < length; i++) { + address _rewardsToken = _rewardTokens[i]; if (rewardData[_rewardsToken].isVeAsset) { // set approve for staking VE3Token IERC20(rewardData[_rewardsToken].ve3Token).safeApprove( @@ -281,9 +283,10 @@ contract VE3DLocker is ReentrancyGuard, Ownable { view returns (EarnedData[] memory userRewards) { - userRewards = new EarnedData[](rewardTokens.length); + uint256 length = rewardTokens.length; + userRewards = new EarnedData[](length); Balances storage userBalance = balances[_account]; - for (uint256 i = 0; i < userRewards.length; i++) { + for (uint256 i = 0; i < length; i++) { address token = rewardTokens[i]; userRewards[i].token = token; userRewards[i].amount = _earned(_account, token, userBalance.locked); @@ -312,7 +315,8 @@ contract VE3DLocker is ReentrancyGuard, Ownable { //need to add up since the range could be in the middle somewhere //traverse inversely to make more current queries more gas efficient - for (uint256 i = locks.length - 1; i + 1 != 0; i--) { + uint256 length = locks.length; + for (uint256 i = length - 1; i + 1 != 0; --i) { uint256 lockEpoch = uint256(locks[i].unlockTime).sub(lockDuration); //lock epoch must be less or equal to the epoch we're basing from. if (lockEpoch <= epochTime) { @@ -357,7 +361,8 @@ contract VE3DLocker is ReentrancyGuard, Ownable { uint256 nextEpoch = uint256(epochs[_epoch].date).add(rewardsDuration); //traverse inversely to make more current queries more gas efficient - for (uint256 i = locks.length - 1; i + 1 != 0; i--) { + uint256 length = locks.length; + for (uint256 i = length - 1; i + 1 != 0; --i) { uint256 lockEpoch = uint256(locks[i].unlockTime).sub(lockDuration); //return the next epoch balance @@ -384,7 +389,7 @@ contract VE3DLocker is ReentrancyGuard, Ownable { } //traverse inversely to make more current queries more gas efficient - for (uint256 i = epochindex - 1; i + 1 != 0; i--) { + for (uint256 i = epochindex - 1; i + 1 != 0; --i) { Epoch storage e = epochs[i]; if (uint256(e.date) <= cutoffEpoch) { break; @@ -402,13 +407,14 @@ contract VE3DLocker is ReentrancyGuard, Ownable { ); uint256 cutoffEpoch = epochStart.sub(lockDuration); + Epoch[] memory _epochs = epochs; //traverse inversely to make more current queries more gas efficient - for (uint256 i = _epoch; i + 1 != 0; i--) { - Epoch storage e = epochs[i]; + for (uint256 i = _epoch; i + 1 != 0; --i) { + Epoch storage e = _epochs[i]; if (uint256(e.date) <= cutoffEpoch) { break; } - supply = supply.add(epochs[i].supply); + supply = supply.add(e.supply); } return supply; @@ -454,10 +460,11 @@ contract VE3DLocker is ReentrancyGuard, Ownable { Balances storage userBalance = balances[_user]; uint256 nextUnlockIndex = userBalance.nextUnlockIndex; uint256 idx; - for (uint256 i = nextUnlockIndex; i < locks.length; i++) { + uint256 length = locks.length; + for (uint256 i = nextUnlockIndex; i < length; i++) { if (locks[i].unlockTime > block.timestamp) { if (idx == 0) { - lockData = new LockedBalance[](locks.length - i); + lockData = new LockedBalance[](length - i); } lockData[idx] = locks[i]; idx++; @@ -717,8 +724,10 @@ contract VE3DLocker is ReentrancyGuard, Ownable { // Claim all pending rewards function getReward(address _account, bool _stake) public nonReentrant updateReward(_account) { - for (uint256 i; i < rewardTokens.length; i++) { - address _rewardsToken = rewardTokens[i]; + address[] memory _rewardTokens = rewardTokens; + uint256 length = _rewardTokens.length; + for (uint256 i; i < length; i++) { + address _rewardsToken = _rewardTokens[i]; uint256 reward = rewards[_account][_rewardsToken]; if (reward > 0) { rewards[_account][_rewardsToken] = 0; @@ -800,8 +809,10 @@ contract VE3DLocker is ReentrancyGuard, Ownable { //stack too deep Balances storage userBalance = balances[_account]; - for (uint256 i = 0; i < rewardTokens.length; i++) { - address token = rewardTokens[i]; + address[] memory _rewardTokens = rewardTokens; + uint256 length = _rewardTokens.length; + for (uint256 i = 0; i < length; i++) { + address token = _rewardTokens[i]; rewardData[token].rewardPerTokenStored = _rewardPerToken(token).to208(); rewardData[token].lastUpdateTime = _lastTimeRewardApplicable( rewardData[token].periodFinish
VE3DRewardPool.sol
diff --git a/contracts/VE3DRewardPool.sol b/contracts/VE3DRewardPool.sol index ebc391c..ad0aba5 100644 --- a/contracts/VE3DRewardPool.sol +++ b/contracts/VE3DRewardPool.sol @@ -145,7 +145,8 @@ contract VE3DRewardPool is Ownable { modifier updateReward(address account) { address _rewardToken; - for (uint256 i = 0; i < rewardTokens.length(); i++) { + uint256 length = rewardTokens.length(); + for (uint256 i = 0; i < length; i++) { _rewardToken = rewardTokens.at(i); rewardTokenInfo[_rewardToken].rewardPerTokenStored = rewardPerToken(_rewardToken); rewardTokenInfo[_rewardToken].lastUpdateTime = lastTimeRewardApplicable(_rewardToken); @@ -169,29 +170,31 @@ contract VE3DRewardPool is Ownable { function rewardPerToken(address _rewardToken) public view returns (uint256) { uint256 supply = totalSupply(); + RewardTokenInfo memory info = rewardTokenInfo[_rewardToken]; if (supply == 0) { - return rewardTokenInfo[_rewardToken].rewardPerTokenStored; + return info.rewardPerTokenStored; } return - rewardTokenInfo[_rewardToken].rewardPerTokenStored.add( + info.rewardPerTokenStored.add( lastTimeRewardApplicable(_rewardToken) - .sub(rewardTokenInfo[_rewardToken].lastUpdateTime) - .mul(rewardTokenInfo[_rewardToken].rewardRate) + .sub(info.lastUpdateTime) + .mul(info.rewardRate) .mul(1e18) .div(supply) ); } function earnedReward(address _rewardToken, address account) internal view returns (uint256) { + RewardTokenInfo memory info = rewardTokenInfo[_rewardToken]; return balanceOf(account) .mul( rewardPerToken(_rewardToken).sub( - rewardTokenInfo[_rewardToken].userRewardPerTokenPaid[account] + info.userRewardPerTokenPaid[account] ) ) .div(1e18) - .add(rewardTokenInfo[_rewardToken].rewards[account]); + .add(info.rewards[account]); } function earned(address _rewardToken, address account) external view returns (uint256) { @@ -278,40 +281,42 @@ contract VE3DRewardPool is Ownable { bool _stake ) public updateReward(_account) { address _rewardToken; - for (uint256 i = 0; i < rewardTokens.length(); i++) { + uint256 length = rewardTokens.length(); + for (uint256 i = 0; i < length; i++) { _rewardToken = rewardTokens.at(i); + RewardTokenInfo memory info = rewardTokenInfo[_rewardToken]; uint256 reward = earnedReward(_rewardToken, _account); if (reward > 0) { rewardTokenInfo[_rewardToken].rewards[_account] = 0; - IERC20(_rewardToken).safeApprove(rewardTokenInfo[_rewardToken].veAssetDeposits, 0); + IERC20(_rewardToken).safeApprove(info.veAssetDeposits, 0); IERC20(_rewardToken).safeApprove( - rewardTokenInfo[_rewardToken].veAssetDeposits, + info.veAssetDeposits, reward ); - IVeAssetDeposit(rewardTokenInfo[_rewardToken].veAssetDeposits).deposit( + IVeAssetDeposit(info.veAssetDeposits).deposit( reward, false ); - uint256 ve3TokenBalance = IERC20(rewardTokenInfo[_rewardToken].ve3Token).balanceOf( + uint256 ve3TokenBalance = IERC20(info.ve3Token).balanceOf( address(this) ); if (_stake) { - IERC20(rewardTokenInfo[_rewardToken].ve3Token).safeApprove( - rewardTokenInfo[_rewardToken].ve3TokenRewards, + IERC20(info.ve3Token).safeApprove( + info.ve3TokenRewards, 0 ); - IERC20(rewardTokenInfo[_rewardToken].ve3Token).safeApprove( - rewardTokenInfo[_rewardToken].ve3TokenRewards, + IERC20(info.ve3Token).safeApprove( + info.ve3TokenRewards, ve3TokenBalance ); - IRewards(rewardTokenInfo[_rewardToken].ve3TokenRewards).stakeFor( + IRewards(info.ve3TokenRewards).stakeFor( _account, ve3TokenBalance ); } else { - IERC20(rewardTokenInfo[_rewardToken].ve3Token).safeTransfer( + IERC20(info.ve3Token).safeTransfer( _account, ve3TokenBalance );
++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.
More gas saving if you do a increment without checking the overflow because its safe, for example in a loop;
for(uint256 = 0; i < length;) { // code here unchecked { ++i; } }
BaseRewardPool.sol
diff --git a/contracts/BaseRewardPool.sol b/contracts/BaseRewardPool.sol index 450220b..2b84b85 100644 --- a/contracts/BaseRewardPool.sol +++ b/contracts/BaseRewardPool.sol @@ -173,8 +173,9 @@ contract BaseRewardPool { require(_amount > 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { + for (uint256 i = 0; i < extraRewards.length;) { IRewards(extraRewards[i]).stake(msg.sender, _amount); + unchecked { ++i; } } _totalSupply = _totalSupply.add(_amount); @@ -196,8 +197,9 @@ contract BaseRewardPool { require(_amount > 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { + for (uint256 i = 0; i < extraRewards.length;) { IRewards(extraRewards[i]).stake(_for, _amount); + unchecked { ++i; } } //give to _for @@ -215,8 +217,9 @@ contract BaseRewardPool { require(amount > 0, "RewardPool : Cannot withdraw 0"); //also withdraw from linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { + for (uint256 i = 0; i < extraRewards.length;) { IRewards(extraRewards[i]).withdraw(msg.sender, amount); + unchecked { ++i; } } _totalSupply = _totalSupply.sub(amount); @@ -242,8 +245,9 @@ contract BaseRewardPool { returns (bool) { //also withdraw from linked rewards - for (uint256 i = 0; i < extraRewards.length; i++) { + for (uint256 i = 0; i < extraRewards.length;) { IRewards(extraRewards[i]).withdraw(msg.sender, amount); + unchecked { ++i; } } _totalSupply = _totalSupply.sub(amount); @@ -279,8 +283,9 @@ contract BaseRewardPool { //also get rewards from linked rewards if (_claimExtras) { - for (uint256 i = 0; i < extraRewards.length; i++) { + for (uint256 i = 0; i < extraRewards.length;) { IRewards(extraRewards[i]).getReward(_account); + unchecked { ++i; } } } return true;
Booster.sol
diff --git a/contracts/Booster.sol b/contracts/Booster.sol index 33f7990..6330ecf 100644 --- a/contracts/Booster.sol +++ b/contracts/Booster.sol @@ -326,7 +326,7 @@ contract Booster { require(msg.sender == owner, "!auth"); isShutdown = true; - for (uint256 i = 0; i < poolInfo.length; i++) { + for (uint256 i = 0; i < poolInfo.length; ++i) { PoolInfo storage pool = poolInfo[i]; if (pool.shutdown) continue;
ExtraRewardStashV2.sol
diff --git a/contracts/ExtraRewardStashV2.sol b/contracts/ExtraRewardStashV2.sol index b0dc2ac..d892e55 100644 --- a/contracts/ExtraRewardStashV2.sol +++ b/contracts/ExtraRewardStashV2.sol @@ -68,14 +68,15 @@ contract ExtraRewardStashV2 { if (length > 0) { //get previous balances of all tokens uint256[] memory balances = new uint256[](length); - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { balances[i] = IERC20(tokenInfo[i].token).balanceOf(staker); + unchecked { ++i; } } //claim rewards on gauge for staker //booster will call for future proofing (cant assume anyone will always be able to call) IDeposit(operator).claimRewards(pid, gauge); - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { address token = tokenInfo[i].token; uint256 newbalance = IERC20(token).balanceOf(staker); //stash if balance increased @@ -127,6 +128,7 @@ contract ExtraRewardStashV2 { } } } + unchecked { ++i; } } } return true; @@ -134,11 +136,12 @@ contract ExtraRewardStashV2 { //check if gauge rewards have changed function checkForNewRewardTokens() internal { - for (uint256 i = 0; i < maxRewards; i++) { + for (uint256 i = 0; i < maxRewards;) { address token = IGauge(gauge).reward_tokens(i); if (token == address(0)) { - for (uint256 x = i; x < tokenCount; x++) { + for (uint256 x = i; x < tokenCount;) { IRewardFactory(rewardFactory).removeActiveReward(tokenInfo[x].token, pid); + unchecked { ++x; } } if (i != tokenCount) { tokenCount = i; @@ -146,6 +149,7 @@ contract ExtraRewardStashV2 { break; } setToken(i, token); + unchecked { ++i; } } } @@ -178,7 +182,7 @@ contract ExtraRewardStashV2 { //after depositing/withdrawing, extra incentive tokens are transfered to the staking contract //need to pull them off and stash here. - for (uint256 i = 0; i < tokenCount; i++) { + for (uint256 i = 0; i < tokenCount; ++i) { TokenInfo storage t = tokenInfo[i]; address token = t.token; if (token == address(0)) continue; @@ -210,7 +214,7 @@ contract ExtraRewardStashV2 { function processStash() external returns (bool) { require(msg.sender == operator, "!authorized"); - for (uint256 i = 0; i < tokenCount; i++) { + for (uint256 i = 0; i < tokenCount; ++i) { TokenInfo storage t = tokenInfo[i]; address token = t.token; if (token == address(0)) continue;
ExtraRewardStashV3.sol
diff --git a/contracts/ExtraRewardStashV3.sol b/contracts/ExtraRewardStashV3.sol index 8f02bcf..b61e335 100644 --- a/contracts/ExtraRewardStashV3.sol +++ b/contracts/ExtraRewardStashV3.sol @@ -81,7 +81,7 @@ contract ExtraRewardStashV3 { //check if gauge rewards have changed function checkForNewRewardTokens() internal { - for (uint256 i = 0; i < maxRewards; i++) { + for (uint256 i = 0; i < maxRewards;) { address token = IGauge(gauge).reward_tokens(i); if (token == address(0)) { if (i != tokenCount) { @@ -90,6 +90,7 @@ contract ExtraRewardStashV3 { break; } setToken(i, token); + unchecked { ++i; } } } @@ -123,7 +124,7 @@ contract ExtraRewardStashV3 { function processStash() external returns (bool) { require(msg.sender == operator, "!authorized"); - for (uint256 i = 0; i < tokenCount; i++) { + for (uint256 i = 0; i < tokenCount; ++i) { TokenInfo storage t = tokenInfo[i]; address token = t.token; if (token == address(0)) continue;
RewardFactory.sol
diff --git a/contracts/RewardFactory.sol b/contracts/RewardFactory.sol index bf1eeed..5caa7ef 100644 --- a/contracts/RewardFactory.sol +++ b/contracts/RewardFactory.sol @@ -46,8 +46,9 @@ contract RewardFactory is Ownable { uint256 pid = _pid + 1; //offset by 1 so that we can use 0 as empty uint256 length = activeList.length; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { if (activeList[i] == pid) return true; + unchecked { ++i; } } activeList.push(pid); return true; @@ -63,7 +64,7 @@ contract RewardFactory is Ownable { uint256 pid = _pid + 1; //offset by 1 so that we can use 0 as empty uint256 length = activeList.length; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { if (activeList[i] == pid) { if (i != length - 1) { activeList[i] = activeList[length - 1]; @@ -71,6 +72,7 @@ contract RewardFactory is Ownable { activeList.pop(); break; } + unchecked { ++i; } } return true; }
s.sol
diff --git a/contracts/VE3DLocker.sol b/contracts/VE3DLocker.sol index f838e67..4f41b8d 100644 --- a/contracts/VE3DLocker.sol +++ b/contracts/VE3DLocker.sol @@ -204,7 +204,7 @@ contract VE3DLocker is ReentrancyGuard, Ownable { //set approvals for locking veAsset and staking VE3Token function setApprovals() external { - for (uint256 i; i < rewardTokens.length; i++) { + for (uint256 i; i < rewardTokens.length;) { address _rewardsToken = rewardTokens[i]; if (rewardData[_rewardsToken].isVeAsset) { // set approve for staking VE3Token @@ -224,6 +224,7 @@ contract VE3DLocker is ReentrancyGuard, Ownable { type(uint256).max ); } + unchecked { ++i; } } } @@ -283,10 +284,11 @@ contract VE3DLocker is ReentrancyGuard, Ownable { { userRewards = new EarnedData[](rewardTokens.length); Balances storage userBalance = balances[_account]; - for (uint256 i = 0; i < userRewards.length; i++) { + for (uint256 i = 0; i < userRewards.length;) { address token = rewardTokens[i]; userRewards[i].token = token; userRewards[i].amount = _earned(_account, token, userBalance.locked); + unchecked { ++i; } } return userRewards; } @@ -422,7 +424,7 @@ contract VE3DLocker is ReentrancyGuard, Ownable { //convert to start point _time = _time.div(rewardsDuration).mul(rewardsDuration); - for (uint256 i = 0; i < 128; i++) { + for (uint256 i = 0; i < 128;) { if (min >= max) break; uint256 mid = (min + max + 1) / 2; @@ -435,6 +437,7 @@ contract VE3DLocker is ReentrancyGuard, Ownable { } else { max = mid - 1; } + unchecked { ++i; } } return min; } @@ -454,17 +457,18 @@ contract VE3DLocker is ReentrancyGuard, Ownable { Balances storage userBalance = balances[_user]; uint256 nextUnlockIndex = userBalance.nextUnlockIndex; uint256 idx; - for (uint256 i = nextUnlockIndex; i < locks.length; i++) { + for (uint256 i = nextUnlockIndex; i < locks.length;) { if (locks[i].unlockTime > block.timestamp) { if (idx == 0) { lockData = new LockedBalance[](locks.length - i); } lockData[idx] = locks[i]; - idx++; + unchecked { idx++; } locked = locked.add(locks[i].amount); } else { unlockable = unlockable.add(locks[i].amount); } + unchecked { ++i; } } return (userBalance.locked, unlockable, locked, lockData); }
VE3DRewardPool.sol
diff --git a/contracts/VE3DRewardPool.sol b/contracts/VE3DRewardPool.sol index ebc391c..2a02b59 100644 --- a/contracts/VE3DRewardPool.sol +++ b/contracts/VE3DRewardPool.sol @@ -145,7 +145,7 @@ contract VE3DRewardPool is Ownable { modifier updateReward(address account) { address _rewardToken; - for (uint256 i = 0; i < rewardTokens.length(); i++) { + for (uint256 i = 0; i < rewardTokens.length();) { _rewardToken = rewardTokens.at(i); rewardTokenInfo[_rewardToken].rewardPerTokenStored = rewardPerToken(_rewardToken); rewardTokenInfo[_rewardToken].lastUpdateTime = lastTimeRewardApplicable(_rewardToken); @@ -158,6 +158,7 @@ contract VE3DRewardPool is Ownable { _rewardToken ].rewardPerTokenStored; } + unchecked { ++i; } } _; @@ -235,8 +236,9 @@ contract VE3DRewardPool is Ownable { //also stake to linked rewards uint256 length = extraRewards.length; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { IRewards(extraRewards[i]).stake(_for, _amount); + unchecked { ++i; } } //add supply @@ -278,7 +280,7 @@ contract VE3DRewardPool is Ownable { bool _stake ) public updateReward(_account) { address _rewardToken; - for (uint256 i = 0; i < rewardTokens.length(); i++) { + for (uint256 i = 0; i < rewardTokens.length();) { _rewardToken = rewardTokens.at(i); uint256 reward = earnedReward(_rewardToken, _account); @@ -318,13 +320,15 @@ contract VE3DRewardPool is Ownable { } emit RewardPaid(_account, ve3TokenBalance); } + unchecked { ++i; } } //also get rewards from linked rewards if (_claimExtras) { uint256 length = extraRewards.length; - for (uint256 i = 0; i < length; i++) { + for (uint256 i = 0; i < length;) { IRewards(extraRewards[i]).getReward(_account); + unchecked { ++i; } } } }
VoterProxy.sol
diff --git a/contracts/VoterProxy.sol b/contracts/VoterProxy.sol index 6b33b9c..f82ec6e 100644 --- a/contracts/VoterProxy.sol +++ b/contracts/VoterProxy.sol @@ -214,8 +214,9 @@ contract VoterProxy { //vote IVoting(gaugeProxy).vote(_tokenVote, _weight); } else { - for (uint256 i = 0; i < _tokenVote.length; i++) { + for (uint256 i = 0; i < _tokenVote.length;) { IVoting(gaugeProxy).vote_for_gauge_weights(_tokenVote[i], _weight[i]); + unchecked { ++i; } } } return true;
In solidity all variables initialize in 0, address(0), false, etc. Remove the initialize
This save gas(2258 according to my tests) for each initialize in the deploy of the contract
#0 - GalloDaSballo
2022-07-14T01:43:56Z
Should save a few thousands of gas, let's guesstimate at 4k
Missing the immutables puts this at huge disadvantage