You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate
2
2
State variables only set in the constructor should be declared immutable
1
3
Using calldata instead of memory for read-only arguments in external functions saves gas
1
4
Use EIP-1167 minimal proxies for 10x cheaper contract instantiation
1
5
State variables should be cached in stack variables rather than re-reading them from storage
9
6
<array>.length should not be looked up in every loop of a for-loop
1
7
Using bools for storage incurs overhead
1
8
It costs more gas to initialize non-constant/non-immutable variables to zero than to let the default of zero be applied
1
9
++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)
3
10
Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead
31
11
Using private rather than public for constants, saves gas
1
12
Use custom errors rather than revert()/require() strings to save gas
2
Total: 54 instances over 12 issues
Gas Optimizations
1. Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate
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. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
There are 2 instances of this issue:
File: lender/Lender.sol #143mapping(address=>uint256) public fees;
44/// @notice maps a token address to a point in time, a hold, after which a withdrawal can be made45: mapping (address=>uint256) public withdrawals;
File: marketplace/MarketPlace.sol #235mapping(address=>mapping(uint256=>address[9])) public markets;
3637/// @notice pools map markets to their respective YieldSpace pools for the MetaPrincipal token38: mapping(address=>mapping(uint256=>address)) public pools;
3. Using calldata instead of memory for read-only arguments in external functions saves gas
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.
If 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, it's still more gass-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
4. Use EIP-1167 minimal proxies for 10x cheaper contract instantiation
When new contracts have to be instantiated frequently, it's much cheaper for it to be done via minimal proxies. The only downside is that they rely on delegatecall() calls for every function, which adds an overhead of ~800 gas, but this is multiple orders of manitude less than the amount saved during deployment
There is 1 instance of this issue:
File: marketplace/MarketPlace.sol #180: address iToken =address(newERC5095(u, m, redeemer, lender, n, s, d));
5. 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 of a state variable replace 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.
There are 9 instances of this issue:
File: lender/Lender.sol
/// @audit marketPlace on line 146147: revertExists(marketPlace);
/// @audit feenominator on line 681681: return feenominator >0? a / feenominator : 0;
File: redeemer/Redeemer.sol
/// @audit marketPlace on line 164187: IElementToken(principal).withdrawPrincipal(amount, marketPlace);
/// @audit marketPlace on line 283281: ) publicauthorized(IMarketPlace(marketPlace).markets(u, m, 0)) returns (bool) {
/// @audit lender on line 128134: uint256 amount =IERC20(principal).balanceOf(lender);
/// @audit lender on line 134136: Safe.transferFrom(IERC20(u), lender, address(this), amount);
/// @audit lender on line 177180: Safe.transferFrom(IERC20(principal), lender, address(this), amount);
/// @audit lender on line 221224: Safe.transferFrom(token, lender, address(this), amount);
/// @audit lender on line 256259: Safe.transferFrom(token, lender, address(this), amount);
// 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.
10. 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.
11. Using private rather than public for constants, saves gas
If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
There is 1 instance of this issue:
File: lender/Lender.sol #123: uint256constant public HOLD =3days;
Summary
Gas Optimizations
address
mappings can be combined into a singlemapping
of anaddress
to astruct
, where appropriateimmutable
calldata
instead ofmemory
for read-only arguments inexternal
functions saves gas<array>.length
should not be looked up in every loop of afor
-loopbool
s for storage incurs overheadconstant
/non-immutable
variables to zero than to let the default of zero be applied++i
costs less gas thani++
, especially when it's used infor
-loops (--i
/i--
too)uints
/ints
smaller than 32 bytes (256 bits) incurs overheadprivate
rather thanpublic
for constants, saves gasrevert()
/require()
strings to save gasTotal: 54 instances over 12 issues
Gas Optimizations
1. Multiple
address
mappings can be combined into a singlemapping
of anaddress
to astruct
, where appropriateSaves 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. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
There are 2 instances of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L43-L45
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/marketplace/MarketPlace.sol#L35-L38
2. State variables only set in the constructor should be declared
immutable
Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmacces (100 gas) with a
PUSH32
(3 gas).There is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/redeemer/Redeemer.sol#L33
3. Using
calldata
instead ofmemory
for read-only arguments inexternal
functions saves gasWhen 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. Each iteration of this for-loop costs at least 60 gas (i.e.60 * <mem_array>.length
). Usingcalldata
directly, obliviates the need for such a loop in the contract code and runtime execution.If the array is passed to an
internal
function which passes the array to another internal function where the array is modified and thereforememory
is used in theexternal
call, it's still more gass-efficient to usecalldata
when theexternal
function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length oneThere is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/marketplace/MarketPlace.sol#L70
4. Use EIP-1167 minimal proxies for 10x cheaper contract instantiation
When new contracts have to be instantiated frequently, it's much cheaper for it to be done via minimal proxies. The only downside is that they rely on
delegatecall()
calls for every function, which adds an overhead of ~800 gas, but this is multiple orders of manitude less than the amount saved during deploymentThere is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/marketplace/MarketPlace.sol#L80
5. 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 of a state variable replace 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.
There are 9 instances of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L147
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/redeemer/Redeemer.sol#L187
6.
<array>.length
should not be looked up in every loop of afor
-loopThe overheads outlined below are PER LOOP, excluding the first loop
MLOAD
(3 gas)CALLDATALOAD
(3 gas)Caching the length changes each of these to a
DUP<N>
(3 gas), and gets rid of the extraDUP<N>
needed to store the stack offsetThere is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L265
7. Using
bool
s for storage incurs overheadhttps://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27
Use
uint256(1)
anduint256(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 pastThere is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L30
8. It costs more gas to initialize non-
constant
/non-immutable
variables to zero than to let the default of zero be appliedNot overwriting the default for stack variables saves 8 gas. Storage and memory variables have larger savings
There is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L265
9.
++i
costs less gas thani++
, especially when it's used infor
-loops (--i
/i--
too)Saves 5 gas per loop
There are 3 instances of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L96
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L120
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L289
10. 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
There are 31 instances of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L48
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/marketplace/MarketPlace.sol#L73
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/redeemer/Redeemer.sol#L36
11. Using
private
rather thanpublic
for constants, saves gasIf needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
There is 1 instance of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L23
12. Use custom errors rather than
revert()
/require()
strings to save gasCustom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hitby avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
There are 2 instances of this issue:
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L710
https://github.com/code-423n4/2022-06-illuminate/blob/912be2a90ded4a557f121fe565d12ec48d0c4684/lender/Lender.sol#L712
The text was updated successfully, but these errors were encountered: