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
It is expected that the value should be converted into a constant value at compile time. But the expression is re-calculated each time the constant is referenced.
consequences:
each usage of a "constant" costs ~100gas more on each access (it is still a little better than storing the result in storage, but not much..)
since these are not real constants, they can't be referenced from a real constant environment (e.g. from assembly, or from another library )
Suggestion:
Change these expressions from constant to immutable and implement the calculation in the constructor or hardcode these values in the constants and add a comment to say how the value was calculated.
Use custom errors rather than revert()/require() strings
Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas.
// gateway/L1GraphTokenGateway.sol142: require(_escrow !=address(0) && Address.isContract(_escrow), "INVALID_ESCROW");
// governance/Governed.sol54-57:
require(
pendingGovernor !=address(0) &&msg.sender== pendingGovernor,
"Caller must be pending governor"
);
// upgrades/GraphProxy.sol142-145:
require(
_pendingImplementation !=address(0) &&msg.sender== _pendingImplementation,
"Caller must be the pending implementation"
);
require() strings longer than 32 bytes cost extra gas
Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas.
Instances number of this issue: 6
// gateway/GraphTokenGateway.sol19-22:
require(
msg.sender== controller.getGovernor() ||msg.sender== pauseGuardian,
"Only Governor or Guardian can call"
);
// governance/Managed.sol53: require(msg.sender== controller.getGovernor(), "Caller must be Controller governor");
// upgrades/GraphProxy.sol105: require(_newAdmin !=address(0), "Cannot change the admin of a proxy to the zero address");
141: require(Address.isContract(_pendingImplementation), "Implementation must be a contract");
142-144:
require(
_pendingImplementation !=address(0) &&msg.sender== _pendingImplementation,
"Caller must be the pending implementation"//upgrades/GraphUpgradeable.sol32: require(msg.sender==_implementation(), "Caller must be the implementation");
Making some variables as non-public
Changing the visibility from public to private or internal can save gas when a variable isn’t used outside of its contract.
Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table.
Instances number of this issue: 1
The followings can be changed from public to internal or private:
Update value order can be adjusted to simplify the code and save gas
For example, to update the num variable with newVal, the current way is as following:
uint oldVal = num;
num = newVal;
emitupdate(oldVal, newVal);
If the execution order is adjusted, some operations can be saved (memory space allocation, variable assignment), reducing both the deployment and run time gas cost.
There are multiple places can use this trick for optimization, since the updates of parameters are widely and frequently used, the optimization can be beneficial.
// 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.
Functions guaranteed to revert when called by normal users can be marked payable
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.
Expressions for constant values such as a call to
keccak256()
,` should use immutable rather than constantConstant expressions are left as expressions, not constants.
Instances number of this issue:
When a constant declared as: 4
It is expected that the value should be converted into a constant value at compile time. But the expression is re-calculated each time the constant is referenced.
consequences:
Suggestion:
Change these expressions from constant to immutable and implement the calculation in the constructor or hardcode these values in the constants and add a comment to say how the value was calculated.
reference:
ethereum/solidity#9232
Use custom errors rather than
revert()/require()
stringsCustom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas.
The demo of the gas comparison can be seen here.
Duplicated require()/assert() checks could be refactored to a modifier or function
Instances number of this issue: 21
Splitting
require()
statements that use &&REQUIRE()
statements with multiple conditions can be split.See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper.
The demo of the gas comparison can be seen here.
Instances number of this issue: 3
require()
strings longer than 32 bytes cost extra gasEach extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas.
Instances number of this issue: 6
Making some variables as non-public
Changing the visibility from public to private or internal can save gas when a variable isn’t used outside of its contract.
Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table.
Instances number of this issue: 1
The followings can be changed from public to internal or private:
Update value order can be adjusted to simplify the code and save gas
For example, to update the
num
variable withnewVal
, the current way is as following:If the execution order is adjusted, some operations can be saved (memory space allocation, variable assignment), reducing both the deployment and run time gas cost.
The demo of the gas comparison can be seen here.
There are multiple places can use this trick for optimization, since the updates of parameters are widely and frequently used, the optimization can be beneficial.
can be changed to
Using
bool
for storage incurs overheadhttps://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 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 number of this issue:
Functions guaranteed to revert when called by normal users can be marked payable
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.The extra opcodes avoided are
which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost.
Instances number of this issue:
The text was updated successfully, but these errors were encountered: