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 #264

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

Gas Optimizations #264

code423n4 opened this issue May 24, 2022 · 0 comments
Labels
bug Something isn't working G (Gas Optimization)

Comments

@code423n4
Copy link
Contributor

Here are gas improvements findings which are commonly seen throughout codes.


[Gas-1] No need to set 0 on uint variables

The default value of uint varibles are 0. Therefore, there is no need to set 0 on uint variables. Not setting 0 on uint variables can reduce the gas cost.

If 0 is set on uint variable like this uint256 unlocksSinceLatestCkpt = 0;, it can rewrite to uint256 unlocksSinceLatestCkpt;.
In for loop, if uint is used like this for (uint256 i = 0; i < userRewardsLength; i++), it can omit setting = 0 and will be for (uint256 i; i < userRewardsLength; i++).


[Gas-2] Use != 0 instead of > 0 on uint variables

uint variables will never be lower than 0. Therefore, > 0 and != 0 have same meanings. Using != 0 can reduce the gas deployment cost, so it is worth using != 0 wherever possible.

If the codebase uses > 0 on uint variables like this:

require(_amount > 0, "Must mint something");

it can rewrite like this:

require(_amount != 0, "Must mint something");

> 0 is used throughout the codebase, so it will list links per file where codes can use != 0 instead of > 0.

Following 2 call sites use i > 0 in for loop for (uint256 i = locksLength; i > 0; i--).
Technically, it can write like this for (uint256 i = locksLength; i != 0; i--).
https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraLocker.sol#L664
https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraLocker.sol#L726


[Gas-3] Use custom errors

Using custom errors can reduce the gas cost.

There are 103 callsites of require check with inline error message. Using the custom errors instead of the inline error messages can reduce the gas cost.


Here are gas improvements findings per file.


AuraBalRewardPool.sol

[Gas-4] Use _totalSupply private state variable instead of calling totalSupply() function

_totalSupply private state variable is defined at AuraBalRewardPool contract. There are 2 places calling totalSupply() function as follows.

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraBalRewardPool.sol#L104

        if (totalSupply() == 0) {

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraBalRewardPool.sol#L109

        return
            rewardPerTokenStored.add(
                lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply())
            );

It is worth considering using _totalSupply private state variable to reduce the gas cost.

They can be written like followings:

        if (_totalSupply == 0) {
        return
            rewardPerTokenStored.add(
                lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(_totalSupply)
            );

Confirmed that both deployements and methods gas costs decreased when using _totalSupply private state variable.


[Gas-5] balance variable does not need to be defined

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraBalRewardPool.sol#L133-L134

        uint256 balance = stakingToken.balanceOf(msg.sender);
        stake(balance);

It can omit defining balance like this to reduce the gas cost:

        stake(stakingToken.balanceOf(msg.sender));

AuraLocker.sol

[Gas-6] false is not needed on isShutdown state variable

The default value of bool type is false. It sets false at isShutdown state variable, but this is not needed.

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraLocker.sol#L114

bool public isShutdown = false;

Removing setting false can reduce the gas cost.

bool public isShutdown;

[Gas-7] Using unchecked in while loop at delegate function

In the while loop at delegate function, i-- is used.

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraLocker.sol#L488-L502

        while (currentLock.unlockTime > upcomingEpoch) {

            ....

            if (i > 0) {
                i--;
                currentLock = locks[i];
            } else {
                break;
            }
        }

i is uint256 and it checks if (i > 0) so it is guaranteed that i-- will not be less than 0. Therefore, this part can add unchecked wrapping i-- like this:

        while (currentLock.unlockTime > upcomingEpoch) {

            ....

            if (i > 0) {
                unchecked {
                    i--;
                }
                currentLock = locks[i];
            } else {
                break;
            }
        }

[Gas-8] Using unchecked in for loop at totalSupplyAtEpoch and balanceAtEpochOf functions

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraLocker.sol#L664

for (uint256 i = locksLength; i > 0; i--) {

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraLocker.sol#L726

for (uint256 i = epochIndex + 1; i > 0; i--) {

It is certain that i will not be underflown since locksLength or epochIndex + 1 are uint256, and having i > 0 makes sure that i-- will not be underflown. Therefore, they can be rewritten like this:

        for (uint256 i = locksLength; i > 0;) {
            uint256 lockEpoch = uint256(locks[i - 1].unlockTime).sub(lockDuration);
            //lock epoch must be less or equal to the epoch we're basing from.
            //also not include the current epoch
            if (lockEpoch < epochStart) {
                if (lockEpoch > cutoffEpoch) {
                    amount = amount.add(locks[i - 1].amount);
                } else {
                    //stop now as no futher checks matter
                    break;
                }
            }

            unchecked {
                i--;
            }
        }

AuraVestedEscrow.sol

[Gas-9] false is not needed on initialised state variable

The default value of bool type is false. It sets false at initialised state variable, but this is not needed.

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraVestedEscrow.sol#L33

bool public initialised = false;

It can be written like this to reduce the gas cost:

bool public initialised;

[Gas-10] Using unchecked on endTime - startTime

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraVestedEscrow.sol#L65

totalTime = endTime - startTime;

Since it checks require(endtime_ > starttime_, "end must be greater"), endTime - startTime will not be underflown. Therefore, unchecked can be used on endTime - startTime like this to reduce the gas cost.

unchecked {
    totalTime = endTime - startTime;
}

[Gas-11] vested variable is not needed at remaining and available functions

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraVestedEscrow.sol#L139-L140

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/AuraVestedEscrow.sol#L148-L149

This is a definition of remaining function.

    function remaining(address _recipient) public view returns (uint256) {
        uint256 vested = _totalVestedOf(_recipient, block.timestamp);
        return totalLocked[_recipient] - vested;
    }

vested variable is defined, but it is not needed. It can write like this:

    function remaining(address _recipient) public view returns (uint256) {
        return totalLocked[_recipient] - _totalVestedOf(_recipient, block.timestamp);
    }

Same workaround can be applied to available function.


BalLiquidityProvider.sol

[Gas-12] unchecked can be used in the for loop at provideLiquidity function

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/BalLiquidityProvider.sol#L51

The range of i variable is from 0 to 1. So it can write like this by using unchecked, and improve gas cost.

        for (uint256 i = 0; i < 2;) {
            address asset = address(_request.assets[i]);
            require(asset == address(startToken) || asset == address(pairToken), "!asset");

            ...

            tkn.safeApprove(address(bVault), 0);
            tkn.safeApprove(address(bVault), bal);

            unchecked {
                i++;
            }
        }

CrvDepositorWrapper.sol

[Gas-13] minOut variable is not needed

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/CrvDepositorWrapper.sol#L73-L74

        uint256 minOut = (((amount * 1e18) / bptOraclePrice) * minOutBps) / 10000;
        return minOut;

It can write as follow to reduce the gas cost.

        return (((amount * 1e18) / bptOraclePrice) * minOutBps) / 10000;

ExtraRewardsDistributor.sol

[Gas-14] latestEpoch variable is not needed

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/ExtraRewardsDistributor.sol#L50-L51

uint256 latestEpoch = auraLocker.epochCount() - 1;
_addReward(_token, _amount, latestEpoch);

It can write as follow to reduce the gas cost.

_addReward(_token, _amount, auraLocker.epochCount() - 1);

[Gas-15] balance variable is not needed

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/ExtraRewardsDistributor.sol#L256-L257

uint256 balance = auraLocker.balanceAtEpochOf(_epoch, _account);
return (balance * rewardData[_token][_epoch]) / 1e20;

It can write as follow to reduce the gas cost.

return (auraLocker.balanceAtEpochOf(_epoch, _account) * rewardData[_token][_epoch]) / 1e20;

[Gas-16] unchecked can be used in the for loop

https://github.com/code-423n4/2022-05-aura/blob/4989a2077546a5394e3650bf3c224669a0f7e690/contracts/ExtraRewardsDistributor.sol#L233-L240

        for (uint256 i = epochIndex; i < tokenEpochs; i++) {
            //only claimable after rewards are "locked in"
            if (rewardEpochs[_token][i] < latestEpoch) {
                claimableTokens += _claimableRewards(_account, _token, rewardEpochs[_token][i]);
                //return index user claims should be set to
                epochIndex = i + 1;
            }
        }

The range of i is from epochIndex to tokenEpochs - 1. Both epochIndex to tokenEpochs - 1 are in uint256 so epochIndex = i + 1 and i++ will not be overflown. Therefore, it can write like this:

        for (uint256 i = epochIndex; i < tokenEpochs;) {
            //only claimable after rewards are "locked in"
            if (rewardEpochs[_token][i] < latestEpoch) {
                claimableTokens += _claimableRewards(_account, _token, rewardEpochs[_token][i]);
                //return index user claims should be set to
                unchecked {
                    epochIndex = i + 1;
                    i++;
                }
            }
        }
@code423n4 code423n4 added bug Something isn't working G (Gas Optimization) labels May 24, 2022
code423n4 added a commit that referenced this issue May 24, 2022
@0xMaharishi 0xMaharishi added the duplicate This issue or pull request already exists label May 28, 2022
@dmvt dmvt removed the duplicate This issue or pull request already exists label Jul 4, 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 G (Gas Optimization)
Projects
None yet
Development

No branches or pull requests

3 participants