Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas Optimizations #158

Open
code423n4 opened this issue May 22, 2022 · 0 comments
Open

Gas Optimizations #158

code423n4 opened this issue May 22, 2022 · 0 comments
Labels
bug Something isn't working duplicate This issue or pull request already exists G (Gas Optimization)

Comments

@code423n4
Copy link
Contributor

Gas Optimizations Report

For-loops: Index initialized with default value

Uninitialized uint variables are assigned with a default value of 0.

Thus, in for-loops, explicitly initializing an index with 0 costs unnecesary gas. For example, the following code:

for (uint256 i = 0; i < length; ++i) {

can be changed to:

for (uint256 i; i < length; ++i) {

Consider declaring the following lines without explicitly setting the index to 0:

2022-05-aura/convex-platform/contracts/contracts/ArbitartorVault.sol:
  49:        for(uint256 i = 0; i < _toPids.length; i++){

2022-05-aura/convex-platform/contracts/contracts/ConvexMasterChef.sol:
 180:        for (uint256 pid = 0; pid < length; ++pid) {

2022-05-aura/convex-platform/contracts/contracts/PoolManagerSecondaryProxy.sol:
  69:        for(uint i=0; i < usedList.length; i++){

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
  29:        uint256 public platformFee = 0; //possible fee to build treasury
 379:        for(uint i=0; i < poolInfo.length; i++){
 538:        for(uint256 i = 0; i < _gauge.length; i++){

2022-05-aura/convex-platform/contracts/contracts/BoosterOwner.sol:
 144:        for(uint256 i = 0; i < poolCount; i++){

2022-05-aura/convex-platform/contracts/contracts/ExtraRewardStashV3.sol:
 125:        for(uint256 i = 0; i < maxRewards; i++){
 199:        for(uint i=0; i < tCount; i++){

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
 214:        for(uint i=0; i < extraRewards.length; i++){
 230:        for(uint i=0; i < extraRewards.length; i++){
 262:        for(uint i=0; i < extraRewards.length; i++){
 296:        for(uint i=0; i < extraRewards.length; i++){

2022-05-aura/contracts/AuraVestedEscrow.sol:
 100:        for (uint256 i = 0; i < _recipient.length; i++) {

2022-05-aura/contracts/AuraLocker.sol:
 174:        for (uint256 i = 0; i < rewardTokensLength; i++) {
 773:        for (uint256 i = 0; i < userRewardsLength; i++) {

2022-05-aura/contracts/BalLiquidityProvider.sol:
  51:        for (uint256 i = 0; i < 2; i++) {

2022-05-aura/contracts/AuraClaimZap.sol:
 143:        for (uint256 i = 0; i < rewardContracts.length; i++) {
 147:        for (uint256 i = 0; i < extraRewardContracts.length; i++) {
 151:        for (uint256 i = 0; i < tokenRewardContracts.length; i++) {

For-Loops: Cache array length outside of loops

Reading an array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.

Caching the array length in the stack saves around 3 gas per iteration.

For example:

for (uint256 i; i < arr.length; ++i) {}

can be changed to:

uint256 len = arr.length;
for (uint256 i; i < len; ++i) {}

Consider making the following change to these lines:

2022-05-aura/convex-platform/contracts/contracts/ArbitartorVault.sol:
  49:        for(uint256 i = 0; i < _toPids.length; i++){

2022-05-aura/convex-platform/contracts/contracts/PoolManagerSecondaryProxy.sol:
  69:        for(uint i=0; i < usedList.length; i++){

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
 379:        for(uint i=0; i < poolInfo.length; i++){
 538:        for(uint256 i = 0; i < _gauge.length; i++){

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
 214:        for(uint i=0; i < extraRewards.length; i++){
 230:        for(uint i=0; i < extraRewards.length; i++){
 262:        for(uint i=0; i < extraRewards.length; i++){
 296:        for(uint i=0; i < extraRewards.length; i++){

2022-05-aura/contracts/AuraVestedEscrow.sol:
 100:        for (uint256 i = 0; i < _recipient.length; i++) {

2022-05-aura/contracts/AuraLocker.sol:
 696:        for (uint256 i = nextUnlockIndex; i < locks.length; i++) {

2022-05-aura/contracts/AuraClaimZap.sol:
 143:        for (uint256 i = 0; i < rewardContracts.length; i++) {
 147:        for (uint256 i = 0; i < extraRewardContracts.length; i++) {
 151:        for (uint256 i = 0; i < tokenRewardContracts.length; i++) {

For-Loops: Index increments can be left unchecked

From Solidity v0.8 onwards, all arithmetic operations come with implicit overflow and underflow checks.

In for-loops, as it is impossible for the index to overflow, it can be left unchecked to save gas every iteration.

For example, the code below:

for (uint256 i; i < numIterations; ++i) {  
    // ...  
}  

can be changed to:

for (uint256 i; i < numIterations;) {  
    // ...  
    unchecked { ++i; }  
}  

Consider making the following change to these lines:

2022-05-aura/contracts/AuraVestedEscrow.sol:
 100:        for (uint256 i = 0; i < _recipient.length; i++) {

2022-05-aura/contracts/AuraLocker.sol:
 174:        for (uint256 i = 0; i < rewardTokensLength; i++) {
 306:        for (uint256 i; i < rewardTokensLength; i++) {
 410:        for (uint256 i = nextUnlockIndex; i < length; i++) {
 696:        for (uint256 i = nextUnlockIndex; i < locks.length; i++) {
 773:        for (uint256 i = 0; i < userRewardsLength; i++) {

2022-05-aura/contracts/BalLiquidityProvider.sol:
  51:        for (uint256 i = 0; i < 2; i++) {

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
 233:        for (uint256 i = epochIndex; i < tokenEpochs; i++) {

2022-05-aura/contracts/AuraClaimZap.sol:
 143:        for (uint256 i = 0; i < rewardContracts.length; i++) {
 147:        for (uint256 i = 0; i < extraRewardContracts.length; i++) {
 151:        for (uint256 i = 0; i < tokenRewardContracts.length; i++) {

Arithmetics: ++i costs less gas compared to i++ or i += 1

++i costs less gas compared to i++ or i += 1 for unsigned integers, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.

i++ increments i and returns the initial value of i. Which means:

uint i = 1;  
i++; // == 1 but i == 2  

But ++i returns the actual incremented value:

uint i = 1;  
++i; // == 2 and i == 2 too, so no need for a temporary variable  

In the first case, the compiler has to create a temporary variable (when used) for returning 1 instead of 2, thus it costs more gas.

The same logic applies for --i and i--.

Consider using ++i instead of i++ or i += 1 in the following instances:

2022-05-aura/convex-platform/contracts/contracts/ArbitartorVault.sol:
  49:        for(uint256 i = 0; i < _toPids.length; i++){

2022-05-aura/convex-platform/contracts/contracts/PoolManagerSecondaryProxy.sol:
  69:        for(uint i=0; i < usedList.length; i++){

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
 379:        for(uint i=0; i < poolInfo.length; i++){
 538:        for(uint256 i = 0; i < _gauge.length; i++){

2022-05-aura/convex-platform/contracts/contracts/BoosterOwner.sol:
 144:        for(uint256 i = 0; i < poolCount; i++){

2022-05-aura/convex-platform/contracts/contracts/ExtraRewardStashV3.sol:
 125:        for(uint256 i = 0; i < maxRewards; i++){
 199:        for(uint i=0; i < tCount; i++){

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
 214:        for(uint i=0; i < extraRewards.length; i++){
 230:        for(uint i=0; i < extraRewards.length; i++){
 262:        for(uint i=0; i < extraRewards.length; i++){
 296:        for(uint i=0; i < extraRewards.length; i++){

2022-05-aura/contracts/AuraVestedEscrow.sol:
 100:        for (uint256 i = 0; i < _recipient.length; i++) {

2022-05-aura/contracts/AuraLocker.sol:
 174:        for (uint256 i = 0; i < rewardTokensLength; i++) {
 306:        for (uint256 i; i < rewardTokensLength; i++) {
 410:        for (uint256 i = nextUnlockIndex; i < length; i++) {
 426:        nextUnlockIndex++;
 497:        i--;
 664:        for (uint256 i = locksLength; i > 0; i--) {
 696:        for (uint256 i = nextUnlockIndex; i < locks.length; i++) {
 702:        idx++;
 726:        for (uint256 i = epochIndex + 1; i > 0; i--) {
 773:        for (uint256 i = 0; i < userRewardsLength; i++) {

2022-05-aura/contracts/BalLiquidityProvider.sol:
  51:        for (uint256 i = 0; i < 2; i++) {

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
 233:        for (uint256 i = epochIndex; i < tokenEpochs; i++) {

2022-05-aura/contracts/AuraClaimZap.sol:
 143:        for (uint256 i = 0; i < rewardContracts.length; i++) {
 147:        for (uint256 i = 0; i < extraRewardContracts.length; i++) {
 151:        for (uint256 i = 0; i < tokenRewardContracts.length; i++) {

Arithmetics: Use != 0 instead of > 0 for unsigned integers

uint will never go below 0. Thus, > 0 is gas inefficient in comparisons as checking if != 0 is sufficient and costs less gas.

Consider changing > 0 to != 0 in these lines:

2022-05-aura/convex-platform/contracts/contracts/VirtualBalanceRewardPool.sol:
 196:        if (reward > 0) {

2022-05-aura/convex-platform/contracts/contracts/ConvexMasterChef.sol:
 213:        if (user.amount > 0) {

2022-05-aura/convex-platform/contracts/contracts/VoterProxy.sol:
 175:        if (balance > 0) {

2022-05-aura/convex-platform/contracts/contracts/PoolManagerSecondaryProxy.sol:
 104:        require(weight > 0, "must have weight");

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
 223:        require(IFeeDistributor(_feeDistro).getTokenTimeCursor(_feeToken) > 0, "!distro");
 593:        if (crvBal > 0) {
 602:        if(treasury != address(0) && treasury != address(this) && platformFee > 0){

2022-05-aura/convex-platform/contracts/contracts/ExtraRewardStashV3.sol:
 205:        if (amount > 0) {

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
 211:        require(_amount > 0, 'RewardPool : Cannot stake 0');
 227:        require(amount > 0, 'RewardPool : Cannot withdraw 0');
 287:        if (reward > 0) {

2022-05-aura/convex-platform/contracts/contracts/CrvDepositor.sol:
 113:        if(crvBalance > 0){
 145:        if(incentiveCrv > 0){
 169:        require(_amount > 0,"!>0");
 175:        if(incentiveCrv > 0){

2022-05-aura/convex-platform/contracts/contracts/interfaces/BoringMath.sol:
  20:        require(b > 0, "BoringMath: division by zero");
 102:        require(b > 0, "BoringMath: division by zero");
 123:        require(b > 0, "BoringMath: division by zero");
 143:        require(b > 0, "BoringMath: division by zero");

2022-05-aura/contracts/AuraPenaltyForwarder.sol:
  52:        require(bal > 0, "!empty");

2022-05-aura/contracts/AuraMerkleDrop.sol:
 122:        require(_amount > 0, "!amount");

2022-05-aura/contracts/AuraVestedEscrow.sol:
 118:        require(totalLocked[_recipient] > 0, "!funding");

2022-05-aura/contracts/AuraLocker.sol:
 210:        require(rewardData[_rewardsToken].lastUpdateTime > 0, "Reward does not exist");
 259:        require(_amount > 0, "Cannot stake 0");
 309:        if (reward > 0) {
 359:        require(amt > 0, "Nothing locked");
 385:        require(length > 0, "no locks");
 400:        if (_checkDelay > 0) {
 419:        if (_checkDelay > 0) {
 431:        require(locked > 0, "no exp locks");
 443:        if (reward > 0) {
 471:        require(len > 0, "Nothing to delegate");
 496:        if (i > 0) {
 520:        if (ckpts.length > 0) {
 664:        for (uint256 i = locksLength; i > 0; i--) {
 726:        for (uint256 i = epochIndex + 1; i > 0; i--) {
 822:        require(_rewards > 0, "No reward");
 851:        require(_reward > 0, "No reward");

2022-05-aura/contracts/AuraStakingProxy.sol:
 177:        if (crvBal > 0) {
 185:        if (cvxCrvBal > 0) {
 207:        if (bal > 0) {

2022-05-aura/contracts/BalLiquidityProvider.sol:
  57:        require(bal > 0 && bal == _request.maxAmountsIn[i], "!bal");
  70:        require(balAfter > 0, "!mint");

2022-05-aura/contracts/AuraBalRewardPool.sol:
 121:        require(_amount > 0, "RewardPool : Cannot stake 0");
 139:        require(_amount > 0, "RewardPool : Cannot stake 0");
 157:        require(amount > 0, "RewardPool : Cannot withdraw 0");
 178:        if (reward > 0) {
 210:        require(rewardsAvailable > 0, "!balance");

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
 149:        if (claimableTokens > 0) {
 171:        require(_index > 0 && _index < rewardEpochs[_token].length - 1, "!past");

2022-05-aura/contracts/AuraClaimZap.sol:
 196:        if (depositCrvMaxAmount > 0) {
 200:        if (crvBalance > 0) {
 218:        if (depositCvxMaxAmount > 0) {
 221:        if (cvxBalance > 0) {

2022-05-aura/contracts/Aura.sol:
  68:        require(_amount > 0, "Must mint something");

Arithmetics: Use Shift Right/Left instead of Division/Multiplication if possible

A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.

While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.

For example, the following code:

uint256 b = a / 2;
uint256 c = a / 4;
uint256 d = a * 8;

can be changed to:

uint256 b = a >> 1;
uint256 c = a >> 2;
uint256 d = a << 3;

Consider making this change to the following lines:

2022-05-aura/contracts/AuraMath.sol:
  36:        return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
  36:        return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
  36:        return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);

2022-05-aura/contracts/AuraMerkleDrop.sol:
 136:        uint256 penalty = address(auraLocker) == address(0) ? 0 : (_amount * 2) / 10;

2022-05-aura/contracts/AuraBalRewardPool.sol:
 183:        uint256 penalty = (reward * 2) / 10;

Visibility: Consider declaring constants as non-public to save gas

If a constant is not used outside of its contract, declaring it as private or internal instead of public can save gas.

Consider changing the visibility of the following from public to internal or private:

2022-05-aura/convex-platform/contracts/contracts/ConvexMasterChef.sol:
  56:        uint256 public constant BONUS_MULTIPLIER = 2;

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
  30:        uint256 public constant MaxFees = 2500;

2022-05-aura/convex-platform/contracts/contracts/BoosterOwner.sol:
  51:        uint256 public constant FORCE_DELAY = 30 days;

2022-05-aura/contracts/AuraLocker.sol:
  81:        uint256 public constant rewardsDuration = 86400 * 7;
  83:        uint256 public constant lockDuration = rewardsDuration * 17;

2022-05-aura/contracts/Aura.sol:
  28:        uint256 public constant EMISSIONS_MAX_SUPPLY = 5e25;
  29:        uint256 public constant totalCliffs = 500;

Visibility: public functions can be set to external

Calls to external functions are cheaper than public functions. Thus, if a function is not used internally in any contract, it should be set to external to save gas and improve code readability.

Consider changing following functions from public to external:

2022-05-aura/convex-platform/contracts/contracts/ConvexMasterChef.sol:
  96:        function add(
  97:            uint256 _allocPoint,
  98:            IERC20 _lpToken,
  99:            IRewarder _rewarder,
 100:            bool _withUpdate
 101:        ) public onlyOwner {

 121:        function set(
 122:            uint256 _pid,
 123:            uint256 _allocPoint,
 124:            IRewarder _rewarder,
 125:            bool _withUpdate,
 126:            bool _updateRewarder
 127:        ) public onlyOwner {

 209:        function deposit(uint256 _pid, uint256 _amount) public {
 239:        function withdraw(uint256 _pid, uint256 _amount) public {
 283:        function emergencyWithdraw(uint256 _pid) public {

2022-05-aura/convex-platform/contracts/contracts/VoterProxy.sol:
 151:        function isValidSignature(bytes32 _hash, bytes memory) public view returns (bytes4) {

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
 493:        function withdrawAll(uint256 _pid) public returns(bool){

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
 191:        function stakeFor(address _for, uint256 _amount)
 192:            public
 193:            returns(bool)
 194:        {

2022-05-aura/contracts/AuraPenaltyForwarder.sol:
  47:        function forward() public {

2022-05-aura/contracts/AuraMerkleDrop.sol:
 114:        function claim(
 115:            bytes32[] calldata _proof,
 116:            uint256 _amount,
 117:            bool _lock
 118:        ) public returns (bool) {

 149:        function forwardPenalty() public {

2022-05-aura/contracts/BalLiquidityProvider.sol:
  46:        function provideLiquidity(bytes32 _poolId, IVault.JoinPoolRequest memory _request) public {

2022-05-aura/contracts/AuraBalRewardPool.sol:
 138:        function stakeFor(address _for, uint256 _amount) public updateReward(_for) returns (bool) {

 152:        function withdraw(
 153:            uint256 amount,
 154:            bool claim,
 155:            bool lock
 156:        ) public updateReward(msg.sender) returns (bool) {

 195:        function forwardPenalty() public {

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
 127:        function getReward(
 128:            address _account,
 129:            address _token,
 130:            uint256 _startIndex
 131:        ) public {

Errors: Reduce the length of error messages (long revert strings)

Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.

Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.

In these instances, consider shortening the revert strings to fit within 32 bytes, or using custom errors:

2022-05-aura/contracts/AuraLocker.sol:
 197:        require(_rewardsToken != address(stakingToken), "Cannot add StakingToken as reward");

Errors: Use custom errors instead of revert strings

Since Solidity v0.8.4, custom errors should be used instead of revert strings due to:

  • Cheaper deployment cost
  • Lower runtime cost upon revert

Taken from Custom Errors in Solidity:

Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.

Custom errors can be defined using of the error statement, both inside or outside of contracts.

Instances where custom errors can be used instead:

2022-05-aura/contracts/AuraPenaltyForwarder.sol:
  48:        require(block.timestamp > lastDistribution + distributionDelay, "!elapsed");
  52:        require(bal > 0, "!empty");

2022-05-aura/contracts/AuraMath.sol:
  40:        require(a <= type(uint224).max, "AuraMath: uint224 Overflow");
  45:        require(a <= type(uint128).max, "AuraMath: uint128 Overflow");
  50:        require(a <= type(uint112).max, "AuraMath: uint112 Overflow");
  55:        require(a <= type(uint96).max, "AuraMath: uint96 Overflow");
  60:        require(a <= type(uint32).max, "AuraMath: uint32 Overflow");

2022-05-aura/contracts/AuraMinter.sol:
  32:        require(block.timestamp > inflationProtectionTime, "Inflation protected for now");

2022-05-aura/contracts/AuraMerkleDrop.sol:
  69:        require(_expiresAfter > 2 weeks, "!expiry");
  78:        require(msg.sender == dao, "!auth");
  84:        require(msg.sender == dao, "!auth");
  85:        require(merkleRoot == bytes32(0), "already set");
  91:        require(msg.sender == dao, "!auth");
  97:        require(msg.sender == dao, "!auth");
  98:        require(block.timestamp > expiryTime, "!expired");
 105:        require(msg.sender == dao, "!auth");
 119:        require(merkleRoot != bytes32(0), "!root");
 120:        require(block.timestamp > startTime, "!started");
 121:        require(block.timestamp < expiryTime, "!active");
 122:        require(_amount > 0, "!amount");
 123:        require(hasClaimed[msg.sender] == false, "already claimed");
 126:        require(MerkleProof.verify(_proof, merkleRoot, leaf), "invalid proof");
 152:        require(penaltyForwarder != address(0), "!forwarder");

2022-05-aura/contracts/ClaimFeesHelper.sol:
  45:        require(tokenTime > lastTokenTimes[address(_token)], "not time yet");

2022-05-aura/contracts/AuraVestedEscrow.sol:
  56:        require(starttime_ >= block.timestamp, "start must be future");
  57:        require(endtime_ > starttime_, "end must be greater");
  66:        require(totalTime >= 16 weeks, "!short");
  78:        require(msg.sender == admin, "!auth");
  87:        require(msg.sender == admin, "!auth");
  97:        require(!initialised, "initialised already");
 117:        require(msg.sender == admin, "!auth");
 118:        require(totalLocked[_recipient] > 0, "!funding");
 185:        require(address(auraLocker) != address(0), "!auraLocker");

2022-05-aura/contracts/CrvDepositorWrapper.sol:
  42:        require(poolAddress != address(0), "!poolAddress");
 119:        require(IERC20(BALANCER_POOL_TOKEN).approve(crvDeposit, type(uint256).max), "!approval");

2022-05-aura/contracts/AuraLocker.sol:
 196:        require(rewardData[_rewardsToken].lastUpdateTime == 0, "Reward already exists");
 197:        require(_rewardsToken != address(stakingToken), "Cannot add StakingToken as reward");
 210:        require(rewardData[_rewardsToken].lastUpdateTime > 0, "Reward does not exist");
 216:        require(_rate <= 500, "over max rate"); //max 5% per epoch
 217:        require(_delay >= 2, "min delay"); //minimum 2 epochs of grace
 232:        require(_tokenAddress != address(stakingToken), "Cannot withdraw staking token");
 233:        require(rewardData[_tokenAddress].lastUpdateTime == 0, "Cannot withdraw reward token");
 259:        require(_amount > 0, "Cannot stake 0");
 260:        require(!isShutdown, "shutdown");
 353:        require(isShutdown, "Must be shutdown");
 359:        require(amt > 0, "Nothing locked");
 385:        require(length > 0, "no locks");
 431:        require(locked > 0, "no exp locks");
 471:        require(len > 0, "Nothing to delegate");
 472:        require(newDelegatee != address(0), "Must delegate to someone");
 476:        require(newDelegatee != oldDelegatee, "Must choose new delegatee");
 598:        require(timestamp <= block.timestamp, "ERC20Votes: block not yet mined");
 616:        require(timestamp < block.timestamp, "ERC20Votes: block not yet mined");
 655:        require(epochStart < block.timestamp, "Epoch is in the future");
 719:        require(epochStart < block.timestamp, "Epoch is in the future");
 821:        require(rewardDistributors[cvxCrv][msg.sender], "!authorized");
 822:        require(_rewards > 0, "No reward");
 849:        require(_rewardsToken != cvxCrv, "Use queueNewRewards");
 850:        require(rewardDistributors[_rewardsToken][msg.sender], "Must be rewardsDistributor");
 851:        require(_reward > 0, "No reward");

2022-05-aura/contracts/AuraStakingProxy.sol:
  89:        require(msg.sender == owner, "!auth");
  90:        require(_outputBps > 9000 && _outputBps < 10000, "Invalid output bps");
 100:        require(msg.sender == owner, "!auth");
 108:        require(msg.sender == owner, "!auth");
 116:        require(msg.sender == owner, "!auth");
 117:        require(pendingOwner != address(0), "invalid owner");
 128:        require(msg.sender == owner, "!auth");
 129:        require(_incentive <= 100, "too high");
 138:        require(msg.sender == owner, "!auth");
 158:        require(msg.sender == owner, "!auth");
 159:        require(_token != crv && _token != cvx && _token != cvxCrv, "not allowed");
 172:        require(msg.sender == keeper, "!auth");
 203:        require(address(_token) != crv && address(_token) != cvxCrv, "not allowed");

2022-05-aura/contracts/BalLiquidityProvider.sol:
  47:        require(msg.sender == provider, "!auth");
  48:        require(_request.assets.length == 2 && _request.maxAmountsIn.length == 2, "!valid");
  49:        require(pairToken.balanceOf(address(this)) > minPairAmount, "!minLiq");
  53:        require(asset == address(startToken) || asset == address(pairToken), "!asset");
  57:        require(bal > 0 && bal == _request.maxAmountsIn[i], "!bal");
  65:        require(supplyBefore == 0, "!init");
  70:        require(balAfter > 0, "!mint");
  79:        require(msg.sender == dao, "!auth");
  89:        require(msg.sender == provider || msg.sender == dao, "!auth");

2022-05-aura/contracts/AuraBalRewardPool.sol:
  77:        require(_startDelay < 2 weeks, "!delay");
 121:        require(_amount > 0, "RewardPool : Cannot stake 0");
 139:        require(_amount > 0, "RewardPool : Cannot stake 0");
 157:        require(amount > 0, "RewardPool : Cannot withdraw 0");
 206:        require(msg.sender == rewardManager || block.timestamp > startTime, "!authorized");
 207:        require(rewardRate == 0, "!one time");
 210:        require(rewardsAvailable > 0, "!balance");

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
  68:        require(_epoch <= latestEpoch, "Cannot assign to the future");
  74:        require(len == 0 || rewardEpochs[_token][len - 1] < _epoch, "Cannot backdate to this epoch");
 171:        require(_index > 0 && _index < rewardEpochs[_token].length - 1, "!past");
 172:        require(_index >= userClaims[_token][msg.sender], "already claimed");

2022-05-aura/contracts/AuraClaimZap.sol:
  96:        require(msg.sender == owner, "!auth");
 137:        require(tokenRewardContracts.length == tokenRewardTokens.length, "!parity");

2022-05-aura/contracts/Aura.sol:
  66:        require(msg.sender == operator, "Only operator");
  67:        require(totalSupply() == 0, "Only once");
  68:        require(_amount > 0, "Must mint something");
  69:        require(_minter != address(0), "Invalid minter");
  92:        require(totalSupply() != 0, "Not initialised");
 129:        require(msg.sender == minter, "Only minter");

Unecessary initialization of variables with default values

Uninitialized variables are assigned with a default value depending on its type:

  • uint: 0
  • bool: false
  • address: address(0)

Thus, explicitly initializing a variable with its default value costs unnecesary gas. For example, the following code:

bool b = false;
address c = address(0);
uint256 a = 0;
for (uint256 i = 0; i < length; ++i) {

can be changed to:

uint256 a;
bool b;
address c;
for (uint256 i; i < length; ++i) {

Consider declaring the following lines without explicitly setting a value:

2022-05-aura/convex-platform/contracts/contracts/VirtualBalanceRewardPool.sol:
  89:        uint256 public periodFinish = 0;
  90:        uint256 public rewardRate = 0;
  93:        uint256 public queuedRewards = 0;
  94:        uint256 public currentRewards = 0;
  95:        uint256 public historicalRewards = 0;

2022-05-aura/convex-platform/contracts/contracts/ConvexMasterChef.sol:
  63:        uint256 public totalAllocPoint = 0;

2022-05-aura/convex-platform/contracts/contracts/VoterProxy.sol:
 308:        uint256 _balance = 0;

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
  71:        uint256 public periodFinish = 0;
  72:        uint256 public rewardRate = 0;
  75:        uint256 public queuedRewards = 0;
  76:        uint256 public currentRewards = 0;
  77:        uint256 public historicalRewards = 0;

2022-05-aura/convex-platform/contracts/contracts/CrvDepositor.sol:
  36:        uint256 public incentiveCrv = 0;

2022-05-aura/contracts/AuraMerkleDrop.sol:
  29:        uint256 public pendingPenalty = 0;

2022-05-aura/contracts/AuraVestedEscrow.sol:
  33:        bool public initialised = false;
  99:        uint256 totalAmount = 0;

2022-05-aura/contracts/AuraLocker.sol:
  72:        uint256 public queuedCvxCrvRewards = 0;
 114:        bool public isShutdown = false;
 381:        uint256 reward = 0;
 485:        uint256 futureUnlocksSum = 0;
 540:        uint256 unlocksSinceLatestCkpt = 0;
 630:        uint256 low = 0;

2022-05-aura/contracts/AuraBalRewardPool.sol:
  35:        uint256 public pendingPenalty = 0;
  38:        uint256 public periodFinish = 0;
  39:        uint256 public rewardRate = 0;

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
 231:        uint256 claimableTokens = 0;

Unnecessary definition of variables

Some variables are defined even though they are only used once in their respective functions. Not defining these variables can help to reduce gas cost and contract size.

Instances include:

2022-05-aura/convex-platform/contracts/contracts/VirtualBalanceRewardPool.sol:
 226:        uint256 currentAtNow = rewardRate * elapsedTime;

2022-05-aura/convex-platform/contracts/contracts/ConvexMasterChef.sol:
 146:        uint256 clampedTo = _to > endBlock ? endBlock : _to;
 147:        uint256 clampedFrom = _from > endBlock ? endBlock : _from;
 179:        uint256 length = poolInfo.length;

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
 383:        address token = pool.lptoken;
 384:        address gauge = pool.gauge;
 440:        address lptoken = poolInfo[_pid].lptoken;
 456:        address gauge = pool.gauge;
 459:        address token = pool.token;
 494:        address token = poolInfo[_pid].token;
 504:        address rewardContract = poolInfo[_pid].crvRewards;
 548:        address stash = poolInfo[_pid].stash;
 561:        address gauge = poolInfo[_pid].gauge;
 576:        address gauge = pool.gauge;
 669:        address rewardContract = poolInfo[_pid].crvRewards;

2022-05-aura/convex-platform/contracts/contracts/ExtraRewardStashV3.sol:
 198:        uint256 tCount = tokenList.length;

2022-05-aura/convex-platform/contracts/contracts/BaseRewardPool.sol:
 338:        uint256 currentAtNow = rewardRate * elapsedTime;

2022-05-aura/convex-platform/contracts/contracts/CrvDepositor.sol:
  95:        uint256 unlockInWeeks = (unlockAt/WEEK)*WEEK;
 191:        bool depositOnly = _stakeAddress == address(0);

2022-05-aura/convex-platform/contracts/contracts/ProxyFactory.sol:
  12:        bytes20 targetBytes = bytes20(target);

2022-05-aura/contracts/AuraVestedEscrow.sol:
 162:        uint256 elapsed = _time - startTime;

2022-05-aura/contracts/CrvDepositorWrapper.sol:
  73:        uint256 minOut = (((amount * 1e18) / bptOraclePrice) * minOutBps) / 10000;

2022-05-aura/contracts/AuraLocker.sol:
 173:        uint256 rewardTokensLength = rewardTokens.length;
 305:        uint256 rewardTokensLength = rewardTokens.length;
 328:        uint256 epochindex = epochs.length;
 355:        LockedBalance[] memory locks = userLocks[msg.sender];
 663:        uint256 locksLength = locks.length;
 694:        uint256 nextUnlockIndex = userBalance.nextUnlockIndex;
 724:        uint256 epochIndex = _epoch > lastIndex ? lastIndex : _epoch;
 772:        uint256 userRewardsLength = userRewards.length;
 838:        uint256 currentAtNow = rdata.rewardRate * elapsedTime;

2022-05-aura/contracts/ExtraRewardsDistributor.sol:
  97:        uint256 rPerT = (_amount * 1e20) / supply;

Storage variables should be declared immutable when possible

If a storage variable is assigned only in the constructor, it should be declared as immutable. This would help to reduce gas costs as calls to immutable variables are much cheaper than regular state variables, as seen from the Solidity Docs:

Compared to regular state variables, the gas costs of constant and immutable variables are much lower. Immutable variables are evaluated once at construction time and their value is copied to all the places in the code where they are accessed.

Consider declaring these variables as immutable:

2022-05-aura/convex-platform/contracts/contracts/DepositToken.sol:
  22:        address public operator;

Boolean comparisons

Comparing to a constant (true or false) is a bit more expensive than directly checking the boolean value.

Considering changing the following lines from var == true to var, or var == false to !var:

2022-05-aura/convex-platform/contracts/contracts/ArbitartorVault.sol:
  54:        require(shutdown==false,"pool closed");

2022-05-aura/convex-platform/contracts/contracts/Booster.sol:
 400:        require(pool.shutdown == false, "pool is closed");
 574:        require(pool.shutdown == false, "pool is closed");

Variables declared as constant are expressions, not constants

Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.

If the variable was immutable instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.

See: ethereum/solidity#9232:

Consequences: each usage of a “constant” costs ~100 gas more on each access (it is still a little better than storing the result in storage, but not much). since these are not real constants, they can’t be referenced from a real constant environment (e.g. from assembly, or from another library)

2022-05-aura/convex-platform/contracts/contracts/CrvDepositor.sol:
  26:        uint256 private constant MAXTIME = 1 * 364 * 86400;
  27:        uint256 private constant WEEK = 7 * 86400;

2022-05-aura/contracts/AuraLocker.sol:
  81:        uint256 public constant rewardsDuration = 86400 * 7;
  83:        uint256 public constant lockDuration = rewardsDuration * 17;

Change these expressions from constant to immutable and implement the calculation in the constructor. Alternatively, hardcode these values in the constants and add a comment to say how the value was calculated.

@code423n4 code423n4 added bug Something isn't working G (Gas Optimization) labels May 22, 2022
code423n4 added a commit that referenced this issue May 22, 2022
@0xMaharishi 0xMaharishi added the duplicate This issue or pull request already exists label May 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working duplicate This issue or pull request already exists G (Gas Optimization)
Projects
None yet
Development

No branches or pull requests

2 participants