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

Open
code423n4 opened this issue May 7, 2022 · 1 comment
Open

Gas Optimizations #93

code423n4 opened this issue May 7, 2022 · 1 comment
Labels
bug Something isn't working G (Gas Optimization)

Comments

@code423n4
Copy link
Contributor

State variables that could be set immutable

In the following files there are state variables that could be set immutable to save gas.

Code instances:

    _name in VoterID.sol
    _minter in VoterID.sol
    _symbol in VoterID.sol
    globalBeneficiary in PermissionlessBasicPoolFactory.sol
    gateMaster in MerkleEligibility.sol

Caching array length can save gas

Caching the array length is more gas efficient.
This is because access to a local variable in solidity is more efficient than query storage / calldata / memory.
We recommend to change from:

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

to:

uint len = array.length  
for (uint256 i=0; i<len; i++) { ... }

Code instances:

    PermissionlessBasicPoolFactory.sol, rewards, 224
    MerkleLib.sol, proof, 22
    PermissionlessBasicPoolFactory.sol, rewardTokens.pool, 266
    PermissionlessBasicPoolFactory.sol, rewardFunding.pool, 141
    PermissionlessBasicPoolFactory.sol, rewardTokenAddresses, 115

Prefix increments are cheaper than postfix increments

Prefix increments are cheaper than postfix increments.
Further more, using unchecked {++x} is even more gas efficient, and the gas saving accumulates every iteration and can make a real change
There is no risk of overflow caused by increamenting the iteration index in for loops (the ++i in for (uint256 i = 0; i < numIterations; ++i)).
But increments perform overflow checks that are not necessary in this case.
These functions use not using prefix increments (++x) or not using the unchecked keyword:

Code instances:

    change to prefix increment and unchecked: PermissionlessBasicPoolFactory.sol, i, 249
    change to prefix increment and unchecked: PermissionlessBasicPoolFactory.sol, i, 168
    change to prefix increment and unchecked: PermissionlessBasicPoolFactory.sol, i, 266
    change to prefix increment and unchecked: PermissionlessBasicPoolFactory.sol, i, 115
    change to prefix increment and unchecked: PermissionlessBasicPoolFactory.sol, i, 224

Unnecessary index init

In for loops you initialize the index to start from 0, but it already initialized to 0 in default and this assignment cost gas.
It is more clear and gas efficient to declare without assigning 0 and will have the same meaning:

Code instances:

    PermissionlessBasicPoolFactory.sol, 249
    MerkleLib.sol, 22
    PermissionlessBasicPoolFactory.sol, 224
    PermissionlessBasicPoolFactory.sol, 168
    PermissionlessBasicPoolFactory.sol, 266

Storage double reading. Could save SLOAD

Reading a storage variable is gas costly (SLOAD). In cases of multiple read of a storage variable in the same scope, caching the first read (i.e saving as a local variable) can save gas and decrease the
overall gas uses. The following is a list of functions and the storage variables that you read twice:

Code instances:

    PermissionlessBasicPoolFactory.sol: globalBeneficiary is read twice in setGlobalTax
    MerkleIdentity.sol: treeAdder is read twice in addMerkleTree

Unnecessary default assignment

Unnecessary default assignments, you can just declare and it will save gas and have the same meaning.

Code instances:

    MerkleDropFactory.sol (L#17) : uint public numTrees = 0; 
    MerkleEligibility.sol (L#31) : uint public numGates = 0; 
    VoterID.sol (L#69) : uint public numIdentities = 0; 
    MerkleVesting.sol (L#16) : uint public numTrees = 0; 
    MerkleResistor.sol (L#24) : uint public numTrees = 0; 

Rearrange state variables

You can change the order of the storage variables to decrease memory uses.

Code instance:

In VoterID.sol,rearranging the storage fields can optimize to: 5 slots from: 6 slots.
The new order of types (you choose the actual variables):
1. string
2. string
3. uint
4. address
5. bytes4
6. bytes4
7. bytes4
8. address
9. bytes4
10. bytes4

Short the following require messages

The following require messages are of length more than 32 and we think are short enough to short
them into exactly 32 characters such that it will be placed in one slot of memory and the require
function will cost less gas.
The list:

Code instances:

    Solidity file: MerkleDropFactory.sol, In line 90, Require message length to shorten: 35, The message: Provided merkle index doesn't exist
    Solidity file: PermissionlessBasicPoolFactory.sol, In line 315, Require message length to shorten: 34, The message: Only globalBeneficiary can set tax
    Solidity file: VoterID.sol, In line 239, Require message length to shorten: 35, The message: Identity: Not authorized to approve
    Solidity file: MerkleResistor.sol, In line 171, Require message length to shorten: 39, The message: You must initialize your account first.
    Solidity file: MerkleResistor.sol, In line 136, Require message length to shorten: 36, The message: Can only initialize your own tranche

Use unchecked to save gas for certain additive calculations that cannot overflow

You can use unchecked in the following calculations since there is no risk to overflow:

Code instances:

    MerkleResistor.sol (L#264) - uint startTime = block.timestamp + vestingTime - (totalCoins / coinsPerSecond); 
    MerkleResistor.sol (L#153) - tranches[destination][treeIndex] = Tranche( totalCoins, totalCoins, startTime, block.timestamp + vestingTime, coinsPerSecond, startTime );

Inline one time use functions

The following functions are used exactly once. Therefore you can inline them and save gas and improve code clearness.

Code instances:

    VoterID.sol, isContract
    VoterID.sol, checkOnERC721Received
    VoterID.sol, transfer

Do not cache msg.sender

We recommend not to cache msg.sender since calling it is 2 gas while reading a variable is more.

Code instance:

    https://github.com/code-423n4/2022-05-factorydao/tree/main/contracts/PermissionlessBasicPoolFactory.sol#L193
@code423n4 code423n4 added bug Something isn't working G (Gas Optimization) labels May 7, 2022
code423n4 added a commit that referenced this issue May 7, 2022
@illuzen
Copy link
Collaborator

illuzen commented May 10, 2022

msg.sender cache: valid
inline functions: valid
unchecked: duplicate
short messages: duplicate
rearrange state: valid
default assignment: duplicate
storage double reading: valid
all others duplicates

@illuzen illuzen closed this as completed May 12, 2022
@itsmetechjay itsmetechjay reopened this May 12, 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