code-423n4 / 2022-10-juicebox-findings

2 stars 0 forks source link

Gas Optimizations #224

Open code423n4 opened 1 year ago

code423n4 commented 1 year ago

Gas Optimizations List

Number Optimization Details Context
[G-01] Changing state variables to Immutable is gas-optimized 2
[G-02] Using storage instead of memory for struct saves gas 7
[G-03] Functions guaranteed to revert when callled by normal users can be marked payable 6
[G-04] x -= y (x += y) costs more gas than x = x – y (x = x + y) for state variables 7
[G-05] Optimize names to save gas All contracts
[G-06] Use assembly to check for address(0) 4
[G-07] The solady Library's Ownable contract is significantly gas-optimized, which can be used 
[G-08] Setting the constructor to payable 3

Total 14 issues

Suggestions

Number Suggestion Details
[S-01] Use v4.8.0 OpenZeppelin contracts
[S-02] Missing zero-address check in constructor

Total 2 suggestions

[G-01] Changing state variables to Immutable is gas-optimized

Context:

contracts\abstract\JB721Delegate.sol:
   
   56:   uint256 public override projectId;
   57  

   62:   IJBDirectory public override directory;
   63  

Description: Changing state variables to immutable is ~16k gas-optimized in terms of deployment cost.

Gas Report: Deployment

╭──────────────────────────────────────┬─────────────────╮
│ src/test/test.sol:Contract0 contract ┆                 ┆
╞══════════════════════════════════════╪═════════════════╡
│ Deployment Cost                      ┆ Deployment Size ┆
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 49587                                ┆ 172             ┆
╰──────────────────────────────────────┴─────────────────╯
╭──────────────────────────────────────┬─────────────────╮
│ src/test/test.sol:Contract1 contract ┆                 ┆
╞══════════════════════════════════════╪═════════════════╡
│ Deployment Cost                      ┆ Deployment Size ┆
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 33514                                ┆ 213             ┆
╰──────────────────────────────────────┴─────────────────╯

[G-02] Using storage instead of memory for struct saves gas

Context:

contracts\JB721TieredGovernance.sol:

  156:     JBTiered721SetTierDelegatesData memory _data;

contracts\JBTiered721Delegate.sol:

  272        // Get a reference to the data being iterated on.
  273:       JBTiered721MintReservesForTiersData memory _data = _mintReservesForTiersData[_i];

  299        // Get a reference to the data being iterated on.
  300:       JBTiered721MintForTiersData memory _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:     JBFundingCycle memory _fundingCycle = fundingCycleStore.currentOf(projectId);
  439  

  447      // 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:   function adjustTiers(JB721TierParams[] calldata _tiersToAdd, uint256[] calldata _tierIdsToRemove) external override onlyOwner
  322    {

  366    */
  367:   function setDefaultReservedTokenBeneficiary(address _beneficiary) external override onlyOwner {
  368      // Set the beneficiary.

  382    */
  383:   function setBaseUri(string memory _baseUri) external override onlyOwner {
  384      // Store the new value.

  398    */
  399:   function setContractUri(string calldata _contractUri) external override onlyOwner {
  400      // Store the new value.

  414    */
  415:   function setTokenUriResolver(IJBTokenUriResolver _tokenUriResolver) external override onlyOwner {
  416      // Store the new value.

  476    */
  477:   function mintFor(uint16[] memory _tierIds, address _beneficiary) public override onlyOwner
  478      returns (uint256[] memory tokenIds)

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.

contract GasTest is DSTest {
    Contract0 c0;
    Contract1 c1;

    function setUp() public {
        c0 = new Contract0();
        c1 = new Contract1();
    }

    function testGas() public {
        c0.foo();
        c1.foo();
    }
}

contract Contract0 {
    uint256 versionNFTDropCollection;

    function foo() external {
        versionNFTDropCollection++;
    }
}

contract Contract1 {
    uint256 versionNFTDropCollection;

    function foo() external payable {
        versionNFTDropCollection++;
    }
}

Gas Report:

╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮
│ src/test/GasTest.t.sol:Contract0 contract ┆                 ┆       ┆        ┆       ┆         │
╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡
│ Deployment Cost                           ┆ Deployment Size ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 44293                                     ┆ 252             ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                             ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ foo                                       ┆ 22308           ┆ 22308 ┆ 22308  ┆ 22308 ┆ 1       │
╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮
│ src/test/GasTest.t.sol:Contract1 contract ┆                 ┆       ┆        ┆       ┆         │
╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡
│ Deployment Cost                           ┆ Deployment Size ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 41893                                     ┆ 240             ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                             ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ foo                                       ┆ 22284           ┆ 22284 ┆ 22284  ┆ 22284 ┆ 1       │
╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯

[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;
  355  

  408          // Add the tier's voting units.
  409:         units += _balance * _storedTierOf[_nft][_i].votingUnits;
  410  

  505        // Get a reference to the account's balance in this tier.
  506:       balance += tierBalanceOf[_nft][_owner][_i];
  507  

  533      for (uint256 _i; _i < _numberOfTokenIds; ) {
  534:       weight += _storedTierOf[_nft][tierIdOfToken(_tokenIds[_i])].contributionFloor;
  535  

  562        // 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;
  828  

contracts\libraries\JBIpfsDecoder.sol:
  51        for (uint256 j = 0; j < digitlength; ++j) {
  52:         carry += uint256(digits[j]) * 256;
  53          digits[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.

contract GasTest is DSTest {

    Contract0 c0;
    Contract1 c1;

    function setUp() public {
        c0 = new Contract0();
        c1 = new Contract1();
    }

    function testGas() public {
        c0.swap(2,3);
        c1.swap1(2,3);
    }
}
contract Contract0 {
    uint256 public amountIn = 10;

    function swap(uint256 amountInToBin, uint256 fee)  external returns (uint256 ) {

       return amountIn -= amountInToBin + fee;
    }
}

contract Contract1 {
    uint256 public amountIn = 10;

    function swap1(uint256 amountInToBin, uint256 fee)  external returns (uint256 result1) {
       return (amountIn = amountIn - (amountInToBin + fee));
    }
}

Gas Report:

╭──────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮
│ src/test/test.sol:Contract0 contract ┆                 ┆      ┆        ┆      ┆         │
╞══════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡
│ Deployment Cost                      ┆ Deployment Size ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 83017                                ┆ 341             ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                        ┆ min             ┆ avg  ┆ median ┆ max  ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ swap                                 ┆ 5454            ┆ 5454 ┆ 5454   ┆ 5454 ┆ 1       │
╰──────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
╭──────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮
│ src/test/test.sol:Contract1 contract ┆                 ┆      ┆        ┆      ┆         │
╞══════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡
│ Deployment Cost                      ┆ Deployment Size ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 83017                                ┆ 341             ┆      ┆        ┆      ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                        ┆ min             ┆ avg  ┆ median ┆ max  ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ swap1                                ┆ 5431            ┆ 5431 ┆ 5431   ┆ 5431 ┆ 1       │
╰──────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯

[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.

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

Sighash   |   Function Signature
========================
59283268  =>  _totalRedemptionWeight()
54c6d1f5  =>  firstOwnerOf(uint256)
70a08231  =>  balanceOf(address)
c87b56dd  =>  tokenURI(uint256)
e8a3d485  =>  contractURI()
01ffc9a7  =>  supportsInterface(bytes4)
982e177c  =>  initialize(uint256,IJBDirectory,string,string,IJBFundingCycleStore,string,)
aa35d9c3  =>  mintReservesFor(JBTiered721MintReservesForTiersData[])
980ed8a8  =>  mintFor(JBTiered721MintForTiersData[])
70d4fddb  =>  adjustTiers(JB721TierParams[],uint256[])
df487e26  =>  setDefaultReservedTokenBeneficiary(address)
a0bcfc7f  =>  setBaseUri(string)
ccb4807b  =>  setContractUri(string)
b6bcc40e  =>  setTokenUriResolver(IJBTokenUriResolver)
aa4fb15b  =>  mintReservesFor(uint256,uint256)
6ac6d941  =>  mintFor(uint16[],address)
8c63c8cd  =>  _processPayment(JBDidPayData)
c8fd1720  =>  _didBurn(uint256[])
b9162928  =>  _mintBestAvailableTier(uint256,address,bool)
515dcef0  =>  _mintAll(uint256,uint16[],address)
a8f32397  =>  _redemptionWeightOf(uint256[])
cad3be83  =>  _beforeTokenTransfer(address,address,uint256)
8f811a1c  =>  _afterTokenTransfer(address,address,uint256)
ce650c23  =>  _afterTokenTransferAccounting(address,address,uint256,JB721Tier)

[G-06] Use assembly to write address storage values [33 gas per instance]

Context:

contracts\JBTiered721Delegate.sol:
  369    */
  370:   function setDefaultReservedTokenBeneficiary(address _beneficiary) external override onlyOwner {
  371      // Set the beneficiary.

  385    */
  386:   function setBaseUri(string memory _baseUri) external override onlyOwner {
  387      // Store the new value.

  401    */
  402:   function setContractUri(string calldata _contractUri) external override onlyOwner {
  403      // Store the new value.

  417    */
  418:   function setTokenUriResolver(IJBTokenUriResolver _tokenUriResolver) external override onlyOwner {
  419      // Store the new value.

Proof Of Concept: The optimizer was turned on and set to 10000 runs.

contract GasTestFoundry is DSTest {
    Contract1 c1;
    Contract2 c2;

    function setUp() public {
        c1 = new Contract1();
        c2 = new Contract2();
    }

    function testGas() public {
        c1.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,356);
        c2.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,356);
    }
}

contract Contract1  {
    address rewardToken ;
    uint256 reward;

    function setRewardTokenAndAmount(address token_, uint256 reward_) external {
        rewardToken = token_;
        reward = reward_;
    }
}

contract Contract2  {
    address rewardToken ;
    uint256 reward;

    function setRewardTokenAndAmount(address token_, uint256 reward_) external {
        assembly {
            sstore(rewardToken.slot, token_)
            sstore(reward.slot, reward_)           

        }
    }
}

Gas Report:

╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮
│ src/test/GasTest.t.sol:Contract1 contract ┆                 ┆       ┆        ┆       ┆         │
╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡
│ Deployment Cost                           ┆ Deployment Size ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 50899                                     ┆ 285             ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                             ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ setRewardTokenAndAmount                   ┆ 44490           ┆ 44490 ┆ 44490  ┆ 44490 ┆ 1       │
╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮
│ src/test/GasTest.t.sol:Contract2 contract ┆                 ┆       ┆        ┆       ┆         │
╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡
│ Deployment Cost                           ┆ Deployment Size ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 38087                                     ┆ 221             ┆       ┆        ┆       ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Function Name                             ┆ min             ┆ avg   ┆ median ┆ max   ┆ # calls │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ setRewardTokenAndAmount                   ┆ 44457           ┆ 44457 ┆ 44457  ┆ 44457 ┆ 1       │
╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯

[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.

https://github.com/Vectorized/solady/blob/main/src/auth/OwnableRoles.sol

[G-08] Setting the constructor to payable [13 gas per instance]

Context:

contracts\JBTiered721Delegate.sol:
  184  
  185:   constructor() {
  186      codeOrigin = address(this);

contracts\JBTiered721DelegateDeployer.sol:
  45  
  46:   constructor(
  47      JB721GlobalGovernance _globalGovernance,

contracts\JBTiered721DelegateProjectDeployer.sol:
  46    */
  47:   constructor(
  48      IJBController _controller,

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.

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

contract GasTestFoundry is DSTest {

    Contract1 c1;
    Contract2 c2;

    function setUp() public {
        c1 = new Contract1();
        c2 = new Contract2();
    }

    function testGas() public {
        c1.x();
        c2.x();
    }
}

contract Contract1 {

    uint256 public dummy;
    constructor() payable {
        dummy = 1;
    }

    function x() public {

    }
}

contract Contract2 {

    uint256 public dummy;
    constructor() {
        dummy = 1;
    }

    function x() public {
    }
}

Gas Report

╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮
│ src/test/GasTest.t.sol:Contract1 contract ┆                 ┆     ┆        ┆     ┆         │
╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡
│ Deployment Cost                           ┆ Deployment Size ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 49563                                     ┆ 159             ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤

╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮
│ src/test/GasTest.t.sol:Contract2 contract ┆                 ┆     ┆        ┆     ┆         │
╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡
│ Deployment Cost                           ┆ Deployment Size ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ 49587                                     ┆ 172             ┆     ┆        ┆     ┆         │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤

[S-01] Use v4.8.0 OpenZeppelin contracts

Description: 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

v4.8.0-rc.0
⛽ Many small optimizations

[S-02] Missing zero-address check in constructor

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.

contracts\JBTiered721DelegateDeployer.sol:
  45  
  46:   constructor(
  47:     JB721GlobalGovernance _globalGovernance,
  48:     JB721TieredGovernance _tieredGovernance,
  49:     JBTiered721Delegate _noGovernance
  50:   ) {
  51:     globalGovernance = _globalGovernance;
  52:     tieredGovernance = _tieredGovernance;
  53:     noGovernance = _noGovernance;
  54:   }
  55 
c4-judge commented 1 year ago

Picodes marked the issue as grade-b