Impact: Low
Likelihood: Low
payable
functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided.
A constructor
can safely be marked as payable
, since only the deployer would be able to pass funds, and the project itself would not pass any funds.
4
GitHub: 9
GitHub: 15
GitHub: 16
GitHub: 42
Impact: Low
Likelihood: Low
Mark data types as calldata
instead of memory
where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata
. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory
storage.
4
GitHub: 112
>=
/<=
costs less gas than operator >
/<
Impact: Low
Likelihood: Low
The compiler uses opcodes GT
and ISZERO
for code that uses >
, but only requires LT
for >=
. A similar behaviour applies for >
, which uses opcodes LT
and ISZERO
, but only requires GT
for <=
.
1
GitHub: 71
bool
s for storage incurs overheadImpact: Low
Likelihood: Low
Use uint256(1)
and uint256(2)
for true
/false
to avoid a Gwarmaccess (100 gas) for the extra SLOAD
, and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past
2
bytes32
rather than a string
Impact: Low
Likelihood: Low
Using the bytes types for fixed-length strings is more efficient than having the EVM have to incur the overhead of string processing. Consider whether the value needs to be a string. A good reason to keep it as a string would be if the variable is defined in an interface that this project does not own.
2
event
Impact: Low
Likelihood: Low
To efficiently emit events, it's possible to utilize assembly by making use of scratch space and the free memory pointer. This approach has the advantage of potentially avoiding the costs associated with memory expansion.
However, it's important to note that in order to safely optimize this process, it is preferable to cache and restore the free memory pointer.
A good example of such practice can be seen in Solady's codebase.
2
GitHub: 28
GitHub: 77
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadImpact: Low
Likelihood: Low
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.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Each operation involving a uint8
costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8
) as compared to ones involving uint256
, due to the compiler having to clear the higher bits of the memory word before operating on the uint8
, as well as the associated stack operations of doing so. Use a larger size then downcast where needed
2
Impact: Low
Likelihood: Low
State variables that are never re-assigned after their initial assignment should typically be declared with the constant
modifier. This ensures that these variables remain unmodified and communicates their unchanging nature. Using the constant
modifier also offers potential gas savings, as accessing these variables does not require any storage operations.
1
GitHub: 30
immutable
Impact: Low
Likelihood: Low
Avoids a Gsset(** 20000 gas**) in the constructor, and replaces the first access in each transaction(Gcoldsload - ** 2100 gas **) and each access thereafter(Gwarmacces - ** 100 gas ) with aPUSH32
( 3 gas **).
Whilestring
s are not value types, and therefore cannot beimmutable
/ constant
if not hard - coded outside of the constructor, the same behavior can be achieved by making the current contract abstract
with virtual
functions for thestring
accessors, and having a child contract override the functions with the hard - coded implementation - specific values.
1
Impact: Low
Likelihood: Low
When performing multiple operations on a state variable in a function, it is recommended to cache it first. Either multiple reads or multiple writes to a state variable can save gas by caching it on the stack. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses. Saves 100 gas per instance.
Mistakenly using public
for functions that should be labeled external
not only leads to increased gas costs but also enlarges the attack surface of a contract. This can expose the contract to unexpected behaviors and potential vulnerabilities. Moreover, from a code clarity perspective, incorrectly using public
can mislead other developers or auditors about the intended usage of the function, leading to potential misunderstandings or oversights. Ensuring the right visibility is chosen based on the function's usage can enhance security, efficiency, and maintainability of the smart contract.
4
revert
when called by normal users can be marked payable
Impact: Low
Likelihood: Low
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.
5
GitHub: 19
GitHub: 23
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.