Gas Optimizations #9
Labels
bug
Something isn't working
G (Gas Optimization)
grade-c
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
[G01] State variables only set in the constructor should be declared
immutable
Impact
Avoids a Gsset (20000 gas)
Findings:
[G02] State variables can be packed into fewer storage slots
Impact
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
Findings:
[G03]
<array>.length
should not be looked up in every loop of afor
loopImpact
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.
Findings:
[G04]
++i/i++
should beunchecked{++i}
/unchecked{++i}
when it is not possible for them to overflow, as is the case when used infor
andwhile
loopsFindings:
[G05] Using
> 0
costs more gas than!= 0
when used on auint
in arequire()
statementFindings:
[G06] It costs more gas to initialize variables to zero than to let the default of zero be applied
Findings:
[G07] Splitting
require()
statements that use&&
Cost gasImpact
See this issue for an example
Findings:
[G08] Expressions for constant values such as a call to
keccak256()
, should useimmutable
rather thanconstant
Impact
See this issue for a detailed description of the issue
Findings:
[G09]
require()
orrevert()
statements that check input arguments should be at the top of the functionImpact
Checks that involve constants should come before checks that involve state variables
Findings:
[G10] Functions guaranteed to revert when called by normal users can be marked
payable
Impact
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.Findings:
[G11] Use a more recent version of solidity
Impact
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
Findings:
[G12] Use
calldata
instead ofmemory
for function parametersImpact
Use calldata instead of memory for function parameters. Having function arguments use calldata instead of memory can save gas.
Findings:
[G13] Multiple
address
mappings can be combined into a singlemapping
of anaddress
to astruct
, where appropriateImpact
Saves a storage slot for the mapping. Depending on the circumstances
and sizes of types, can avoid a Gsset (20000 gas) per mapping combined.
Reads and subsequent writes can also be cheaper when a function requires
both values and they both fit in the same storage slot
Findings:
[G14] Using
bools
for storage incurs overheadFindings:
[G15] Usage of
assert()
instead ofrequire()
Impact
Between solc 0.4.10 and 0.8.0, require() used REVERT (0xfd) opcode which refunded remaining gas on failure while assert() used INVALID (0xfe) opcode which consumed all the supplied gas. (see https://docs.soliditylang.org/en/v0.8.1/control-structures.html#error-handling-assert-require-revert-and-exceptions).require() should be used for checking error conditions on inputs and return values while assert() should be used for invariant checking (properly functioning code should never reach a failing assert statement, unless there is a bug in your contract you should fix).
From the current usage of assert, my guess is that they can be replaced with require, unless a Panic really is intended.
Findings:
[G16] Empty blocks should be removed or emit something
Impact
The code should be refactored such that they no longer exist, or the
block should do something useful, such as emitting an event or
reverting. If the contract is meant to be extended, the contract should
be abstract and the function signatures be added without
any default implementation. If the block is an empty if-statement block
to avoid doing subsequent checks in the else-if/else conditions, the
else-if/else conditions should be nested under the negation of the
if-statement, because they involve different classes of checks, which
may lead to the introduction of errors when the code is later modified (if(x){}else if(y){...}else{...} => if(!x){if(y){...}else{...}})
Findings:
[G17]
abi.encode()
is less efficient than abi.encodePacked()Findings:
[G18] Optimize names to save gas
Impact
public/external function names and public member variable names can be optimized to save gas. See this
link for an example of how it works. Below are the interfaces/abstract
contracts that can be optimized so that the most frequently-called
functions use the least amount of gas possible during method lookup.
Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
Findings:
The text was updated successfully, but these errors were encountered: