Awesome
WTF Gas Optimization
Solidity gas optimization techniques, verified with Foundry.
总结 Solidity 智能合约省 gas 技巧,并使用 Foundry 验证。
Lead by @0xKaso
Outline
6. use custom error over require/assert
7. use local variable over storage
8. use clone over new/create2 to deploy contracts
10. use ++i as better increment
11. use uint in reentrancy guard
13. optimized selector/method id
14. selector/method-id order matters
15. use shorter string in require()
16. use short circuit in logic operation ||
or &&
17. delete variables to get gas refund
18. do not initialize state variables with default values
19. swap 2 variables in 1 line with destructuring assignment
20. set constructor to payable to save gas
21. use bytes32 for short string
22. use fixed-size array over dynamic array
23. use event to store data when possible
24. use mapping
over array
when possible
1. use constant and immutable
Testing
forge test --contracts 01_Constant/Constant.t.sol --gas-report
Gas report
Function Name | Gas Cost |
---|---|
varConstant | 161 ✅ |
varImmutable | 161 ✅ |
variable | 2261 |
2. use calldata over memory
Testing
forge test --contracts 02_CalldataAndMemory/CalldataAndMemory.T.sol --gas-report
Gas report
Function Name | Gas Cost |
---|---|
writeByCalldata | 67905 ✅ |
writeByMemory | 68456 |
3. use Bitmap
Testing
forge test --contracts 03_Bitmap/Bitmap.T.sol --gas-report
Gas report
Function Name | Gas Cost |
---|---|
setDataWithBitmap | 22366 ✅ |
setDataWithBoolArray | 35729 |
4. use unchecked
Testing
forge test --contracts 04_unchecked/Unchecked.T.sol --gas-report
Gas report
Function Name | Gas Cost |
---|---|
forNormal | 1910309 |
forUnckecked | 570287 ✅ |
5. use uint256 over uint8
Testing
forge test --contracts 05_Uint/Uint.T.sol --gas-report
Gas report
Function Name | Gas Cost |
---|---|
read Uint8 | 2301 |
read Uint32 | 2301 |
read Uint256 | 2261 ✅ |
set Uint8 | 22234 |
set Uint128 | 22234 |
set Uint256 | 22238 |
UseUint8 | 53,427 |
UseUint32 | 53,895 |
UseUint256 | 42,950 ✅ |
6. use custom error over require/assert
Testing
forge test --contracts 06_Error/Error.T.sol --gas-report
Gas report
Error Name | Gas Cost |
---|---|
Assert | 180 |
Require | 268 |
Revert | 164 ✅ |
7. use local variable over storage
Testing
forge test --contracts 07_LocalData/LocalData.T.sol --gas-report
Gas report
Data Type | Gas Cost |
---|---|
localData | 1902339 ✅ |
storageData | 4022155 |
8. use clone over new/create2 to deploy contract
Testing
forge test --contracts 08_Clone/Clone.T.sol --gas-report
Gas report
Create Type | Gas Cost |
---|---|
clone | 41493 ✅ |
create2 | 93031 |
new | 79515 |
9. packing storage slots
Testing
forge test --contracts 09_Packing/Packing.T.sol --gas-report
Gas report
Create Type | Gas Cost |
---|---|
normal | 133521 |
packing | 111351 ✅ |
10. use ++i as better increment
forge test --contracts 10_Increment/Increment.T.sol --gas-report
Gas report
Increment | Gas Cost |
---|---|
i += 1 | 204 |
i = i +1 | 204 |
i++ | 198 |
++i | 193 ✅ |
11. use Uint in Reentrancy Guard
Testing
forge test --contracts 11_ReentrancyGuard/ReentrancyGuard.T.sol --gas-report
Gas report
ReentrancyGuard | Gas Cost | tips |
---|---|---|
Bool | 27757 | |
Uint01 | 27604 | 0 to non-zero -> 20000 gas |
Uint12 | 13908 ✅ | non-zero to non-zero -> 2900 gas |
12. use < over <=
Testing
forge test --contracts 12_LessThan/LessThan.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
<= | 250 |
< | 247 ✅ |
13 optimized selector
Testing
forge test --contracts 13_MethodName/MethodName.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
regular selector 0xf8a8fd6d | 5285 |
optimized selector 0x000073eb | 5265 ✅ |
14 selector order
Testing
forge test --contracts 14_MethodIdSort/MethodIdSort.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
test1 0x0dbe671f | 164 |
test2 0x66e41cb7 | 142 |
test3 0x0a8e8e01 | 120 |
test_y2K 0x000073eb | 98 ✅ |
15 shorter string in require()
Testing
forge test --contracts 15_RequireString/RequireString.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
longString | 2578 |
shortString | 2347 ✅ |
16. short circuit in logic operation
Testing
forge test --contracts 16_ShortCircuit/ShortCircuit.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
normal | 191,282 |
shortCircuit | 120 ✅ |
17. delete variables to get gas refund
Testing
forge test --contracts 17_DeleteVar/DeleteVar.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
update | 22,238 |
updateDefault | 2360 ✅ |
updateDelete | 2316 ✅ |
18. do not initialize state variables with default values
Testing
forge test --contracts 18_InitDefault/InitDefault.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
testDefault | 67,148 ✅ |
testInitDefault | 69,376 |
19. swap 2 variables in 1 line with destructuring assignment
Testing
forge test --contracts 19_SwapVars/SwapVars.t.sol --gas-report
Gas report
This technique will not save gas, but it makes your code look better :p
Operator | Gas Cost |
---|---|
swap | 282 |
desSwap | 282 ✅ |
20. set constructor to payable to save gas
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. The following opcodes are cut out:
CALLVALUE
DUP1
ISZERO
PUSH2
JUMPI
PUSH1
DUP1
REVERT
JUMPDEST
POP
In Solidity, this chunk of assembly would mean the following:
if(msg.value != 0) revert();
Testing
forge test --contracts 20_PayableConstructor/PayableConstructor.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
default | 67,171 |
payable constructor | 67,102 ✅ |
21. use bytes32 for short string
Testing
forge test --contracts 21_Bytes32String/Bytes32String.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
setBytes32 | 22,222 ✅ |
setString | 22,682 |
22. use fixed-size array over dynamic array
Testing
forge test --contracts 22_FixedSize/FixedSize.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
set dynamic-length array | 2,224,770 |
set fixed-length array | 2,182,608 ✅ |
23. use event
to store data when possible
Testing
forge test --contracts 23_Event/Event.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
useVar | 22,216 |
useEvent | 1,189 ✅ |
24. use mapping
over array
when possible
Testing
forge test --contracts 24_MappingArray/MappingArray.t.sol --gas-report
Gas report
Operator | Gas Cost |
---|---|
Mapping get | 451 ✅ |
Mapping insert | 22,385 ✅ |
Mapping remove | 305 ✅ |
Array get | 710 |
Array insert | 44,442 |
Array remove | 748 |