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;
++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
Velo.sol, L09
Pair.sol L20, L61, L62
This save gas(2258 according to my tests) for each initialize in the deploy of the contract
Gas report
Innecesary safeAproove
There is no need to set safeAppove to 0 if you are gonna set the maximum. Replace;
With;
Recomendation;
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;
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.
On
Booster.sol
On
ExtraRewardStashV2.sol
on
ExtraRewardStashV3.sol
On
VE3DLocker.sol
On
VE3DRewardPool.sol
ARITHMETICS
++i
costs less gas compared toi++
ori += 1
++i
costs less gas compared toi++
ori += 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;Suggestion
On
BaseRewardPool.sol
On
Booster.sol
On
ExtraRewardStashV2.sol
On
ExtraRewardStashV3.sol
On
RewardFactory.sol
On
s.sol
On
VE3DRewardPool.sol
On
VoterProxy.sol
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