State variables can be packed into fewer storage slots
If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper
Variable ordering with 3 slots instead of the current 4:
mapping(32):priceOracleOf, address(20):base, uint8(1):baseDecimals, address(20):registry
State variables should be cached in stack variables rather than re-reading them from storage
The instances below point to the second access of a state variable within a function. Caching will replace each Gwarmaccess (100 gas) with a much cheaper stack read.
Less obvious optimizations include having local storage variables of mappings within state variable mappings or mappings within state variable structs, having local storage variables of structs within mappings, or having local caches of state variable contracts/addresses.
<array>.length should not be looked up in every loop of a for-loop
Even memory arrays incur the overhead of bit tests and bit shifts to calculate the array length. Storage array length checks incur an extra Gwarmaccess (100 gas) PER-LOOP.
Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead
When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
Use custom errors rather than revert()/require() strings to save deployment gas
File: contracts/BaseIndex.sol (Various lines throughout the file)
File: contracts/ChainlinkPriceOracle.sol (Various lines throughout the file)
File: contracts/IndexLogic.sol (Various lines throughout the file)
File: contracts/libraries/FullMath.sol (Various lines throughout the file)
File: contracts/libraries/IndexLibrary.sol (Various lines throughout the file)
File: contracts/libraries/NAV.sol (Various lines throughout the file)
File: contracts/ManagedIndexReweightingLogic.sol (Various lines throughout the file)
File: contracts/ManagedIndex.sol (Various lines throughout the file)
File: contracts/PhuturePriceOracle.sol (Various lines throughout the file)
File: contracts/TopNMarketCapIndex.sol (Various lines throughout the file)
File: contracts/TopNMarketCapReweightingLogic.sol (Various lines throughout the file)
File: contracts/TrackedIndexReweightingLogic.sol (Various lines throughout the file)
File: contracts/TrackedIndex.sol (Various lines throughout the file)
File: contracts/UniswapV2PathPriceOracle.sol (Various lines throughout the file)
File: contracts/UniswapV2PriceOracle.sol (Various lines throughout the file)
File: contracts/vToken.sol (Various lines throughout the file)
Functions guaranteed to revert when called by normal users can be marked payable
If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.
State variables only set in the constructor should be declared
immutable
Avoids a Gsset (20000 gas)
State variables can be packed into fewer storage slots
If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper
Variable ordering with 3 slots instead of the current 4: mapping(32):priceOracleOf, address(20):base, uint8(1):baseDecimals, address(20):registry
State variables should be cached in stack variables rather than re-reading them from storage
The instances below point to the second access of a state variable within a function. Caching will replace each Gwarmaccess (100 gas) with a much cheaper stack read. Less obvious optimizations include having local storage variables of mappings within state variable mappings or mappings within state variable structs, having local storage variables of structs within mappings, or having local caches of state variable contracts/addresses.
asset
pointer for next iteration of the loop)asset
pointer for next iteration of the loop)Result of static calls should be cached in stack variables rather than re-calling storage-touching functions
Caching will replace each Gwarmaccess (100 gas) with a much cheaper stack read.
x = x + y
is cheaper thanx += y
<array>.length
should not be looked up in every loop of afor
-loopEven memory arrays incur the overhead of bit tests and bit shifts to calculate the array length. Storage array length checks incur an extra Gwarmaccess (100 gas) PER-LOOP.
++i
/i++
should beunchecked{++i}
/unchecked{++i}
when it is not possible for them to overflow, as is the case when used infor
- andwhile
-loopsrequire()
/revert()
strings longer than 32 bytes cost extra gasNot using the named return variables when a function returns, wastes deployment gas
Using
> 0
costs more gas than!= 0
when used on auint
in arequire()
statementIt costs more gas to initialize variables to zero than to let the default of zero be applied
++i
costs less gas than++i
, especially when it's used infor
-loops (--i
/i--
too)Splitting
require()
statements that use&&
saves gasSee this issue for an example
Usage of
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadhttps://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed
Expressions for constant values such as a call to
keccak256()
, should useimmutable
rather thanconstant
See this issue for a detail description of the issue
Duplicated
require()
/revert()
checks should be refactored to a modifier or functionrequire()
orrevert()
statements that check input arguments should be at the top of the functionChecks that involve constants should come before checks that involve state variables
Use custom errors rather than
revert()
/require()
strings to save deployment gasFunctions guaranteed to revert when called by normal users can be marked
payable
If a function modifier such as
onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function aspayable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.Use a more recent version of solidity
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value