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] Using storage instead of memory for struct saves gas
Context:
contracts\JB721TieredGovernance.sol:
156: JBTiered721SetTierDelegatesDatamemory_data;contracts\JBTiered721Delegate.sol:
272// Get a reference to the data being iterated on.273: JBTiered721MintReservesForTiersDatamemory_data=_mintReservesForTiersData[_i];299// Get a reference to the data being iterated on.300: JBTiered721MintForTiersDatamemory_data=_mintForTiersData[_i];348// Record the added tiers in the store.349: uint256[]memory_tierIdsAdded=store.recordAddTiers(_tiersToAdd);437// Get a reference to the project's current funding cycle.438: JBFundingCyclememory_fundingCycle=fundingCycleStore.currentOf(projectId);439447// Record the minted reserves for the tier.448: uint256[]memory_tokenIds=store.recordMintReservesFor(_tierId,_count);557// Keep a reference to the the specific tier IDs to mint.558: uint16[]memory_tierIdsToMint;
Description:
When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read.
[G-03] Functions guaranteed to revert when callled by normal users can be marked payable [24 gas per instance]
Context:
contracts\JBTiered721Delegate.sol:
320*/321: functionadjustTiers(JB721TierParams[]calldata_tiersToAdd,uint256[]calldata_tierIdsToRemove)externaloverrideonlyOwner322{366*/367: functionsetDefaultReservedTokenBeneficiary(address_beneficiary)externaloverrideonlyOwner{368// Set the beneficiary.382*/383: functionsetBaseUri(stringmemory_baseUri)externaloverrideonlyOwner{384// Store the new value.398*/399: functionsetContractUri(stringcalldata_contractUri)externaloverrideonlyOwner{400// Store the new value.414*/415: functionsetTokenUriResolver(IJBTokenUriResolver_tokenUriResolver)externaloverrideonlyOwner{416// Store the new value.476*/477: functionmintFor(uint16[]memory_tierIds,address_beneficiary)publicoverrideonlyOwner478returns(uint256[]memorytokenIds)
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 21 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-04] x -= y (x += y) costs more gas than x = x – y (x = x + y) for state variables [32 gas per instance]
Context:
contracts\JBTiered721DelegateStore.sol:
353// Increment the total supply with the amount used already.354: supply+=_storedTier.initialQuantity-_storedTier.remainingQuantity;355408// Add the tier's voting units.409: units+=_balance*_storedTierOf[_nft][_i].votingUnits;410505// Get a reference to the account's balance in this tier.506: balance+=tierBalanceOf[_nft][_owner][_i];507533for(uint256_i;_i<_numberOfTokenIds;){534: weight+=_storedTierOf[_nft][tierIdOfToken(_tokenIds[_i])].contributionFloor;535562// Add the tier's contribution floor multiplied by the quantity minted.563: weight+=564(_storedTier.contributionFloor*826// Increment the number of reserved tokens minted.827: numberOfReservesMintedFor[msg.sender][_tierId]+=_count;828contracts\libraries\JBIpfsDecoder.sol:
51for(uint256j=0;j<digitlength;++j){52: carry+=uint256(digits[j])*256;53digits[j]=uint8(carry%58);
Description: x -= y costs more gas than x = x – y for state variables.
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
[G-05] Optimize names to save gas [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 JBTiered721Delegate.sol contract will be the most used; A lower method ID may be given.
[G-06] Use assembly to write address storage values [33 gas per instance]
Context:
contracts\JBTiered721Delegate.sol:
369*/370: functionsetDefaultReservedTokenBeneficiary(address_beneficiary)externaloverrideonlyOwner{371// Set the beneficiary.385*/386: functionsetBaseUri(stringmemory_baseUri)externaloverrideonlyOwner{387// Store the new value.401*/402: functionsetContractUri(stringcalldata_contractUri)externaloverrideonlyOwner{403// Store the new value.417*/418: functionsetTokenUriResolver(IJBTokenUriResolver_tokenUriResolver)externaloverrideonlyOwner{419// Store the new value.
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
[G-07] The solady Library's Ownable contract is significantly gas-optimized, which can be used
Description:
The project uses the onlyOwner authorization model with the PendingOwnable.sol contract. I recommend using Solady's highly gas optimized contract.
Description:
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check of msg.value == 0 and saves 13 gas on deployment with no security risks.
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
payable
x -= y (x += y)
costs more gas thanx = x – y (x = x + y)
for state variablesaddress(0)
payable
Total 14 issues
Suggestions
v4.8.0 OpenZeppelin
contractszero-address
check inconstructor
Total 2 suggestions
[G-01] Changing state variables to Immutable is gas-optimized
Context:
Description:
Changing state variables to immutable is ~16k gas-optimized in terms of deployment cost.
Gas Report:
Deployment
[G-02] Using storage instead of memory for struct saves gas
Context:
Description:
When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read.
[G-03] Functions guaranteed to revert when callled by normal users can be marked
payable
[24 gas per instance]Context:
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 21 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-04]
x -= y (x += y)
costs more gas thanx = x – y (x = x + y)
for state variables [32 gas per instance]Context:
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-05] Optimize names to save gas [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
JBTiered721Delegate.sol
contract will be the most used; A lower method ID may be given.Proof of Consept:
https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92
JBTiered721Delegate.sol function names can be named and sorted according to METHOD ID
[G-06] Use
assembly
to write address storage values [33 gas per instance]Context:
Proof Of Concept:
The optimizer was turned on and set to 10000 runs.
Gas Report:
[G-07] The solady Library's Ownable contract is significantly gas-optimized, which can be used
Description:
The project uses the
onlyOwner
authorization model with thePendingOwnable.sol
contract. I recommend using Solady's highly gas optimized contract.https://github.com/Vectorized/solady/blob/main/src/auth/OwnableRoles.sol
[G-08] Setting the constructor to
payable
[13 gas per instance]Context:
Description:
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check of
msg.value == 0
and saves13 gas
on deployment with no security risks.Recommendation:
Set the constructor to
payable
Proof Of Concept:
https://forum.openzeppelin.com/t/a-collection-of-gas-optimisation-tricks/19966/5?u=pcaversaccio
The optimizer was turned on and set to 10000 runs
Gas Report
[S-01] Use
v4.8.0 OpenZeppelin
contractsDescription:
The upcoming v4.8.0 version of OpenZeppelin provides many small gas optimizations.
https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v4.8.0-rc.0
[S-02] Missing
zero-address
check inconstructor
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.
The text was updated successfully, but these errors were encountered: