Splitting require() statements that use && saves gas - 8 gas per &&
Instead of using the && operator in a single require statement to check multiple conditions,using multiple require statements with 1 condition per require statement will save 8 GAS per &&
The gas difference would only be realized if the revert condition is realized(met).
require(makerOrderValid && executionValid, 'order not verified');
The above should be modified to
require(makerOrderValid,'order not verified');
require(executionValid, 'order not verified');
ProofThe following tests were carried out in remix with both optimization turned on and off
require ( a > 1 && a < 5, "Initialized");
return a + 2;
}
Execution cost
21617 with optimization and using &&
21976 without optimization and using &&
After splitting the require statement
require (a > 1 ,"Initialized");
require (a < 5 , "Initialized");
return a + 2;
}
Execution cost
21609 with optimization and split require
21968 without optimization and using split require
Comparisons: != is more efficient than > in require
!= 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas)
For uints the minimum value would be 0 and never a negative value. Since it cannot be a negative value, then the check > 0 is essentially checking that the value is not equal to 0 therefore >0 can be replaced with !=0 which saves 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. see
Something similar to my proposal is already implemented here
File: InfinityStaker.sol line 68
require(amount != 0, 'stake amount cant be 0');
use shorter revert strings(less than 32 bytes)
You can (and should) attach error reason strings along with require statements to make it easier to understand why a contract call reverted. These strings, however, take space in the deployed bytecode. Every reason string takes at least 32 bytes so make sure your string fits in 32 bytes or it will become more expensive.
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.
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).
Using unchecked blocks to save gas
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
Note: Before the subtraction operation is carried out, we have a check that is executed. The first arithemetic startPrice - endPrice will be executed if the condition startPrice > endPrice is true. This means startPrice must be greater than endPrice so no underflow would occur here.
The second part of the subtraction arithemetic endPrice - startPrice would be executed only if the check startPrice > endPrice fails which would mean startPrice not greater than endPrice therefore the operation endPrice - startPrice would not underflow as endPrice is guaranted to be greater than startPrice
The above cannot underflow due to the check on line 306
No need to initialize variables with their default values
If a variable is not set/initialized, it is assumed to have the default value (0, false, 0x0 etc depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas.
It costs more gas to initialize variables to zero/false than to let the default of zero/false be applied
Splitting require() statements that use && saves gas - 8 gas per &&
Instead of using the && operator in a single require statement to check multiple conditions,using multiple require statements with 1 condition per require statement will save 8 GAS per && The gas difference would only be realized if the revert condition is realized(met).
File:InfinityExchange.sol line 264
The above should be modified to
File: InfinityExchange.sol line 949
The above should be modified to
Proof The following tests were carried out in remix with both optimization turned on and off
Execution cost 21617 with optimization and using && 21976 without optimization and using &&
After splitting the require statement
Execution cost 21609 with optimization and split require 21968 without optimization and using split require
Comparisons: != is more efficient than > in require
!= 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas)
For uints the minimum value would be 0 and never a negative value. Since it cannot be a negative value, then the check > 0 is essentially checking that the value is not equal to 0 therefore >0 can be replaced with !=0 which saves 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. see
I suggest changing > 0 with != 0 here:
File: InfinityExchange.sol line 392
The above should be modifed to:
Something similar to my proposal is already implemented here File: InfinityStaker.sol line 68
use shorter revert strings(less than 32 bytes)
You can (and should) attach error reason strings along with require statements to make it easier to understand why a contract call reverted. These strings, however, take space in the deployed bytecode. Every reason string takes at least 32 bytes so make sure your string fits in 32 bytes or it will become more expensive.
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.
File: InfinityExchange.sol line 395
File:InfinityStaker.sol line 92
File:InfinityStaker.sol line 96
I suggest shortening the revert strings to fit in 32 bytes, or using custom errors which is explained below
Use 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)
see Source
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).
Using unchecked blocks to save gas
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
File: InfinityExchange.sol line 1156
Note: Before the subtraction operation is carried out, we have a check that is executed. The first arithemetic
startPrice - endPrice
will be executed if the conditionstartPrice > endPrice
is true. This means startPrice must be greater than endPrice so no underflow would occur here. The second part of the subtraction arithemeticendPrice - startPrice
would be executed only if the checkstartPrice > endPrice
fails which would mean startPrice not greater than endPrice therefore the operationendPrice - startPrice
would not underflow as endPrice is guaranted to be greater than startPriceThe above should be modified to
Other instances to modify
File: InfinityExchange.sol line 1164
File:InfinityOrderBookComplication.sol line 333
FIle:InfinityStaker.sol line 301
The above cannot underflow due to the check on line 298 that ensures that amount is greater than noVesting. see below
FIle:InfinityStaker.sol lne 305
The above cannot underflow due the check on line 302
FIle:InfinityStaker.sol line 309
The above cannot underflow due to the check on line 306
No need to initialize variables with their default values
If a variable is not set/initialized, it is assumed to have the default value (0, false, 0x0 etc depending on the data type). If you explicitly initialize it with its default value, you are just wasting gas. It costs more gas to initialize variables to zero/false than to let the default of zero/false be applied
File: InfinityOrderBookComplication.sol line 42
The default values for booleans is false therefore no need to initialize it here. so the above should be declared as follows
Something similar to my proposal was implemented a few lines after that but for uint256
File: InfinityOrderBookComplication.sol line 45
Other Instances to modify File: InfinityOrderBookComplication.sol line 108
File: InfinityOrderBookComplication.sol line 214
File: InfinityOrderBookComplication.sol line 244 File: InfinityOrderBookComplication.sol line 289 File: InfinityOrderBookComplication.sol line 318