Strict inequalities (>) are more expensive than non-strict ones (>=). This is due to some supplementary checks (ISZERO, 3 gas). This also holds true between <= and <.
Consider replacing strict inequalities with non-strict ones to save some gas here:
3. Splitting require() statements that use && saves gas
If you're using the Optimizer at 200, instead of using the && operator in a single require statement to check multiple conditions, Consider using multiple require statements with 1 condition per require statement:
Please, note that this might not hold true at a higher number of runs for the Optimizer (10k). However, it indeed is true at 200.
4. Using private rather than public for constants saves gas
If needed, the value can be read from the verified contract source code. 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
token/InfinityToken.sol:25: bytes32 public constant EPOCH_INFLATION = keccak256('Inflation');
token/InfinityToken.sol:26: bytes32 public constant EPOCH_DURATION = keccak256('EpochDuration');
token/InfinityToken.sol:27: bytes32 public constant EPOCH_CLIFF = keccak256('Cliff');
token/InfinityToken.sol:28: bytes32 public constant MAX_EPOCHS = keccak256('MaxEpochs');
token/TimelockConfig.sol:9: bytes32 public constant ADMIN = keccak256('Admin');
token/TimelockConfig.sol:10: bytes32 public constant TIMELOCK = keccak256('Timelock');
5. It costs more gas to initialize variables with their default value than letting the default value be applied
If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for address...). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
As an example: for (uint256 i = 0; i < numIterations; ++i) { should be replaced with for (uint256 i; i < numIterations; ++i) {
Affected code:
core/InfinityExchange.sol:148: for (uint256 i = 0; i < numMakerOrders; ) {
core/InfinityExchange.sol:200: for (uint256 i = 0; i < ordersLength; ) {
core/InfinityExchange.sol:219: for (uint256 i = 0; i < ordersLength; ) {
core/InfinityExchange.sol:272: for (uint256 i = 0; i < numSells; ) {
core/InfinityExchange.sol:308: for (uint256 i = 0; i < numMakerOrders; ) {
core/InfinityExchange.sol:349: for (uint256 i = 0; i < ordersLength; ) {
core/InfinityExchange.sol:393: for (uint256 i = 0; i < numNonces; ) {
core/InfinityExchange.sol:1048: for (uint256 i = 0; i < numNfts; ) {
core/InfinityExchange.sol:1086: for (uint256 i = 0; i < numTokens; ) {
core/InfinityExchange.sol:1109: for (uint256 i = 0; i < numNfts; ) {
core/InfinityExchange.sol:1190: for (uint256 i = 0; i < numNfts; ) {
core/InfinityExchange.sol:1206: for (uint256 i = 0; i < numTokens; ) {
core/InfinityOrderBookComplication.sol:42: bool _isPriceValid = false;
core/InfinityOrderBookComplication.sol:76: for (uint256 i = 0; i < ordersLength; ) {
core/InfinityOrderBookComplication.sol:82: for (uint256 j = 0; j < nftsLength; ) {
core/InfinityOrderBookComplication.sol:108: bool _isPriceValid = false;
core/InfinityOrderBookComplication.sol:197: uint256 numConstructedItems = 0;
core/InfinityOrderBookComplication.sol:199: for (uint256 i = 0; i < nftsLength; ) {
core/InfinityOrderBookComplication.sol:214: uint256 numTakerItems = 0;
core/InfinityOrderBookComplication.sol:216: for (uint256 i = 0; i < nftsLength; ) {
core/InfinityOrderBookComplication.sol:244: uint256 numCollsMatched = 0;
core/InfinityOrderBookComplication.sol:246: for (uint256 i = 0; i < order2NftsLength; ) {
core/InfinityOrderBookComplication.sol:247: for (uint256 j = 0; j < order1NftsLength; ) {
core/InfinityOrderBookComplication.sol:289: uint256 numTokenIdsPerCollMatched = 0;
core/InfinityOrderBookComplication.sol:290: for (uint256 k = 0; k < item2TokensLength; ) {
core/InfinityOrderBookComplication.sol:291: for (uint256 l = 0; l < item1TokensLength; ) {
core/InfinityOrderBookComplication.sol:318: uint256 sum = 0;
core/InfinityOrderBookComplication.sol:320: for (uint256 i = 0; i < ordersLength; ) {
Consider removing explicit initializations for default values.
6. Some variables should be immutable
These variables are only set in the constructor and are never edited after that:
staking/InfinityStaker.sol:25: address public INFINITY_TOKEN;
Consider marking them as immutable, as it would avoid the expensive storage-writing operation (around 20 000 gas)
7. Use Custom Errors instead of Revert Strings to save Gas
Solidity 0.8.4 introduced custom errors. They are more gas efficient than revert strings, when it comes to deploy cost as well as runtime cost when the revert condition is met. Use custom errors instead of revert strings for gas savings.
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).
Consider replacing all revert strings with custom errors in the solution.
8. 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.
core/InfinityExchange.sol:1235: function addCurrency(address _currency) external onlyOwner {
core/InfinityExchange.sol:1240: function addComplication(address _complication) external onlyOwner {
core/InfinityExchange.sol:1245: function removeCurrency(address _currency) external onlyOwner {
core/InfinityExchange.sol:1250: function removeComplication(address _complication) external onlyOwner {
core/InfinityExchange.sol:1255: function updateMatchExecutor(address _matchExecutor) external onlyOwner {
core/InfinityExchange.sol:1260: function updateWethTranferGas(uint32 _wethTransferGasUnits) external onlyOwner {
core/InfinityExchange.sol:1266: function setProtocolFee(uint16 _protocolFeeBps) external onlyOwner {
staking/InfinityStaker.sol:351: function updateStakeLevelThreshold(StakeLevel stakeLevel, uint16 threshold) external onlyOwner {
staking/InfinityStaker.sol:375: function updateInfinityTreasury(address _infinityTreasury) external onlyOwner {
token/TimelockConfig.sol:118: function requestChange(bytes32 configId, uint256 value) external onlyAdmin {
token/TimelockConfig.sol:126: function cancelChange(bytes32 configId) external onlyAdmin {
9. Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)
core/InfinityExchange.sol:1161: uint256 PRECISION = 10**4; // precision for division; similar to bps
core/InfinityOrderBookComplication.sol:338: uint256 PRECISION = 10**4; // precision for division; similar to bps
staking/InfinityStaker.sol:237: (userstakedAmounts[user][Duration.TWELVE_MONTHS].amount * 4)) / (10**18);
Overview
Table of Contents:
>=
is cheaper than>
(and<=
cheaper than<
)require()
statements that use&&
saves gaspayable
1e18
) rather than exponentiation (e.g.10**18
)1. Unchecking arithmetics operations that can't underflow/overflow
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an
unchecked
block: https://docs.soliditylang.org/en/v0.8.10/control-structures.html#checked-or-unchecked-arithmeticConsider wrapping with an
unchecked
block here:2.
>=
is cheaper than>
(and<=
cheaper than<
)Strict inequalities (
>
) are more expensive than non-strict ones (>=
). This is due to some supplementary checks (ISZERO, 3 gas). This also holds true between<=
and<
.Consider replacing strict inequalities with non-strict ones to save some gas here:
3. Splitting
require()
statements that use&&
saves gasIf you're using the Optimizer at 200, instead of using the
&&
operator in a single require statement to check multiple conditions, Consider using multiple require statements with 1 condition per require statement:Please, note that this might not hold true at a higher number of runs for the Optimizer (10k). However, it indeed is true at 200.
4. Using private rather than public for constants saves gas
If needed, the value can be read from the verified contract source code. 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
5. It costs more gas to initialize variables with their default value than letting the default value be applied
If a variable is not set/initialized, it is assumed to have the default value (
0
foruint
,false
forbool
,address(0)
for address...). Explicitly initializing it with its default value is an anti-pattern and wastes gas.As an example:
for (uint256 i = 0; i < numIterations; ++i) {
should be replaced withfor (uint256 i; i < numIterations; ++i) {
Affected code:
Consider removing explicit initializations for default values.
6. Some variables should be immutable
These variables are only set in the constructor and are never edited after that:
Consider marking them as immutable, as it would avoid the expensive storage-writing operation (around 20 000 gas)
7. Use Custom Errors instead of Revert Strings to save Gas
Solidity 0.8.4 introduced custom errors. They are more gas efficient than revert strings, when it comes to deploy cost as well as runtime cost when the revert condition is met. Use custom errors instead of revert strings for gas savings.
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: https://blog.soliditylang.org/2021/04/21/custom-errors/:
Custom errors are defined using the
error
statement, which can be used inside and outside of contracts (including interfaces and libraries).Consider replacing all revert strings with custom errors in the solution.
8. 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 aspayable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.9. Use scientific notation (e.g.
1e18
) rather than exponentiation (e.g.10**18
)