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
[G-02] Optimize gas by using != 0 over > 0 in require() for unsigned integers
For contracts using Solidity versions up to 0.8.13, there's a slight gas optimization to be had when using != 0 instead of > 0 in require() checks on unsigned integers.
When the optimizer is enabled, the != 0 check consumes 6 gas units less. Although it might appear that > 0 is more gas-efficient, this holds true only without the optimizer and outside of require statements.
For contracts where every gas unit matters, consider this optimization.
File: contracts/StabilityPool.sol
}276: for (uint128 i =0; i < _snapshots.tokenToSArray.length; i++) {
}285: for (uint128 i =0; i < nextTokensToSum_cached.length; i++) {
}424: for (uint128 i =0; i < currentTokenToSArray.length; i++) {
}552: for (uint128 j =0; j < _snapshots.tokenToSArray.length; j++) {
}561: for (uint128 j =0; j < nextTokensToSum_cached.length; j++) {
}758: for (uint256 i =0; i < _depositorCollateralGains.length; i++) {
File: contracts/Vault.sol
}177: for (uint256 i =0; i < collateralSet.length(); i++) {
}385: for (uint256 i =0; i < collateralSet.length(); i++) {
}408: for (uint256 i =0; i < collateralSet.length(); i++) {
[G-06] Using assembly to check for zero can save gas
Using assembly to check for zero can save gas by allowing more direct access to the evm and reducing some of the overhead associated with high-level operations in solidity.
File: ~All files / Example with BONQMath.sol
}67: if (_minutes ==0) {
}77: if (n %2==0) {
[G-07] Revert strings are less gas-efficient than custom errors
From Solidity v0.8.4 onward, custom errors have been introduced.
These errors provide a savings of approximately 50 gas each instance they're triggered, as they circumvent the need to allocate and keep the revert string.
Omitting these strings also conserves gas during deployment.
Furthermore, custom errors are versatile, applicable both inside and outside contracts, including in interfaces and libraries.
As stated in the Solidity blog:
With the introduction of Solidity v0.8.4, a streamlined and gas-efficient method has been provided to elucidate to users the reasons behind an operation's failure through custom errors.
Prior to this, although strings could be used to detail failure reasons (e.g., revert('Insufficient funds.');), they were notably costlier, especially in terms of deployment, and incorporating dynamic information into them was challenging.
It's advisable to transition all revert strings to custom errors in your solution, especially focusing on those that appear multiple times.
File: ~All filesrequire(...
[G-08] Using a double if statement instead of a logical AND(&&)
Using a double if statement instead of a logical AND (&&) can provide similar short-circuiting behavior whereas double if is slightly more gas efficient.
[G-09] Use shift right/left instead of division/multiplication if possible
While the DIV / MUL opcode uses 5 gas, the SHR / SHL opcode only uses 3 gas. Furthermore, beware that Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting. Eventually, overflow checks are never performed for shift operations as they are done for arithmetic operations. Instead, the result is always truncated, so the calculation can be unchecked in Solidity version 0.8+
Use >> 1 instead of / 2
Use >> 2 instead of / 4
Use << 3 instead of * 8
...
Use >> 5 instead of / 2^5 == / 32
Use << 6 instead of * 2^6 == * 64
TL;DR:
Shifting left by N is like multiplying by 2^N (Each bits to the left is an increased power of 2)
Shifting right by N is like dividing by 2^N (Each bits to the right is a decreased power of 2)
Saves around 2 gas + 20 for unchecked per instance
File: contracts/utils/BONQMath.sol
}47: decProd = (prod_xy + (DECIMAL_PRECISION /2)) / DECIMAL_PRECISION;
}79: n = n /2;
}84: n = (n -1) /2;
Using a more recent version of Solidity offers various benefits, including enhanced functionality, bug fixes, and potential gas savings. It ensures your smart contracts are more secure and compatible with the latest tools and libraries
File: ~All files
}2: pragma solidity^0.8.4;
[G-11] Increments/decrements can be unchecked in for-loops
In Solidity 0.8+, there's a default overflow check on unsigned integers. It's possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.
- for (uint256 i; i < numIterations; i++) {+ for (uint256 i; i < numIterations;) {
// ...
+ unchecked { ++i; }
}
These save around 25 gas saved per instance.
The same can be applied with decrements (which should use break when i == 0).
The risk of overflow is non-existent for uint256.
File: ~All files / Example with AuctionManager.sol
}170: for (uint256 i =0; i < _collateralsLength; i++) {
}259: for (uint256 i =0; i < _collateralsLength; i++) {
}299: for (uint256 i =0; i < _collateralsLength; i++) {
[G-12] Separate require() conditions for gas efficiency
It's beneficial to separate conditions in require() statements rather than using the && operator.
As outlined in this discussed issue, while there might be a slightly higher deployment gas cost initially, the overall gas savings in runtime calls makes this approach more cost-effective in the long run.
Implementing this change can result in a saving of approximately 3 gas per instance.
@antoniopel @CristianRicharte6
I wanted to contribute to the protocol and make all these Gas Optimisations issues.
All are free of charge to show you my respect.
!= 0
over> 0
inrequire()
for unsigned integers[G-01] Missing validation check in redeemReward
Context StabilityPool
Impact Users with a zero deposit are allowed to execute the function, potentially causing unnecessary gas expenditure.
Description
Recomendation. Add validation check.
function redeemReward() external { Snapshots memory snapshots = depositSnapshots[msg.sender]; uint256 contributorDeposit = deposits[msg.sender]; + require(contributorDeposit > 0, "deposit-is-0") uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(contributorDeposit, snapshots); _redeemReward(); _updateDepositAndSnapshots(msg.sender, compoundedDeposit); }
[G-02] Optimize gas by using
!= 0
over> 0
inrequire()
for unsigned integersFor contracts using Solidity versions up to
0.8.13
, there's a slight gas optimization to be had when using!= 0
instead of> 0
inrequire()
checks on unsigned integers.When the optimizer is enabled, the
!= 0
check consumes 6 gas units less. Although it might appear that> 0
is more gas-efficient, this holds true only without the optimizer and outside ofrequire
statements.For contracts where every gas unit matters, consider this optimization.
Reference: Tweet by @gzeon.
Ensure the Optimizer is enabled for maximal benefit.
+ require(_amount != 0, "amount-is-0");
[G-03] a = a + b consumes less gas than a += b for state variables, except for arrays and mappings
144, 180
[G-04] Use assembly to validate msg.sender
We can use assembly to efficiently validate msg.sender with the least amount of opcodes necessary. For more details check the following report here
156
84, 89
[G-05] Store array length outside loops for efficiency
When array length isn't cached, the Solidity compiler repeatedly reads it in every loop iteration.
For storage arrays, this means an added sload operation costing 100 extra gas for each subsequent iteration.
For memory arrays, it's an additional mload operation costing 3 extra gas post the first iteration.
125
276, 285, 424, 552, 561, 758
177, 385, 408
[G-06] Using assembly to check for zero can save gas
Using assembly to check for zero can save gas by allowing more direct access to the evm and reducing some of the overhead associated with high-level operations in solidity.
67, 77
[G-07] Revert strings are less gas-efficient than custom errors
From Solidity v0.8.4 onward, custom errors have been introduced.
These errors provide a savings of approximately 50 gas each instance they're triggered, as they circumvent the need to allocate and keep the revert string.
Omitting these strings also conserves gas during deployment.
Furthermore, custom errors are versatile, applicable both inside and outside contracts, including in interfaces and libraries.
As stated in the Solidity blog:
With the introduction of Solidity v0.8.4, a streamlined and gas-efficient method has been provided to elucidate to users the reasons behind an operation's failure through custom errors.
Prior to this, although strings could be used to detail failure reasons (e.g., revert('Insufficient funds.');), they were notably costlier, especially in terms of deployment, and incorporating dynamic information into them was challenging.
It's advisable to transition all revert strings to custom errors in your solution, especially focusing on those that appear multiple times.
[G-08] Using a double if statement instead of a logical AND(&&)
Using a double if statement instead of a logical AND (&&) can provide similar short-circuiting behavior whereas double if is slightly more gas efficient.
46, 53, 91
[G-09] Use shift right/left instead of division/multiplication if possible
While the
DIV
/MUL
opcode uses 5 gas, theSHR
/SHL
opcode only uses 3 gas. Furthermore, beware that Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting. Eventually, overflow checks are never performed for shift operations as they are done for arithmetic operations. Instead, the result is always truncated, so the calculation can be unchecked in Solidity version0.8+
>> 1
instead of/ 2
>> 2
instead of/ 4
<< 3
instead of* 8
>> 5
instead of/ 2^5 == / 32
<< 6
instead of* 2^6 == * 64
TL;DR:
Saves around 2 gas + 20 for unchecked per instance
47, 79, 84
21
[G-10] Upgrade to a newer Solidity version.
Using a more recent version of Solidity offers various benefits, including enhanced functionality, bug fixes, and potential gas savings. It ensures your smart contracts are more secure and compatible with the latest tools and libraries
[G-11] Increments/decrements can be unchecked in for-loops
In Solidity 0.8+, there's a default overflow check on unsigned integers. It's possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.
ethereum/solidity#10695
The change would be:
These save around 25 gas saved per instance.
The same can be applied with decrements (which should use
break
wheni == 0
).The risk of overflow is non-existent for
uint256
.170, 259, 299
[G-12] Separate require() conditions for gas efficiency
It's beneficial to separate conditions in
require()
statements rather than using the&&
operator.As outlined in this discussed issue, while there might be a slightly higher deployment gas cost initially, the overall gas savings in runtime calls makes this approach more cost-effective in the long run.
Implementing this change can result in a saving of approximately 3 gas per instance.
232
92
The text was updated successfully, but these errors were encountered: