Using > 0 costs more gas than != 0 when used on a uint in a require() statement
3
G-2
Splitting require() statements that use && saves gas
1
G-3
Use custom errors rather than revert()/require() strings to save deployment gas
55
G-4
Usage of assert() instead of require()
3
G-5
Expressions for constant values such as a call to keccak256(), should use immutable rather than constant
2
G-6
abi.encode() is less efficient than abi.encodePacked()
6
G-7
Reduce the size of error messages (Long revert Strings)
4
G-8
Use calldata instead of memory in external functions
2
G-9
Using bools for storage incurs overhead
4
G-10
Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead
8
G-11
Use a more recent version of solidity
23
G-12
Use Assembly to check for address(0)
23
G-13
Inline a modifier that’s only used once
5
Total instances found in this contest: 139 | Among all files in scope
1. Using > 0 costs more gas than != 0 when used on a uint in a require() statement
0 is less efficient than != 0 for unsigned integers (with proof)
!= 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas) Proof: While it may seem that > 0 is cheaper than !=, this is only true without the optimizer enabled and outside a require statement. If you enable the optimizer at 10k AND you’re in a require statement, this will save gas. You can see this tweet for more proof: https://twitter.com/gzeon/status/1485428085885640706
Breakdown each condition in a separate require statement (though require statements should be replaced with custom errors)
3. Custom Errors instead of Revert Strings to save Gas
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met). Starting from Solidity 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. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");),but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
contracts/governance/Governed.sol:41: require(_newGovernor != address(0), "Governor must be set");
Remediation:
I suggest replacing revert strings with custom errors.
4. Usage of assert() instead of require()
Between solc 0.4.10 and 0.8.0, require() used REVERT (0xfd) opcode which refunded the 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.
5. Expressions for constant values such as a call to keccak256(), should use immutable rather than constant
This results in the keccak operation being performed whenever the variable is used, increasing gas costs relative to just storing the output hash. Changing to immutable will only perform hashing on contract deployment which will save gas.
contracts/l2/gateway/L2GraphTokenGateway.sol:275: abi.encode(0, _data) // we don't need to track exitNums (b/c we have no fast exits) so we always use 0
// 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.
Refer Here 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
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.
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining
Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings
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
Gas Optimization
Total instances found in this contest: 139 | Among all files in scope
1. Using > 0 costs more gas than != 0 when used on a uint in a require() statement
Instances
L2GraphTokenGateway.sol:L146
L1GraphTokenGateway.sol:L201
L1GraphTokenGateway.sol:L217
Reference:
https://twitter.com/gzeon/status/1485428085885640706
Remediation:
I suggest changing > 0 with != 0. Also, please enable the Optimizer.
2. Splitting require() statements that use && saves gas
Require statements including conditions with the && operator can be broken down in multiple require statements to save gas.
Instances
L1GraphTokenGateway.sol:L142
Mitigation:
Breakdown each condition in a separate require statement (though require statements should be replaced with custom errors)
3. Custom Errors instead of Revert Strings to save Gas
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met). Starting from Solidity 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. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");),but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them. Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Instances
L2GraphTokenGateway.sol:L98
L2GraphTokenGateway.sol:L108
L2GraphTokenGateway.sol:L118
L2GraphTokenGateway.sol:L145
L2GraphTokenGateway.sol:L146
L2GraphTokenGateway.sol:L147
L2GraphTokenGateway.sol:L148
L2GraphTokenGateway.sol:L153
L2GraphTokenGateway.sol:L233
L2GraphTokenGateway.sol:L234
L2GraphToken.sol:L36
L2GraphToken.sol:L49
L2GraphToken.sol:L60
L2GraphToken.sol:L70
GraphTokenUpgradeable.sol:L60
GraphTokenUpgradeable.sol:L94
GraphTokenUpgradeable.sol:L95
GraphTokenUpgradeable.sol:L106
GraphTokenUpgradeable.sol:L115
GraphTokenUpgradeable.sol:L123
GraphProxy.sol:L105
GraphProxy.sol:L141
GraphProxy.sol:L157
GraphUpgradeable.sol:L24
GraphUpgradeable.sol:L32
GraphProxyStorage.sol:L62
L1GraphTokenGateway.sol:L74
L1GraphTokenGateway.sol:L78
L1GraphTokenGateway.sol:L82
L1GraphTokenGateway.sol:L110
L1GraphTokenGateway.sol:L111
L1GraphTokenGateway.sol:L122
L1GraphTokenGateway.sol:L132
L1GraphTokenGateway.sol:L142
L1GraphTokenGateway.sol:L153
L1GraphTokenGateway.sol:L154
L1GraphTokenGateway.sol:L165
L1GraphTokenGateway.sol:L166
L1GraphTokenGateway.sol:L200
L1GraphTokenGateway.sol:L201
L1GraphTokenGateway.sol:L202
L1GraphTokenGateway.sol:L217
L1GraphTokenGateway.sol:L224
L1GraphTokenGateway.sol:L271
L1GraphTokenGateway.sol:L275
GraphTokenGateway.sol:L31
GraphTokenGateway.sol:L40
Managed.sol:L44
Managed.sol:L45
Managed.sol:L49
Managed.sol:L53
Managed.sol:L57
Managed.sol:L104
Governed.sol:L24
Governed.sol:L41
Remediation:
I suggest replacing revert strings with custom errors.
4. Usage of assert() instead of require()
Between solc 0.4.10 and 0.8.0, require() used REVERT (0xfd) opcode which refunded the 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.
Instances:
GraphProxy.sol:L47
GraphProxy.sol:L48
GraphProxy.sol:L51
References:
https://code4rena.com/reports/2022-05-bunker/#g-08-usage-of-assert-instead-of-require
5. Expressions for constant values such as a call to keccak256(), should use immutable rather than constant
This results in the keccak operation being performed whenever the variable is used, increasing gas costs relative to just storing the output hash. Changing to immutable will only perform hashing on contract deployment which will save gas.
Instances:
GraphTokenUpgradeable.sol:L38
GraphTokenUpgradeable.sol:L39
References:
https://github.com/code-423n4/2021-10-slingshot-findings/issues/3
6. abi.encode() is less efficient than abi.encodepacked()
Use abi.encodepacked() instead of abi.encode()
Instances:
L2GraphTokenGateway.sol:L174
L2GraphTokenGateway.sol:L275
GraphTokenUpgradeable.sol:L88
GraphTokenUpgradeable.sol:L162
L1GraphTokenGateway.sol:L249
L1GraphTokenGateway.sol:L342
Reference:
https://code4rena.com/reports/2022-06-notional-coop#15-abiencode-is-less-efficient-than-abiencodepacked
7. Reduce the size of error messages (Long revert Strings)
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
Instances:
GraphProxy.sol:L105
GraphProxy.sol:L141
GraphUpgradeable.sol:L32
Managed.sol:L53
Recommendations:
I suggest shortening the revert strings to fit in 32 bytes, or using custom errors as described next.
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Source Custom Errors in Solidity:
8. Use calldata instead of memory in external functions
Use calldata instead of memory in external functions to save gas.
Instances:
L2GraphTokenGateway.sol:L193
L1GraphTokenGateway.sol:L198
Reference:
https://code4rena.com/reports/2022-04-badger-citadel/#g-12-stakedcitadeldepositall-use-calldata-instead-of-memory
9. Using bools for storage incurs overhead
Refer Here 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
Instances:
GraphTokenUpgradeable.sol:L51
L1GraphTokenGateway.sol:L35
Pausable.sol:L8
Pausable.sol:L10
References:
https://code4rena.com/reports/2022-06-notional-coop#8-using-bools-for-storage-incurs-overhead
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.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed
Instances:
IStaking.sol:L47
IStaking.sol:L48
IStaking.sol:L49
IStakingData.sol:L37
IStakingData.sol:L38
IStakingData.sol:L39
GraphTokenUpgradeable.sol:L79
IGraphToken.sol:L33
References:
https://code4rena.com/reports/2022-04-phuture#g-14-usage-of-uintsints-smaller-than-32-bytes-256-bits-incurs-overhead
11. Use a more recent version of solidity
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings 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
Instances:
ICuration.sol:L3
IGraphCurationToken.sol:L3
IStaking.sol:L3
IStakingData.sol:L3
L2GraphTokenGateway.sol:L3
L2GraphToken.sol:L3
GraphTokenUpgradeable.sol:L3
IGraphProxy.sol:L3
GraphProxyAdmin.sol:L3
GraphProxy.sol:L3
GraphUpgradeable.sol:L3
GraphProxyStorage.sol:L3
L1GraphTokenGateway.sol:L3
ICallhookReceiver.sol:L9
BridgeEscrow.sol:L3
GraphTokenGateway.sol:L3
IEpochManager.sol:L3
IRewardsManager.sol:L3
Pausable.sol:L3
Managed.sol:L3
Governed.sol:L3
IController.sol:L3
IGraphToken.sol:L3
References:
https://code4rena.com/reports/2022-06-notional-coop/#10-use-a-more-recent-version-of-solidity
12. Use assembly to check for address(0)
Saves 6 gas per instance if using assembly to check for address(0)
e.g.
Instances
L2GraphTokenGateway.sol:L98
L2GraphTokenGateway.sol:L108
L2GraphTokenGateway.sol:L118
L2GraphTokenGateway.sol:L148
L2GraphToken.sol:L49
L2GraphToken.sol:L60
L2GraphToken.sol:L70
GraphTokenUpgradeable.sol:L106
GraphProxy.sol:L105
GraphProxy.sol:L143
L1GraphTokenGateway.sol:L74
L1GraphTokenGateway.sol:L110
L1GraphTokenGateway.sol:L111
L1GraphTokenGateway.sol:L122
L1GraphTokenGateway.sol:L132
L1GraphTokenGateway.sol:L142
L1GraphTokenGateway.sol:L153
L1GraphTokenGateway.sol:L165
L1GraphTokenGateway.sol:L202
GraphTokenGateway.sol:L31
Managed.sol:L104
Governed.sol:L41
Governed.sol:L55
13. Inline a modifier that’s only used once
Description
As notDelegated() is only used once in this contract (in function proxiableUUID()), it should get inlined to save gas:
Instances 1
GraphTokenUpgradeable.sol:L59
Instances where modifiers is used only once
GraphTokenUpgradeable.sol:L132
Instances 2
L2GraphTokenGateway.sol:L68
Instances where modifiers is used only once
L2GraphTokenGateway.sol:L232
Instances 3
Managed.sol:L71
Instances where modifiers is used only once
Managed.sol:L95
Instances 4
L1GraphTokenGateway.sol:L73
Instances where modifiers is used only once
L1GraphTokenGateway.sol:L269
Instances 5
GraphTokenGateway.sol:L18
Instances where modifiers is used only once
GraphTokenGateway.sol:L47