code-423n4 / 2022-05-vetoken-findings

1 stars 1 forks source link

Gas Optimizations #114

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

Gas report

Innecesary safeAproove

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

Comparasions

> 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

Unnecesary use of SafeMath

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.

Cache variables

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;

On 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);
             }
         }
     }

On 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;

on 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;

On 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

On 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
                     );

ARITHMETICS

++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; }
}

Suggestion

On 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;

On 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;

On 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;

On 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;

On 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;
     }

On 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);
     }

On 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; }
             }
         }
     }

On 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;

No need to initialize variables with default values

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

GalloDaSballo commented 2 years ago

Should save a few thousands of gas, let's guesstimate at 4k

Missing the immutables puts this at huge disadvantage