Gas savings are estimated using existing tests and may vary depending on the implementation of the fix. I keep my version of the fix for each finding and can provide them if you need. Since the optimizer config is set to 10000 runs, some optimizations were excluded. At a lower level of optimization, additional fixes make sense.
Gas Optimizations
Issue
Instances
Estimated gas(deployments)
Estimated gas(method call)
1
Use Custom Errors instead of Revert/Require Strings to save Gas
42
195612
-
2
The same check can be combined into a modifier or a function
3
180175
-
3
Avoid contract existence checks by using solidity version 0.8.10 or later
2
128057
7684
4
Pack structure members in one slot
1
102300
18294
5
State variables only set in the constructor should be declared immutable
2
29599
4203
6
Increment and assignment optimization
4/15
28389
3572
7
Using private rather than public for constants, saves gas
3
25197
8
Using bools for storage incurs overhead
1
3894
-
9
Division by two should use bit shifting
2
3692
10
Do not recalculate the same expression
1
3452
896
Total: 72 instances over 10 issues
Use Custom Errors instead of Revert/Require Strings to save Gas (42 instances)
Deployment gas saved: 195612
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.
Avoid contract existence checks by using solidity version 0.8.10 or later
Use a solidity version of at least 0.8.0 to get overflow protection without SafeMath
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining
Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (700 gas), to check for contract existence for external calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value
By changing version to 0.8.10 in contracts VotingEscrow.sol and Blocklist.sol
Deployment gas saved: 128057
Method Call gas saved: 7684
Pack structure members in one slot
Deployment Gas Saved: 102300
Method Call Gas Saved: 18294
309 for (uint256 i = 0; i < 255; i++) {
...
717 for (uint256 i = 0; i < 128; i++) {
...
739 for (uint256 i = 0; i < 128; i++) {
...
834 for (uint256 i = 0; i < 255; i++) {
It is also more efficient to use x = x + y instead of x +=
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
46 uint256 public constant WEEK = 7 days;
47 uint256 public constant MAXTIME = 365 days;
48 uint256 public constant MULTIPLIER = 10**18;
Using bools for storage incurs overhead (1 instances)
// 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.
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past
/ 2 is the same as >> 1. The DIV opcode costs 5 gas, whereas SHR only costs 3 gas. Especially in loops
- contracts/VotingEscrow.sol:[719](https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L719),[743](https://github.com/code-423n4/2022-08-fiatdao/blob/fece3bdb79ccacb501099c24b60312cd0b2e4bb2/contracts/VotingEscrow.sol#L743)
```solidity
719 uint256 mid = (min + max + 1) / 2;
...
743 uint256 mid = (min + max + 1) / 2;
```
Do not recalculate the same expression
Deployment Gas Saved: 3452
Method Call Gas Saved: 896
Summary
Gas savings are estimated using existing tests and may vary depending on the implementation of the fix. I keep my version of the fix for each finding and can provide them if you need. Since the optimizer config is set to 10000 runs, some optimizations were excluded. At a lower level of optimization, additional fixes make sense.
Total: 72 instances over 10 issues
Use Custom Errors instead of Revert/Require Strings to save Gas (42 instances)
Deployment gas saved: 195612
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 save ~50 gas each time they're hitby avoiding having to allocate and store the revert string. Additional gas is saved due to the lack of defining string. https://blog.soliditylang.org/2021/04/21/custom-errors/#errors-in-depth
The same check can be combined into a modifier or a function (3 instances)
Deployment Gas saved in total: 180175
Deployment Gas Saved: 89936
fix:
Deployment Gas saved: 57894
fix:
Deployment Gas saved: 32345
fix:
Avoid contract existence checks by using solidity version 0.8.10 or later
Deployment gas saved: 128057 Method Call gas saved: 7684
Pack structure members in one slot
Deployment Gas Saved: 102300 Method Call Gas Saved: 18294
fix:
State variables only set in the constructor should be declared immutable
Deployment Gas Saved: 29599 Method Call Gas Saved: 4203
Increment and assignment optimization (4/15 instances)
All gas calculation for this 4 optimization are cummulitive
Post increment and preincrement:
Deployment gas Saved: 1724
x=x+1 more efficient :
Deployment Gas Saved: 12329
increment in loop can be unchecked
Deployment Gas Saved : 21181
Method call Gas SAved: 3420
It is also more efficient to use x = x + y instead of x +=
Deployment Gas Saved : 28389
Method call Gas SAved: 3572
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
Deployment Gas Saved: 25197
Using bools for storage incurs overhead (1 instances)
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past
Gas Saved: 3894
fix:
plus сhanging all occurrences in the code
Division by two should use bit shifting
Deploy Gas Saved: 3692
Do not recalculate the same expression
Deployment Gas Saved: 3452 Method Call Gas Saved: 896
fix: