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
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 than to let the default of zero be applied
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
Using unchecked blocks to save gas - Increments in for loop can be unchecked (saves around 30-40 gas per loop iteration)
The majority of Solidity for loops increment a uint256 variable that starts at 0. These increment operations never need to be checked for over/underflow because the variable will never reach the max number of uint256 (will run out of gas long before that happens). The default over/underflow check wastes gas in every iteration of virtually every for loop . eg.
Cache the length of arrays in loops ~6 gas per iteration
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
The solidity compiler will always read the length of the array during each iteration. That is,
1.if it is a storage array, this is an extra sload operation (100 additional extra gas (EIP-2929 2) for each iteration except for the first),
2.if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first),
3.if it is a calldata array, this is an extra calldataload operation (3 additional gas for each iteration except for the first)
This extra costs can be avoided by caching the array length (in stack):
When reading the length of an array, sload or mload or calldataload operation is only called once and subsequently replaced by a cheap dupN instruction. Even though mload , calldataload and dupN have the same gas cost, mload and calldataload needs an additional dupN to put the offset in the stack, i.e., an extra 3 gas. which brings this to 6 gas
Here, I suggest storing the array’s length in a variable before the for-loop, and use it instead:
for (uint i = 0; i < _prices.length; i++) {
priceAverageCumulative += _prices[i];
}
The above should be modified to
uint length = _prices.length;
for (uint i = 0; i < length; i++) {
priceAverageCumulative += _prices[i];
}
++i costs less gas compared to i++ or i += 1 (saves ~5 gas per iteration)
++i costs less gas compared to i++ or i += 1 for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
Instances include:
File: GovernorAlpha.sol line 181
for (uint i = 0; i < proposal.targets.length; i++) {
FIle: GovernorAlpha.sol line 152
FIle: GovernorAlpha.sol line 197
FIle: GovernorAlpha.sol line 211
File: GovernorBravoDelegate.sol line 66
File: GovernorBravoDelegate.sol line 88
Use Shift Right/Left instead of Division/Multiplication
A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.
While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.
File: BaseV1-periphery.sol line 122
File: BaseV1-periphery.sol line 123
Splitting require() statements that use && saves gas - (saves around 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).
Proof The following tests were carried out in remix with both optimization turned on and off
function multiple (uint a) public pure returns (uint){
require ( a > 1 && a < 5, "Initialized");
return a + 2;
}
Execution cost
21617 with optimization and using &&
After splitting the require statement
function multiple(uint a) public pure returns (uint){
require (a > 1 ,"Initialized");
require (a < 5 , "Initialized");
return a + 2;
}
Execution cost
21609 with optimization and split require
Expressions for constant values such as a call to keccak256(), should use immutable rather than constant
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.
If the variable was immutable instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.
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 )
FINDINGS
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 than to let the default of zero be applied
File: CToken.sol line 82
Other instances to modify
File: BaseV1-core.sol line 48
File: BaseV1-core.sol line 226-227
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: GovernorBravoDelegate.sol line 180-183
The operation
a-b
cannot underflow due to the check on line 181 which ensures that the value of a is greater than b before performing the operationThe above can be modified like
File: Comp.sol line 293
File: CToken.sol line 1064
Using unchecked blocks to save gas - Increments in for loop can be unchecked (saves around 30-40 gas per loop iteration)
The majority of Solidity for loops increment a uint256 variable that starts at 0. These increment operations never need to be checked for over/underflow because the variable will never reach the max number of uint256 (will run out of gas long before that happens). The default over/underflow check wastes gas in every iteration of virtually every for loop . eg.
e.g Let's work with a sample loop below.
can be written as shown below.
We can also write it as an inlined function like below.
Affected code
File: GovernorAlpha.sol line 181
The above should be modified to:
Other Instances to modify
FIle: GovernorAlpha.sol line 197
FIle: GovernorAlpha.sol line 211
File: GovernorBravoDelegate.sol line 66
File: GovernorBravoDelegate.sol line 88
see resource
Cache the length of arrays in loops ~6 gas per iteration
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
The solidity compiler will always read the length of the array during each iteration. That is,
1.if it is a storage array, this is an extra sload operation (100 additional extra gas (EIP-2929 2) for each iteration except for the first),
2.if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first),
3.if it is a calldata array, this is an extra calldataload operation (3 additional gas for each iteration except for the first)
This extra costs can be avoided by caching the array length (in stack):
When reading the length of an array, sload or mload or calldataload operation is only called once and subsequently replaced by a cheap dupN instruction. Even though mload , calldataload and dupN have the same gas cost, mload and calldataload needs an additional dupN to put the offset in the stack, i.e., an extra 3 gas. which brings this to 6 gas
Here, I suggest storing the array’s length in a variable before the for-loop, and use it instead:
File: BaseV1-core.sol line 210
The above should be modified to
++i costs less gas compared to i++ or i += 1 (saves ~5 gas per iteration)
++i costs less gas compared to i++ or i += 1 for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
Instances include:
File: GovernorAlpha.sol line 181
FIle: GovernorAlpha.sol line 152
FIle: GovernorAlpha.sol line 197
FIle: GovernorAlpha.sol line 211
File: GovernorBravoDelegate.sol line 66
File: GovernorBravoDelegate.sol line 88
File: BaseV1-core.sol line 210
Use Shift Right/Left instead of Division/Multiplication
A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.
While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.
relevant source
File:ERC20DirectBalanceManipulation.sol line 18
The above should be modified to
Comparisons: != is more efficient than > in require statement (6 gas less)
!= 0 costs less gas compared to > 0 for unsigned integers in require statements with the optimizer enabled (6 gas)
Note: This only applies in a require statement
I suggest changing > 0 with != 0 here:
File: BaseV1-core.sol line 289
File: BaseV1-periphery.sol line 122
File: BaseV1-periphery.sol line 123
Splitting require() statements that use && saves gas - (saves around 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: GovernorAlpha.sol line 138
File: GovernorAlpha.sol line 228
File: GovernorBravoDelegate.sol line 42
File: CToken.sol line 34
Proof
The following tests were carried out in remix with both optimization turned on and off
Execution cost
21617 with optimization and using &&
After splitting the require statement
Execution cost
21609 with optimization and split require
Expressions for constant values such as a call to keccak256(), should use immutable rather than constant
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.
If the variable was immutable instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.
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..)
File: TreasuryDelegate.sol line 12
File: TreasuryDelegate.sol line 13
File: Comp.sol line 39
File: Comp.sol line 42
File: GovernorAlpha.sol line 110
File: GovernorAlpha.sol line 113
File: GovernorBravoDelegate.sol line 15
File: GovernorBravoDelegate.sol line 18
File: ERC20MinterBurnerDecimals.sol line 27
File: ERC20MinterBurnerDecimals.sol line 28
File: ERC20MinterBurnerDecimals.sol line 29
The text was updated successfully, but these errors were encountered: