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
The following sections detail the gas optimizations found throughout the codebase.
Each optimization is documented with the setup, an explainer for the optimization, a gas report and line identifiers for each optimization across the codebase. For each section's gas report, the optimizer was turned on and set to 10000 runs.
You can replicate any tests/gas reports by heading to 0xKitsune/gas-lab and cloning the repo. Then, simply copy/paste the contract examples from any section and run forge test --gas-report.
You can also easily update the optimizer runs in the foundry.toml.
Consider marking constants as private
Marking constant variables in storage as constant saves gas. Unless a constant variable should be easily accessible by another protocol or offchain logic, consider marking it as private.
Mark storage variables as constant if they never change.
State variables can be declared as constant or immutable. In both cases, the variables cannot be modified after the contract has been constructed. For constant variables, the value has to be fixed at compile-time, while for immutable, it can still be assigned at construction time.
The compiler does not reserve a storage slot for these variables, and every occurrence is inlined by the respective value.
Compared to regular state variables, the gas costs of constant and immutable variables are much lower. For a constant variable, the expression assigned to it is copied to all the places where it is accessed and also re-evaluated each time. This allows for local optimizations. Immutable variables are evaluated once at construction time and their value is copied to all the places in the code where they are accessed. For these values, 32 bytes are reserved, even if they would fit in fewer bytes. Due to this, constant values can sometimes be cheaper than immutable values.
Use assembly when getting a contract's balance of ETH.
You can use selfbalance() instead of address(this).balance when getting your contract's balance of ETH to save gas. Additionally, you can use balance(address) instead of address.balance() when getting an external contract's balance of ETH.
Use calldata instead of memory for function arguments that do not get mutated.
Mark data types as calldata instead of memory where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory storage.
You can mark public or external functions as payable to save gas. Functions that are not payable have additional logic to check if there was a value sent with a call, however, making a function payable eliminates this check. This optimization should be carefully considered due to potentially unwanted behavior when a function does not need to accept ether.
Use assembly for math instead of Solidity. You can check for overflow/underflow in assembly to ensure safety. If using Solidity versions < 0.8.0 and you are using Safemath, you can gain significant gas savings by using assembly to calculate values and checking for overflow/underflow.
contractGasTestisDSTest{Contract0c0;Contract1c1;Contract2c2;Contract3c3;Contract4c4;Contract5c5;Contract6c6;Contract7c7;functionsetUp()public{c0=newContract0();c1=newContract1();c2=newContract2();c3=newContract3();c4=newContract4();c5=newContract5();c6=newContract6();c7=newContract7();}functiontestGas()public{c0.addTest(34598345,100);c1.addAssemblyTest(34598345,100);c2.subTest(34598345,100);c3.subAssemblyTest(34598345,100);c4.mulTest(34598345,100);c5.mulAssemblyTest(34598345,100);c6.divTest(34598345,100);c7.divAssemblyTest(34598345,100);}}contractContract0{//addition in SolidityfunctionaddTest(uint256a,uint256b)publicpure{uint256c=a+b;}}contractContract1{//addition in assemblyfunctionaddAssemblyTest(uint256a,uint256b)publicpure{assembly{letc :=add(a,b)iflt(c,a){mstore(0x00,"overflow")revert(0x00,0x20)}}}}contractContract2{//subtraction in SolidityfunctionsubTest(uint256a,uint256b)publicpure{uint256c=a-b;}}contractContract3{//subtraction in assemblyfunctionsubAssemblyTest(uint256a,uint256b)publicpure{assembly{letc :=sub(a,b)ifgt(c,a){mstore(0x00,"underflow")revert(0x00,0x20)}}}}contractContract4{//multiplication in SolidityfunctionmulTest(uint256a,uint256b)publicpure{uint256c=a*b;}}contractContract5{//multiplication in assemblyfunctionmulAssemblyTest(uint256a,uint256b)publicpure{assembly{letc :=mul(a,b)iflt(c,a){mstore(0x00,"overflow")revert(0x00,0x20)}}}}contractContract6{//division in SolidityfunctiondivTest(uint256a,uint256b)publicpure{uint256c=a*b;}}contractContract7{//division in assemblyfunctiondivAssemblyTest(uint256a,uint256b)publicpure{assembly{letc :=div(a,b)ifgt(c,a){mstore(0x00,"underflow")revert(0x00,0x20)}}}}
Gas Optimizations - (Total Optimizations 108)
The following sections detail the gas optimizations found throughout the codebase.
Each optimization is documented with the setup, an explainer for the optimization, a gas report and line identifiers for each optimization across the codebase. For each section's gas report, the optimizer was turned on and set to 10000 runs.
You can replicate any tests/gas reports by heading to 0xKitsune/gas-lab and cloning the repo. Then, simply copy/paste the contract examples from any section and run
forge test --gas-report
.You can also easily update the optimizer runs in the
foundry.toml
.Consider marking constants as private
Marking constant variables in storage as constant saves gas. Unless a constant variable should be easily accessible by another protocol or offchain logic, consider marking it as private.
Gas Report
Lines
Use assembly to write storage values
Gas Report
Lines
Mark storage variables as
constant
if they never change.State variables can be declared as constant or immutable. In both cases, the variables cannot be modified after the contract has been constructed. For constant variables, the value has to be fixed at compile-time, while for immutable, it can still be assigned at construction time.
The compiler does not reserve a storage slot for these variables, and every occurrence is inlined by the respective value.
Compared to regular state variables, the gas costs of constant and immutable variables are much lower. For a constant variable, the expression assigned to it is copied to all the places where it is accessed and also re-evaluated each time. This allows for local optimizations. Immutable variables are evaluated once at construction time and their value is copied to all the places in the code where they are accessed. For these values, 32 bytes are reserved, even if they would fit in fewer bytes. Due to this, constant values can sometimes be cheaper than immutable values.
Gas Report
Lines
Use assembly when getting a contract's balance of ETH.
You can use
selfbalance()
instead ofaddress(this).balance
when getting your contract's balance of ETH to save gas. Additionally, you can usebalance(address)
instead ofaddress.balance()
when getting an external contract's balance of ETH.Gas Report
Lines
Use
calldata
instead ofmemory
for function arguments that do not get mutated.Mark data types as
calldata
instead ofmemory
where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in ascalldata
. The one exception to this is if the argument must later be passed into another function that takes an argument that specifiesmemory
storage.Gas Report
Lines
Right shift or Left shift instead of dividing or multiplying by powers of two
Gas Report
Lines
Mark functions as payable (with discretion)
You can mark public or external functions as payable to save gas. Functions that are not payable have additional logic to check if there was a value sent with a call, however, making a function payable eliminates this check. This optimization should be carefully considered due to potentially unwanted behavior when a function does not need to accept ether.
Gas Report
Lines
Use assembly to check for address(0)
Gas Report
Lines
Use assembly for math (add, sub, mul, div)
Use assembly for math instead of Solidity. You can check for overflow/underflow in assembly to ensure safety. If using Solidity versions < 0.8.0 and you are using Safemath, you can gain significant gas savings by using assembly to calculate values and checking for overflow/underflow.
Gas Report
Lines
The text was updated successfully, but these errors were encountered: