Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible, some gas can be saved by using an unchecked block. For example:
for (uint256 i = 0; i < selectorsLength; ++i) {
bytes4 selector = _selectors[i];
SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet
_removeOneFunction(oldFacet.facetAddress, selector);
}
It is cheaper to use pre-increment (++i
) than using shorthand addition/subtraction assignment operators (+=
, -=
)
for example:
Line 29: s.diamondCutStorage.currentProposalId += 1;
Could be refactored to
Line 29: ++s.diamondCutStorage.currentProposalId;
A function with access control marked as payable will be cheaper for legitimate callers: the compiler removes checks for msg.value
, saving approximately 20
gas per function call.
For example: Line 46 Line 78 Line 225
The EVM only operates on 32 bytes/ 256 bits at a time. This means that if you use uint8, EVM has to first convert it uint256 to work on it and the conversion costs extra gas! You may wonder, What were the devs thinking? Why did they create smaller variables then? The answer lies in packing. In solidity, you can pack multiple small variables into one slot, but if you are defining a lone variable and can’t pack it, it’s optimal to use a uint256 rather than uint8.
When a function with a memory
array is called externally, the abi.decode()
step has to use a for-loop to copy each index of the calldata
to the memory
index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata
directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory
arguments, it’s still valid for implementation contracts to use calldata
arguments instead.
Suppose the array is passed to an internal
function which passes the array to another internal function where the array is modified and therefore memory is used in the external
call. In that case, it’s still more gas-efficient to use calldata
when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one
For example:
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