When certain state variable is read more than once, cache it to local variable to save gas.
Defined Variables Used Only Once
Issue
Certain variables is defined even though they are used only once.
Remove these unnecessary variables to save gas.
For cases where it will reduce the readability, one can use comments to help describe
what the code is doing.
I recommend making duplicate require statement into modifier or a function.
Use require instead of &&
Issue
When there are multiple conditions in require statement, break down the require statement into
multiple require statements instead of using && can save gas.
PoC
Total of 3 instances found.
./Governed.sol:54: require(
pendingGovernor != address(0) && msg.sender == pendingGovernor,
"Caller must be pending governor"
);
./GraphProxy.sol:142: require(
_pendingImplementation != address(0) && msg.sender == _pendingImplementation,
"Caller must be the pending implementation"
);
./L1GraphTokenGateway.sol:142: require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW");
Mitigation
Break down into several require statement.
Internal Function Called Only Once Can be Inlined
Issue
Certain function is defined even though it is called only once.
Inline it instead to where it is called to avoid usage of extra gas.
Using Elements Smaller than 32 bytes (256 bits) Might Use More Gas
Issue
Since EVM operates on 32 bytes at a time, if the element is smaller than that, the EVM must use more operations
in order to reduce the elements from 32 bytes to specified size. Therefore it is more gas saving to use 32 bytes
unless it is used in a struct or state variable in order to reduce storage slot.
Keep The Revert Strings of Error Messages within 32 Bytes
Issue
Since each storage slot is size of 32 bytes, error messages that is longer than this will need
extra storage slot leading to more gas cost.
PoC
Total of 6 instances found.
./GraphProxy.sol:105: require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
./GraphProxy.sol:141: require(Address.isContract(_pendingImplementation), "Implementation must be a contract");
./GraphProxy.sol:142: require(
_pendingImplementation != address(0) && msg.sender == _pendingImplementation,
"Caller must be the pending implementation"
);
./GraphUpgradeable.sol:32: require(msg.sender == _implementation(), "Caller must be the implementation");
./GraphTokenGateway.sol:19: require(
msg.sender == controller.getGovernor() || msg.sender == pauseGuardian,
"Only Governor or Guardian can call"
);
./Managed.sol:53: require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");
Mitigation
Simply keep the error messages within 32 bytes to avoid extra storage slot cost.
Table of Contents
Total of 11 issues found.
Minimize the Number of SLOADs by Caching State Variable
Issue
SLOADs cost 100 gas where MLOADs/MSTOREs cost only 3 gas. Whenever function reads storage value more than once, it should be cached to save gas.
PoC
Total of 3 instances found.
Cache
l1GRT
of outboundTransfer() of L2GraphTokenGateway.sol 2 SLOADs to 1 SLOAD, 1 MSTORE and 2 MLOAD https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/l2/gateway/L2GraphTokenGateway.sol#L145 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/l2/gateway/L2GraphTokenGateway.sol#L156Cache
l1GRT
of finalizeInboundTransfer() of L2GraphTokenGateway.sol 2 SLOADs to 1 SLOAD, 1 MSTORE and 2 MLOAD https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/l2/gateway/L2GraphTokenGateway.sol#L233 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/l2/gateway/L2GraphTokenGateway.sol#L236Cache
escrow
of finalizeInboundTransfer() of L1GraphTokenGateway.sol 2 SLOADs to 1 SLOAD, 1 MSTORE and 2 MLOAD https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/gateway/L1GraphTokenGateway.sol#L273 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/gateway/L1GraphTokenGateway.sol#L276Mitigation
When certain state variable is read more than once, cache it to local variable to save gas.
Defined Variables Used Only Once
Issue
Certain variables is defined even though they are used only once. Remove these unnecessary variables to save gas. For cases where it will reduce the readability, one can use comments to help describe what the code is doing.
PoC
Total of 1 instance found.
escrowBalance
variable of finalizeInboundTransfer function of L1GraphTokenGateway.sol https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/gateway/L1GraphTokenGateway.sol#L273-L275 Mitigation: Delete line 273 and replace line 275 with below codeMitigation
Don't define variable that is used only once. Details are listed on above PoC.
Redundant Use of Variable
Issue
Below has redundant use of variables. Instead of defining a new variable, emit the event first and then update the variable.
PoC
Total of 4 instances found.
Do not define
oldPendingGovernor
of transferOwnership function of Governed.sol https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Governed.sol#L40-L47 Change it toDo not define
oldGovernor
andoldPendingGovernor
of acceptOwnership function of Governed.sol https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Governed.sol#L53-L67 Change it toDo not define
oldPauseGuardian
of _setPauseGuardian function of Pausable.sol https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Pausable.sol#L55-L59 Change it toMitigation
Instead of defining a new variable, emit the event first and then update the variable like shown in above PoC.
Duplicate require() Checks Should be a Modifier or a Function
Issue
Since below require checks are used more than once, I recommend making these to a modifier or a function.
PoC
Total of 5 instances found.
Mitigation
I recommend making duplicate require statement into modifier or a function.
Use require instead of &&
Issue
When there are multiple conditions in require statement, break down the require statement into multiple require statements instead of using && can save gas.
PoC
Total of 3 instances found.
Mitigation
Break down into several require statement.
Internal Function Called Only Once Can be Inlined
Issue
Certain function is defined even though it is called only once. Inline it instead to where it is called to avoid usage of extra gas.
PoC
Total of 3 instances found.
_notPartialPaused() of Managed.sol _getCurrentDelegated function called only once at line 61 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Managed.sol#L43 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Managed.sol#L61
_onlyGovernor() of Managed.sol _getCurrentDelegated function called only once at line 78 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Managed.sol#L52 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Managed.sol#L78
_onlyController() of Managed.sol _getCurrentDelegated function called only once at line 72 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Managed.sol#L56 https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/governance/Managed.sol#L72
Mitigation
I recommend to not define above functions and instead inline it at place it is called.
Boolean Comparisons
Issue
It is more gas expensive to compare boolean with "variable == true" or "variable == false" than directly checking the returned boolean value.
PoC
Total of 1 instance found.
Mitigation
Simply check by returned boolean value. Change it to
Using Elements Smaller than 32 bytes (256 bits) Might Use More Gas
Issue
Since EVM operates on 32 bytes at a time, if the element is smaller than that, the EVM must use more operations in order to reduce the elements from 32 bytes to specified size. Therefore it is more gas saving to use 32 bytes unless it is used in a struct or state variable in order to reduce storage slot.
Reference: https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html
PoC
Total of 18 instances found.
Mitigation
I suggest using uint256 instead of anything smaller and downcast where needed.
!= 0 costs less gas than > 0
Issue
!= 0 costs less gas when optimizer is enabled and is used for unsigned integers in require statement.
PoC
Total of 3 instances found.
Mitigation
I suggest changing > 0 to != 0
Keep The Revert Strings of Error Messages within 32 Bytes
Issue
Since each storage slot is size of 32 bytes, error messages that is longer than this will need extra storage slot leading to more gas cost.
PoC
Total of 6 instances found.
Mitigation
Simply keep the error messages within 32 bytes to avoid extra storage slot cost.
Use Custom Errors to Save Gas
Issue
Custom errors from Solidity 0.8.4 are cheaper than revert strings. Details are explained here: https://blog.soliditylang.org/2021/04/21/custom-errors/
PoC
Total of 64 instances found.
Mitigation
I suggest implementing custom errors to save gas.