Closed code423n4 closed 2 years ago
This report includes a lot of invalid examples - some outright invalid recommendations, others include out of scope files including test mocks. Please spot check your report when generating these. Spammy lists like this are not worth the time to sort through just in case a valid finding is buried in there somewhere.
siding with sponsor here. it's a waste of both our times.
Foundation Drop Contest Gas Optimization Report
Summary
The following gas optimization issues were found during the code audit:
calldata
instead ofmemory
(21 instances)<array>.length
(9 instances)unchecked{}
to suppress overflow/underflow check (13 instances)require()
/revert()
string (44 instances)bool
s for storage incurs overhead (5 instances)!= 0
instead of> 0
when comparing uint (5 instances)abi.encodePacked()
instead ofabi.encode()
(2 instances)private
instead ofpublic
for constants (6 instances)revert()
/require()
strings (52 instances)Total 231 instances of 15 issues.
Use
calldata
instead ofmemory
(21 instances)When a function with a
memory
array is called externally, theabi.decode()
step has to use a for loop to copy each index of thecalldata
to thememory
index. This overhead can be optimized by usingcalldata
directly.Cache
<array>.length
(9 instances)If
<array>.length
is used as for loop termination condition, then the.length
method will be called in each iteration. Caching it in a local variable can save gas.Use
unchecked{}
to suppress overflow/underflow check (13 instances)Starting from version 0.8.0, Solidity does overflow/underflow checks by default. It is a good feature to prevent vulnerabilities but it has a significant overhead, especially when used in for loop. When using uint256/int256, it is extremely hard to trigger overflow, so it makes sense to skip these checks. To suppress the overflow/underflow checks, use
unchecked {}
. For increment situations, just useunchecked {}
directly; for decrement situations, add arequire()
statement before decrementing to prevent underflow.Long
require()
/revert()
string (44 instances)Shortening revert strings to fit in 32 bytes will decrease gas costs for deployment and gas costs when the revert condition has been met.
Using
bool
s for storage incurs overhead (5 instances)Use
uint256(1)
anduint256(2)
for true/false. Booleans are more expensive than uint256 or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back. This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled.Use
!= 0
instead of> 0
when comparing uint (5 instances)When dealing with unsigned integer types, comparisons with
!= 0
are cheaper then with> 0
.Empty blocks should be removed (5 instances)
Empty blocks exist in the code. 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.
Don't initialize variables with default value (8 instances)
Uninitialized variables are assigned with the types default value. Explicitly initializing a variable with it's default value costs unnecesary gas.
Use uint256/int256 instead of other variations (43 instances)
Using smaller data types such as uint8/int8 is more expensive than using uint256/int256. The EVM works with 256bit/32byte words. Every operation is based on these base units. If the data is smaller, further operations are needed to downscale from 256 bits to 8 bits, and this is more expensive than using uint256/int256.
Use
abi.encodePacked()
instead ofabi.encode()
(2 instances)abi.encodePacked()
is more efficient thanabi.encode()
.Use
private
instead ofpublic
for constants (6 instances)If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table.
Do not use SafeMath for Solidity >= 0.8.0 (1 instances)
Solidity v0.8.0 introduces built-in overflow checks, so using
SafeMath
is a waste of gas.Use custom errors instead of
revert()
/require()
strings (52 instances)Using
require()
/revert()
strings is expensive. Starting from Soldity 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.Custom errors decrease both deploy and runtime gas costs. Note that runtime gas cost is only relevant when the revert condition is met.
Use shift right/left instead of division/multiplication if possible (1 instance)
A division/multiplication by any number
x
being a power of 2 can be calculated by shiftinglog2(x)
to the right/left. While theDIV
opcode uses 5 gas, theSHR
opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.Not using the named return variables when a function returns, wastes deployment gas (16 instances)
If a named return variable is declared when declaring the function, don't add return statement.