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
Description:
Short-circuiting is a solidity contract development model that uses OR/AND logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum Virtual Machine operation.
//f(x) is a low gas cost operation
//g(y) is a high gas cost operation
//Sort operations with different gas costs as follows
f(x) || g(y)
f(x) && g(y)
Proof Of Concept:
The optimizer was turned on and set to 10000 runs
ERC1967Upgrade.sol#L61: [9 Gas Saved (PoC is given below)]
Description:
A division by 2 can be calculated by shifting one to the right. 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.
Recommendation:
Instead of dividing by 2 use >> 1.
like this:
medium=high-(high-low)>>1;
Proof Of Concept:
The optimizer was turned on and set to 10000 runs
Description:
OpenZeppelin uint256 and bool comparison :
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.The values being non-zero value makes deployment a bit more expensive.
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past.
Recommendation:
Use uint256(1) and uint256(2) instead of bool.
Proof Of Concept:
The optimizer was turned on and set to 10000 runs
contractGasTestisDSTest{Contract0c0;Contract1c1;functionsetUp()public{c0=newContract0();c1=newContract1();}functiontestGas()public{c0._setPolicyPermissions(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,79,true);c1._setPolicyPermissions(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,79,2);}}contractContract0{mapping(address=>mapping(uint256=>bool))publicmodulePermissions;errorboolcheck();function_setPolicyPermissions(addressaddr_,uint256id,boollog)external{modulePermissions[addr_][id]=log;if(modulePermissions[addr_][id]=false)revertboolcheck();}}contractContract1{mapping(address=>mapping(uint256=>uint256))publicmodulePermissions;errorboolcheck();// Use uint256 instead of bool (1 = false , 2 = true) function_setPolicyPermissions(addressaddr_,uint256id,uint256log)external{modulePermissions[addr_][id]==log;if(modulePermissions[addr_][id]==1)revertboolcheck();}}
Description:
If a function modifier or require such as onlyOwner-admin is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2), which costs an average of about 24 gas per call to the function, in addition to the extra deployment cost.
Recommendation:
Functions guaranteed to revert when called by normal users can be marked payable (for only onlyowner or admin functions)
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
[G-07] Function ordering via method ID [22 gas per instance]
Context:
All Contracts
Description:
Contracts most called functions could simply save gas by function ordering via Method ID. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.
Recommendation:
Find a lower method ID name for the most called functions for example Call() vs. Call1() is cheaper by 22 gas
For example, the function IDs in the Governor.sol contract will be the most used; A lower method ID may be given.
Description:
WETH is a wrap Ether contract with a specific address in the Etherum network, giving the option to define it may cause false recognition, it is healthier to define it directly.
Advantages of defining a specific contract directly:
It saves gas,
Prevents incorrect argument definition,
Prevents execution on a different chain and re-signature issues,
/// @param _manager The contract upgrade manager address/// @param _weth The address of WETHconstructor(address_manager,address_weth)payableinitializer{manager=IManager(_manager);WETH=_weth;}
Description: WETH9 was written for 0.4.18 (2017!) and we are at 0.8.17 today, so WETH9 works with a very old and high gas consuming system.
WETH3074 is a new version of the WETH contract which utilizes EIP-3074 to avoid the need for wrapping/unwrapping. In essence it turns ETH into an ERC-20 compatible token.
Description:
In Token.sol file, ERC721Votes and ERC721 contracts are imported, but ERC721Votes has already imported ERC721 contract.
Therefore, there is no need to import ERC721.
Description:
There’s a way to save a significant amount of gas on deployment using Clones: https://www.youtube.com/watch?v=3Mw-pMmJ7TA .
It is extremely inexpensive to distribute by Cloneing with Create2.
Description:
It is more efficient to use abi.encodePacked instead of abi.encode.
When are these used? abi.encode:
abi.encode encode its parameters using the ABI specs. The ABI was designed to make calls to contracts. Parameters are padded to 32 bytes. If you are making calls to a contract you likely have to use abi.encode
If you are dealing with more than one dynamic data type as it prevents collision.
abi.encodePacked:
abi.encodePacked encode its parameters using the minimal space required by the type. Encoding an uint8 it will use 1 byte. It is used when you want to save some space, and not calling a contract.Takes all types of data and any amount of input.
Description:
If statements can be placed earlier to reduce gas usage on revert.
functionpropose(address[]memory_targets,uint256[]memory_values,bytes[]memory_calldatas,stringmemory_description)externalreturns(bytes32){// Get the current proposal thresholduint256currentProposalThreshold=proposalThreshold();// Cannot realistically underflow and `getVotes` would revertunchecked{// Ensure the caller's voting weight is greater than or equal to the thresholdif(getVotes(msg.sender,block.timestamp-1)<proposalThreshold())revertBELOW_PROPOSAL_THRESHOLD();}// Cache the number of targetsuint256numTargets=_targets.length;// Ensure at least one target existsif(numTargets==0)revertPROPOSAL_TARGET_MISSING();// Ensure the number of targets matches the number of values and calldataif(numTargets!=_values.length)revertPROPOSAL_LENGTH_MISMATCH();if(numTargets!=_calldatas.length)revertPROPOSAL_LENGTH_MISMATCH();…
Recommendation:
functionpropose(address[]memory_targets,uint256[]memory_values,bytes[]memory_calldatas,stringmemory_description)externalreturns(bytes32){// Cache the number of targetsuint256 numTargets =_targets.length;// Ensure at least one target existsif(numTargets==0)revertPROPOSAL_TARGET_MISSING();// Ensure the number of targets matches the number of values and calldataif(numTargets!=_values.length)revertPROPOSAL_LENGTH_MISMATCH();if(numTargets!=_calldatas.length)revertPROPOSAL_LENGTH_MISMATCH();// Get the current proposal thresholduint256currentProposalThreshold=proposalThreshold();// Cannot realistically underflow and `getVotes` would revertunchecked{// Ensure the caller's voting weight is greater than or equal to the thresholdif(getVotes(msg.sender,block.timestamp-1)<proposalThreshold())revertBELOW_PROPOSAL_THRESHOLD();}…
[S-01] Missing zero-address check in constructor
Context:
All Constructors
Description:
Missing checks for zero-addresses may lead to infunctional protocol, if the variable addresses are updated incorrectly. It also wast gas as it requires the redeployment of the contract.
Gas Optimizations List
return
keyword for gas efficientuint256
instead ofbool
in mappings is more gas efficientx += y
costs more gas thanx = x + y
for state variablesprivate
rather thanpublic
forconstans
, saves gasaddress(0)
>
instead of>=
is more gas efficientuint256
instead of awhile
loop saves gasuse with WETH3074
clone
instead ofnew
abi.encodePacked
instead ofabi.encode
Total 20 issues
Suggestions
zero-address
check in `constructorv4.8.0 OpenZeppelin ERC721Votes
contractTotal 2 suggestions
[G-01] Sort solidity operations using short-circuit mode [9 gas saved]
Context:
ERC1967Upgrade.sol#L61
Initializable.sol#L36
Description:
Short-circuiting is a solidity contract development model that uses
OR/AND
logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum Virtual Machine operation.Proof Of Concept:
The optimizer was turned on and set to 10000 runs
ERC1967Upgrade.sol#L61: [9 Gas Saved (PoC is given below)]
Gas Report:
[G-02] No need
return
keyword for gas efficient [~10 / ~2000 gas saved]Context:
MetadataRenderer.sol#L77-L78
MetadataRenderer.sol#L83-L84
MetadataRenderer.sol#L317-L319
MetadataRenderer.sol#L322-L324
MetadataRenderer.sol#L327-L329
MetadataRenderer.sol#L332-L334
MetadataRenderer.sol#L337-L339
EIP712.sol#L58-L60
EIP712.sol#L63-L65
EIP712.sol#L68-L70
Treasury.sol#L68-L70
Recommendation:
You must remove the
return
keyword from the specified contexts.Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
MetadataRenderer.sol#L317-L319: [~2000 Gas saved (PoC is given below)]
Gas Report:
[G-03] Shifting cheaper than division [146 gas per instance]
Context:
ERC721Votes.sol#L95
Description:
A division by 2 can be calculated by shifting one to the right. While the
DIV
opcode uses5 gas
, theSHR
opcode only uses3 gas
Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.Recommendation:
Instead of dividing by
2
use>> 1
.like this:
Proof Of Concept:
The optimizer was turned on and set to 10000 runs
Gas Report
[G-04] Using
uint256
instead ofbool
in mappings is more gas efficient [146 gas per instance]Context:
GovernorStorageV1.sol#L19
ERC721.sol#L38
Description:
OpenZeppelin uint256 and bool comparison :
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.The values being non-zero value makes deployment a bit more expensive.
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from ‘false’ to ‘true’, after having been ‘true’ in the past.
Recommendation:
Use uint256(1) and uint256(2) instead of bool.
Proof Of Concept:
The optimizer was turned on and set to 10000 runs
Gas Report
[G-05] Use assembly to write address storage values [33 gas per instance]
Context:
Auction.sol#L41
Manager.sol#L62-L66
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report
[G-06] Functions guaranteed to revert when callled by normal users can be marked
payable
[24 gas per instance]Context:
Treasury.sol#L180
Treasury.sol#L116
Governor.sol#L605
Governor.sol#L596
Governor.sol#L588
Governor.sol#L580
Governor.sol#L572
Governor.sol#L564
Auction.sol#L331
Auction.sol#L323
Auction.sol#L315
Auction.sol#L307
Auction.sol#L263
Auction.sol#L244
Description:
If a function modifier or require such as onlyOwner-admin is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2), which costs an average of about 24 gas per call to the function, in addition to the extra deployment cost.
Recommendation:
Functions guaranteed to revert when called by normal users can be marked payable (for only
onlyowner or admin
functions)Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report
[G-07] Function ordering via method ID [22 gas per instance]
Context:
All Contracts
Description:
Contracts most called functions could simply save gas by function ordering via
Method ID
. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because22 gas
are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.Recommendation:
Find a lower
method ID
name for the most called functions for example Call() vs. Call1() is cheaper by22 gas
For example, the function IDs in the
Governor.sol
contract will be the most used; A lower method ID may be given.Proof of Consept:
https://coinsbench.com/advanced-gas-optimizations-tips-for-solidity-85c47f413dc5
Governor.sol function names can be named and sorted according to METHOD ID
[G-08]
x += y
costs more gas thanx = x + y
for state variables [16 gas per instance]Context:
Governor.sol#L280
Governor.sol#L285
Governor.sol#L290
Token.sol#L88
Token.sol#L118
MetadataRenderer.sol#L140
Description:
x += y
costs more gas thanx = x + y
for state variables.Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report
[G-09] Using
private
rather thanpublic
forconstans
, saves gas [7 gas per instance]Context:
Governor.sol#L27
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report:
[G-10] Use assembly to check for
address(0)
[6 gas per instance]Context:
Auction.sol#L104
Governor.sol#L70-L71
Governor.sol#L597
Treasury.sol#L48
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report:
[G-11] Using
>
instead of>=
is more gas efficient [3 gas per instance]Context:
Governor.sol#L261
Token.sol#L88
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
use for GasTest: Governor.sol#L261
Gas Report:
[G-12] Direct writing of variables with constant definition saves gas [~500 gas]
Context:
Auction.sol#L41
Description:
WETH is a wrap Ether contract with a specific address in the Etherum network, giving the option to define it may cause false recognition, it is healthier to define it directly.
Advantages of defining a specific contract directly:
WETH Address : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Recommendation:
[G-13] Use Inline Assembly for contract balance [2.4k gas per instance]
Context:
Auction.sol#L346
Description:
You can use
balance(address)
instead ofaddress.balance()
when getting an contract’s balance of ETH.Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report
[G-14] Using
private
rather thanpublic
for constants, saves gas [10 gas per instance]Context:
Governor.sol#L27
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report:
[G-15] Storing the latest NFT Id in a
uint256
instead of awhile
loop saves gas [114k gas per instance]Context:
Token.sol#L132
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report:
[G-16] Gas optimization in WETH
use with WETH3074
[20k gas]Context:
Auction.sol#L28
Description:
WETH9
was written for 0.4.18 (2017!) and we are at 0.8.17 today, so WETH9 works with a very old and high gas consuming system.WETH3074
is a new version of the WETH contract which utilizesEIP-3074
to avoid the need for wrapping/unwrapping. In essence it turns ETH into an ERC-20 compatible token.https://github.com/axic/weth3074
Proof Of Concept:
https://twitter.com/alexberegszaszi/status/1568261578138451972?t=So9apBBgB54GZsiQFMcjsQ&s=19
[G-17] Extra importing increases deployment gas
Context:
Token.sol#L6-L7
Description:
In
Token.sol
file, ERC721Votes and ERC721 contracts are imported, but ERC721Votes has already imported ERC721 contract.Therefore, there is no need to import ERC721.
Recommendation:
[G-18] Deploy the contract with
clone
instead ofnew
Context:
Manager.sol#L126-L129
Description:
There’s a way to save a significant amount of gas on deployment using Clones:
https://www.youtube.com/watch?v=3Mw-pMmJ7TA .
It is extremely inexpensive to distribute by Cloneing with Create2.
Gas usage difference between the two? (answer: a new clone is 10x cheaper)
[G-19] It is more efficient to use
abi.encodePacked
instead ofabi.encode
[164 gas saved]Context:
Governor.sol#L104
Treasury.sol#L107
MetadataRenderer.sol#L251
Description:
It is more efficient to use
abi.encodePacked
instead ofabi.encode
.When are these used?
abi.encode:
abi.encode encode its parameters using the ABI specs. The ABI was designed to make calls to contracts. Parameters are padded to 32 bytes. If you are making calls to a contract you likely have to use abi.encode
If you are dealing with more than one dynamic data type as it prevents collision.
abi.encodePacked:
abi.encodePacked encode its parameters using the minimal space required by the type. Encoding an uint8 it will use 1 byte. It is used when you want to save some space, and not calling a contract.Takes all types of data and any amount of input.
Gas Report:
[G-20] Statements should be checked first, this is a gas-optimized structure
Context:
Governor.sol#L132-L139
Description:
If statements can be placed earlier to reduce gas usage on revert.
Recommendation:
[S-01] Missing
zero-address
check inconstructor
Context:
All Constructors
Description:
Missing checks for zero-addresses may lead to infunctional protocol, if the variable addresses are updated incorrectly. It also wast gas as it requires the redeployment of the contract.
14 results - 7 files
[S-02] Use
v4.8.0 OpenZeppelin ERC721Votes
contractContext:
ERC721Votes.sol
Description:
The upcoming v4.8.0 version of OpenZeppelin provides close to `50% gas
... See the rest this report here
The text was updated successfully, but these errors were encountered: