Platform: Code4rena
Start Date: 22/08/2022
Pot Size: $50,000 USDC
Total HM: 4
Participants: 160
Period: 5 days
Judge: gzeon
Total Solo HM: 2
Id: 155
League: ETH
Rank: 18/160
Findings: 2
Award: $509.93
๐ Selected for report: 2
๐ Solo Findings: 0
๐ Selected for report: IllIllI
Also found by: 0bi, 0x040, 0x1337, 0x1f8b, 0xDjango, 0xNazgul, 0xNineDec, 0xRajeev, 0xSky, 0xSmartContract, 0xbepresent, 0xkatana, 0xmatt, 8olidity, Aymen0909, Bjorn_bug, Bnke0x0, CertoraInc, Ch_301, Chom, CodingNameKiki, Deivitto, DevABDee, DimitarDimitrov, Dravee, ElKu, Funen, GalloDaSballo, GimelSec, Guardian, Haruxe, JC, JansenC, Jeiwan, JohnSmith, KIntern_NA, Lambda, LeoS, Noah3o6, Olivierdem, R2, RaymondFam, Respx, ReyAdmirado, Rohan16, Rolezn, Ruhum, Saintcode_, Sm4rty, SooYa, Soosh, TomJ, Tomo, Trabajo_de_mates, Waze, _Adam, __141345__, ajtra, android69, asutorufos, auditor0517, berndartmueller, bobirichman, brgltd, c3phas, cRat1st0s, carlitox477, catchup, cccz, csanuragjain, d3e4, delfin454000, dipp, djxploit, durianSausage, erictee, exd0tpy, fatherOfBlocks, gogo, hyh, ladboy233, lukris02, mics, mrpathfindr, natzuu, oyc_109, p_crypt0, pashov, pauliax, pfapostol, prasantgupta52, rajatbeladiya, rbserver, ret2basic, rfa, robee, rokinot, rvierdiiev, sach1r0, saian, seyni, shenwilly, sikorico, simon135, sryysryy, sseefried, throttle, tnevler, tonisives, wagmi, xiaoming90, yixxas, z3s, zkhorse, zzzitron
346.8866 USDC - $346.89
Issue | Instances | |
---|---|---|
[Lโ01] | Nouns will not be able to be transferred once the block.number passes type(uint32).max | 2 |
[Lโ02] | Unused/empty receive() /fallback() function | 1 |
[Lโ03] | Missing checks for address(0x0) when assigning values to address state variables | 5 |
Total: 8 instances over 3 issues
Issue | Instances | |
---|---|---|
[Nโ01] | public functions not called by the contract should be declared external instead | 8 |
[Nโ02] | Non-assembly method available | 3 |
[Nโ03] | 2**<n> - 1 should be re-written as type(uint<n>).max | 2 |
[Nโ04] | constant s should be defined rather than using magic numbers | 8 |
[Nโ05] | Use a more recent version of solidity | 3 |
[Nโ06] | Expressions for constant values such as a call to keccak256() , should use immutable rather than constant | 6 |
[Nโ07] | Constant redefined elsewhere | 11 |
[Nโ08] | Lines are too long | 7 |
[Nโ09] | Non-library/interface files should use fixed compiler versions, not floating ones | 4 |
[Nโ10] | Event is missing indexed fields | 21 |
[Nโ11] | Not using the named return variables anywhere in the function is confusing | 8 |
[Nโ12] | Typos | 4 |
Total: 85 instances over 12 issues
block.number
passes type(uint32).max
While this currently equates to ~1260 years, if there's a hard fork which makes block times much more frequent (e.g. to compete with Solana), then this limit may be reached much faster than expected, and transfers and delegations will remain stuck at their existing settings
There are 2 instances of this issue:
File: /contracts/base/ERC721Checkpointable.sol 238 uint32 blockNumber = safe32( 239 block.number, 240 'ERC721Checkpointable::_writeCheckpoint: block number exceeds 32 bits' 241: );
File: /contracts/governance/NounsDAOLogicV2.sol 923: uint32 blockNumber = safe32(blockNumber_, 'NounsDAO::getDynamicQuorumParamsAt: block number exceeds 32 bits');
receive()
/fallback()
functionIf the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth))
). Having no access control on the function means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds
There is 1 instance of this issue:
File: contracts/governance/NounsDAOLogicV2.sol 1030: receive() external payable {}
address(0x0)
when assigning values to address
state variablesThere are 5 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 605: pendingAdmin = newPendingAdmin; 642: vetoer = newVetoer;
File: contracts/governance/NounsDAOLogicV2.sol 807: pendingAdmin = newPendingAdmin; 844: vetoer = newVetoer;
File: contracts/governance/NounsDAOProxy.sol 71: admin = admin_;
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
There are 8 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 174 function propose( 175 address[] memory targets, 176 uint256[] memory values, 177 string[] memory signatures, 178 bytes[] memory calldatas, 179 string memory description 180: ) public returns (uint256) { 649 function _burnVetoPower() public { 650 // Check caller is pendingAdmin and pendingAdmin โ address(0) 651: require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only'); 660: function proposalThreshold() public view returns (uint256) { 668: function quorumVotes() public view returns (uint256) {
File: contracts/governance/NounsDAOLogicV2.sol 184 function propose( 185 address[] memory targets, 186 uint256[] memory values, 187 string[] memory signatures, 188 bytes[] memory calldatas, 189 string memory description 190: ) public returns (uint256) { 851 function _burnVetoPower() public { 852 // Check caller is pendingAdmin and pendingAdmin โ address(0) 853: require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only'); 862: function proposalThreshold() public view returns (uint256) { 1002: function maxQuorumVotes() public view returns (uint256) {
assembly{ id := chainid() }
=> uint256 id = block.chainid
, assembly { size := extcodesize() }
=> uint256 size = address().code.length
There are some automated tools that will flag a project as having higher complexity if there is inline-assembly, so it's best to avoid using it where it's not necessary
There are 3 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 285: chainId := chainid()
File: contracts/governance/NounsDAOLogicV1.sol 679: chainId := chainid()
File: contracts/governance/NounsDAOLogicV2.sol 1013: chainId := chainid()
2**<n> - 1
should be re-written as type(uint<n>).max
Earlier versions of solidity can use uint<n>(-1)
instead. Expressions not including the - 1
can often be re-written to accomodate the change (e.g. by using a >
rather than a >=
, which will also save some gas)
There are 2 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 254: require(n < 2**32, errorMessage); 259: require(n < 2**96, errorMessage);
constant
s should be defined rather than using magic numbersEven assembly can benefit from using readable constants instead of hex/numeric literals
There are 8 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol /// @audit 32 254: require(n < 2**32, errorMessage); /// @audit 96 259: require(n < 2**96, errorMessage);
File: contracts/governance/NounsDAOLogicV1.sol /// @audit 10000 673: return (number * bps) / 10000;
File: contracts/governance/NounsDAOLogicV2.sol /// @audit 10000 908: uint256 againstVotesBPS = (10000 * againstVotes) / totalSupply; /// @audit 1e6 909: uint256 quorumAdjustmentBPS = (params.quorumCoefficient * againstVotesBPS) / 1e6; /// @audit 10000 1007: return (number * bps) / 10000;
File: contracts/governance/NounsDAOProxy.sol /// @audit 0x20 98: revert(add(returnData, 0x20), returndatasize()) /// @audit 0x40 113: let free_mem_ptr := mload(0x40)
Use a solidity version of at least 0.8.12 to get string.concat()
to be used instead of abi.encodePacked(<str>,<str>)
There are 3 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 35: pragma solidity ^0.8.6;
File: contracts/governance/NounsDAOLogicV1.sol 61: pragma solidity ^0.8.6;
File: contracts/governance/NounsDAOLogicV2.sol 53: pragma solidity ^0.8.6;
keccak256()
, should use immutable
rather than constant
While it doesn't save any gas because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant
variables and immutable
variables, and they should each be used in their appropriate contexts. constants
should be used for literal values written into the code, and immutable
variables should be used for expressions, or values calculated in, or passed into the constructor.
There are 6 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 59 bytes32 public constant DOMAIN_TYPEHASH = 60: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); 63 bytes32 public constant DELEGATION_TYPEHASH = 64: keccak256('Delegation(address delegatee,uint256 nonce,uint256 expiry)');
File: contracts/governance/NounsDAOLogicV1.sol 97 bytes32 public constant DOMAIN_TYPEHASH = 98: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); 101: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
File: contracts/governance/NounsDAOLogicV2.sol 101 bytes32 public constant DOMAIN_TYPEHASH = 102: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); 105: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant
in a library
. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.
There are 11 instances of this issue:
File: contracts/governance/NounsDAOLogicV2.sol /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 59: string public constant name = 'Nouns DAO'; /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 62: uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1; // 1 basis point or 0.01% /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 65: uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000; // 1,000 basis points or 10% /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 68: uint256 public constant MIN_VOTING_PERIOD = 5_760; // About 24 hours /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 71: uint256 public constant MAX_VOTING_PERIOD = 80_640; // About 2 weeks /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 74: uint256 public constant MIN_VOTING_DELAY = 1; /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 77: uint256 public constant MAX_VOTING_DELAY = 40_320; // About 1 week /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 89: uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20% /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 92: uint256 public constant proposalMaxOperations = 10; // 10 actions /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 101 bytes32 public constant DOMAIN_TYPEHASH = 102: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); /// @audit seen in contracts/governance/NounsDAOLogicV1.sol 105: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length
There are 7 instances of this issue:
File: contracts/governance/NounsDAOInterfaces.sol 156: /// @notice The basis point number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed. *DIFFERS from GovernerBravo 181: /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo 256: /// @notice The basis point number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed. *DIFFERS from GovernerBravo 281: /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo 375: /// @notice The minimum number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo
File: contracts/governance/NounsDAOLogicV1.sol 507: /// @notice: Unlike GovernerBravo, votes are considered from the block the proposal was created in order to normalize quorumVotes and proposalThreshold metrics
File: contracts/governance/NounsDAOLogicV2.sol 599: /// @notice: Unlike GovernerBravo, votes are considered from the block the proposal was created in order to normalize quorumVotes and proposalThreshold metrics
There are 4 instances of this issue:
File: contracts/governance/NounsDAOInterfaces.sol 33: pragma solidity ^0.8.6;
File: contracts/governance/NounsDAOLogicV1.sol 61: pragma solidity ^0.8.6;
File: contracts/governance/NounsDAOLogicV2.sol 53: pragma solidity ^0.8.6;
File: contracts/governance/NounsDAOProxy.sol 36: pragma solidity ^0.8.6;
indexed
fieldsIndex event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event
should use three indexed
fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.
There are 21 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 73: event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
File: contracts/governance/NounsDAOInterfaces.sol 37 event ProposalCreated( 38 uint256 id, 39 address proposer, 40 address[] targets, 41 uint256[] values, 42 string[] signatures, 43 bytes[] calldatas, 44 uint256 startBlock, 45 uint256 endBlock, 46 string description 47: ); 50 event ProposalCreatedWithRequirements( 51 uint256 id, 52 address proposer, 53 address[] targets, 54 uint256[] values, 55 string[] signatures, 56 bytes[] calldatas, 57 uint256 startBlock, 58 uint256 endBlock, 59 uint256 proposalThreshold, 60 uint256 quorumVotes, 61 string description 62: ); 70: event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 votes, string reason); 73: event ProposalCanceled(uint256 id); 76: event ProposalQueued(uint256 id, uint256 eta); 79: event ProposalExecuted(uint256 id); 82: event ProposalVetoed(uint256 id); 85: event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay); 88: event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod); 91: event NewImplementation(address oldImplementation, address newImplementation); 94: event ProposalThresholdBPSSet(uint256 oldProposalThresholdBPS, uint256 newProposalThresholdBPS); 97: event QuorumVotesBPSSet(uint256 oldQuorumVotesBPS, uint256 newQuorumVotesBPS); 100: event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); 103: event NewAdmin(address oldAdmin, address newAdmin); 106: event NewVetoer(address oldVetoer, address newVetoer); 111: event MinQuorumVotesBPSSet(uint16 oldMinQuorumVotesBPS, uint16 newMinQuorumVotesBPS); 114: event MaxQuorumVotesBPSSet(uint16 oldMaxQuorumVotesBPS, uint16 newMaxQuorumVotesBPS); 117: event QuorumCoefficientSet(uint32 oldQuorumCoefficient, uint32 newQuorumCoefficient); 120: event RefundableVote(address indexed voter, uint256 refundAmount, bool refundSent); 123: event Withdraw(uint256 amount, bool sent);
Consider changing the variable to be an unnamed one
There are 8 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol /// @audit targets /// @audit values /// @audit signatures /// @audit calldatas 392 function getActions(uint256 proposalId) 393 external 394 view 395 returns ( 396 address[] memory targets, 397 uint256[] memory values, 398 string[] memory signatures, 399: bytes[] memory calldatas
File: contracts/governance/NounsDAOLogicV2.sol /// @audit targets /// @audit values /// @audit signatures /// @audit calldatas 403 function getActions(uint256 proposalId) 404 external 405 view 406 returns ( 407 address[] memory targets, 408 uint256[] memory values, 409 string[] memory signatures, 410: bytes[] memory calldatas
There are 4 instances of this issue:
File: /contracts/governance/NounsDAOLogicV1.sol /// @audit contructor 104: * @notice Used to initialize the contract during delegator contructor /// @audit priviledges 646: * @notice Burns veto priviledges
File: /contracts/governance/NounsDAOLogicV2.sol /// @audit contructor 115: * @notice Used to initialize the contract during delegator contructor /// @audit priviledges 848: * @notice Burns veto priviledges
#0 - gzeoneth
2022-09-30T15:47:30Z
lgtm
๐ Selected for report: IllIllI
Also found by: 0x040, 0x1f8b, 0xDjango, 0xNazgul, 0xNineDec, 0xSmartContract, 0xbepresent, 0xc0ffEE, 0xkatana, 2997ms, ACai, Amithuddar, Aymen0909, Ben, BipinSah, Bjorn_bug, Bnke0x0, CertoraInc, Ch_301, Chom, CodingNameKiki, Deivitto, DevABDee, DimitarDimitrov, Diraco, Dravee, ElKu, EthLedger, Fitraldys, Funen, GalloDaSballo, GimelSec, Guardian, IgnacioB, JC, JohnSmith, Junnon, KIntern_NA, Lambda, LeoS, Noah3o6, Olivierdem, Polandia94, R2, Randyyy, RaymondFam, Respx, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, Ruhum, SaharAP, Saintcode_, SerMyVillage, Shishigami, Sm4rty, SooYa, TomJ, Tomio, Tomo, Waze, Yiko, _Adam, __141345__, a12jmx, ajtra, ak1, bobirichman, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, catchup, ch0bu, d3e4, delfin454000, djxploit, durianSausage, erictee, exolorkistis, fatherOfBlocks, francoHacker, gogo, hyh, ignacio, jag, joestakey, karanctf, ladboy233, lucacez, lukris02, m_Rassska, martin, medikko, mics, mrpathfindr, natzuu, newfork01, oyc_109, pauliax, peritoflores, pfapostol, prasantgupta52, rbserver, ret2basic, rfa, robee, rokinot, rotcivegaf, rvierdiiev, sach1r0, saian, samruna, seyni, shark, shr1ftyy, sikorico, simon135, sryysryy, tay054, tnevler, wagmi, zishansami
163.0429 USDC - $163.04
Issue | Instances | |
---|---|---|
[Gโ01] | State checks unnecessarily re-fetch Proposal s | 5 |
[Gโ02] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , where appropriate | 1 |
[Gโ03] | Structs can be packed into fewer storage slots | 3 |
[Gโ04] | Using calldata instead of memory for read-only arguments in external functions saves gas | 10 |
[Gโ05] | Using storage instead of memory for structs/arrays saves gas | 1 |
[Gโ06] | State variables should be cached in stack variables rather than re-reading them from storage | 11 |
[Gโ07] | Multiple accesses of a mapping/array should use a local variable cache | 2 |
[Gโ08] | internal functions only called once can be inlined to save gas | 7 |
[Gโ09] | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if -statement | 1 |
[Gโ10] | <array>.length should not be looked up in every loop of a for -loop | 8 |
[Gโ11] | ++i /i++ should be unchecked{++i} /unchecked{i++} when it is not possible for them to overflow, as is the case when used in for - and while -loops | 8 |
[Gโ12] | require() /revert() strings longer than 32 bytes cost extra gas | 86 |
[Gโ13] | Optimize names to save gas | 5 |
[Gโ14] | Use a more recent version of solidity | 1 |
[Gโ15] | ++i costs less gas than i++ , especially when it's used in for -loops (--i /i-- too) | 10 |
[Gโ16] | Splitting require() statements that use && saves gas | 19 |
[Gโ17] | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 1 |
[Gโ18] | Using private rather than public for constants, saves gas | 31 |
[Gโ19] | Don't compare boolean expressions to boolean literals | 2 |
[Gโ20] | Division by two should use bit shifting | 2 |
[Gโ21] | require() or revert() statements that check input arguments should be at the top of the function | 3 |
[Gโ22] | Empty blocks should be removed or emit something | 1 |
[Gโ23] | Use custom errors rather than revert() /require() strings to save gas | 95 |
Total: 313 instances over 23 issues
Proposal
sEvery call to state()
fetches the Proposal
storage variable, which is fetched again immediately afterwards by the caller. If instead there were a version of state()
that took in a Proposal storage
variable, the proposal could be fetched only once, saving the gas of the mapping lookup
There are 5 instances of this issue:
File: /contracts/governance/NounsDAOLogicV2.sol 347 require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); 348 349: Proposal storage proposal = _proposals[proposalId]; 286 require( 287 state(proposalId) == ProposalState.Succeeded, 288 'NounsDAO::queue: proposal can only be queued if it is succeeded' 289 ); 290: Proposal storage proposal = _proposals[proposalId]; 324 require( 325 state(proposalId) == ProposalState.Queued, 326 'NounsDAO::execute: proposal can only be executed if it is queued' 327 ); 328: Proposal storage proposal = _proposals[proposalId]; 377 require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal'); 378 379: Proposal storage proposal = _proposals[proposalId]; 593 require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed'); 594 require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type'); 595: Proposal storage proposal = _proposals[proposalId];
address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
, where appropriateSaves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
There is 1 instance of this issue:
File: contracts/base/ERC721Checkpointable.sol 53 mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; 54 55 /// @notice The number of checkpoints for each account 56: mapping(address => uint32) public numCheckpoints;
Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings
There are 3 instances of this issue:
File: contracts/governance/NounsDAOInterfaces.sol /// @audit Variable ordering with 15 slots instead of the current 16: /// uint256(32):id, uint256(32):proposalThreshold, uint256(32):quorumVotes, uint256(32):eta, address[](32):targets, uint256[](32):values, string[](32):signatures, bytes[](32):calldatas, uint256(32):startBlock, uint256(32):endBlock, uint256(32):forVotes, uint256(32):againstVotes, uint256(32):abstainVotes, mapping(32):receipts, address(20):proposer, bool(1):canceled, bool(1):vetoed, bool(1):executed 174 struct Proposal { 175 /// @notice Unique id for looking up a proposal 176 uint256 id; 177 /// @notice Creator of the proposal 178 address proposer; 179 /// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo 180 uint256 proposalThreshold; 181 /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo 182 uint256 quorumVotes; 183 /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds 184 uint256 eta; 185 /// @notice the ordered list of target addresses for calls to be made 186 address[] targets; 187 /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made 188 uint256[] values; 189 /// @notice The ordered list of function signatures to be called 190 string[] signatures; 191 /// @notice The ordered list of calldata to be passed to each call 192 bytes[] calldatas; 193 /// @notice The block at which voting begins: holders must delegate their votes prior to this block 194 uint256 startBlock; 195 /// @notice The block at which voting ends: votes must be cast prior to this block 196 uint256 endBlock; 197 /// @notice Current number of votes in favor of this proposal 198 uint256 forVotes; 199 /// @notice Current number of votes in opposition to this proposal 200 uint256 againstVotes; 201 /// @notice Current number of votes for abstaining for this proposal 202 uint256 abstainVotes; 203 /// @notice Flag marking whether the proposal has been canceled 204 bool canceled; 205 /// @notice Flag marking whether the proposal has been vetoed 206 bool vetoed; 207 /// @notice Flag marking whether the proposal has been executed 208 bool executed; 209 /// @notice Receipts of ballots for the entire set of voters 210 mapping(address => Receipt) receipts; 211: } /// @audit Variable ordering with 17 slots instead of the current 18: /// uint256(32):id, uint256(32):proposalThreshold, uint256(32):quorumVotes, uint256(32):eta, address[](32):targets, uint256[](32):values, string[](32):signatures, bytes[](32):calldatas, uint256(32):startBlock, uint256(32):endBlock, uint256(32):forVotes, uint256(32):againstVotes, uint256(32):abstainVotes, mapping(32):receipts, uint256(32):totalSupply, uint256(32):creationBlock, address(20):proposer, bool(1):canceled, bool(1):vetoed, bool(1):executed 274 struct Proposal { 275 /// @notice Unique id for looking up a proposal 276 uint256 id; 277 /// @notice Creator of the proposal 278 address proposer; 279 /// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo 280 uint256 proposalThreshold; 281 /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo 282 uint256 quorumVotes; 283 /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds 284 uint256 eta; 285 /// @notice the ordered list of target addresses for calls to be made 286 address[] targets; 287 /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made 288 uint256[] values; 289 /// @notice The ordered list of function signatures to be called 290 string[] signatures; 291 /// @notice The ordered list of calldata to be passed to each call 292 bytes[] calldatas; 293 /// @notice The block at which voting begins: holders must delegate their votes prior to this block 294 uint256 startBlock; 295 /// @notice The block at which voting ends: votes must be cast prior to this block 296 uint256 endBlock; 297 /// @notice Current number of votes in favor of this proposal 298 uint256 forVotes; 299 /// @notice Current number of votes in opposition to this proposal 300 uint256 againstVotes; 301 /// @notice Current number of votes for abstaining for this proposal 302 uint256 abstainVotes; 303 /// @notice Flag marking whether the proposal has been canceled 304 bool canceled; 305 /// @notice Flag marking whether the proposal has been vetoed 306 bool vetoed; 307 /// @notice Flag marking whether the proposal has been executed 308 bool executed; 309 /// @notice Receipts of ballots for the entire set of voters 310 mapping(address => Receipt) receipts; 311 /// @notice The total supply at the time of proposal creation 312 uint256 totalSupply; 313 /// @notice The block at which this proposal was created 314 uint256 creationBlock; 315: } /// @audit Variable ordering with 12 slots instead of the current 13: /// uint256(32):id, uint256(32):proposalThreshold, uint256(32):quorumVotes, uint256(32):eta, uint256(32):startBlock, uint256(32):endBlock, uint256(32):forVotes, uint256(32):againstVotes, uint256(32):abstainVotes, uint256(32):totalSupply, uint256(32):creationBlock, address(20):proposer, bool(1):canceled, bool(1):vetoed, bool(1):executed 368 struct ProposalCondensed { 369 /// @notice Unique id for looking up a proposal 370 uint256 id; 371 /// @notice Creator of the proposal 372 address proposer; 373 /// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo 374 uint256 proposalThreshold; 375 /// @notice The minimum number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo 376 uint256 quorumVotes; 377 /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds 378 uint256 eta; 379 /// @notice The block at which voting begins: holders must delegate their votes prior to this block 380 uint256 startBlock; 381 /// @notice The block at which voting ends: votes must be cast prior to this block 382 uint256 endBlock; 383 /// @notice Current number of votes in favor of this proposal 384 uint256 forVotes; 385 /// @notice Current number of votes in opposition to this proposal 386 uint256 againstVotes; 387 /// @notice Current number of votes for abstaining for this proposal 388 uint256 abstainVotes; 389 /// @notice Flag marking whether the proposal has been canceled 390 bool canceled; 391 /// @notice Flag marking whether the proposal has been vetoed 392 bool vetoed; 393 /// @notice Flag marking whether the proposal has been executed 394 bool executed; 395 /// @notice The total supply at the time of proposal creation 396 uint256 totalSupply; 397 /// @notice The block at which this proposal was created 398 uint256 creationBlock; 399: }
calldata
instead of memory
for read-only arguments in external
functions saves gasWhen a function with a memory
array is called externally, the abi.decode()
step has to use a for-loop to copy each index of the calldata
to the memory
index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length
). Using calldata
directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory
arguments, it's still valid for implementation contracs to use calldata
arguments instead.
If the array is passed to an internal
function which passes the array to another internal function where the array is modified and therefore memory
is used in the external
call, it's still more gass-efficient to use calldata
when the external
function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one
Note that I've also flagged instances where the function is public
but can be marked as external
since it's not called by the contract, and cases where a constructor is involved
There are 10 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol /// @audit targets /// @audit values /// @audit signatures /// @audit calldatas /// @audit description 174 function propose( 175 address[] memory targets, 176 uint256[] memory values, 177 string[] memory signatures, 178 bytes[] memory calldatas, 179 string memory description 180: ) public returns (uint256) {
File: contracts/governance/NounsDAOLogicV2.sol /// @audit targets /// @audit values /// @audit signatures /// @audit calldatas /// @audit description 184 function propose( 185 address[] memory targets, 186 uint256[] memory values, 187 string[] memory signatures, 188 bytes[] memory calldatas, 189 string memory description 190: ) public returns (uint256) {
storage
instead of memory
for structs/arrays saves gasWhen 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. Instead of declearing the variable with the memory
keyword, declaring the variable with the storage
keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory
variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory
, or if the array/struct is being read from another memory
array/struct
There is 1 instance of this issue:
File: contracts/governance/NounsDAOLogicV2.sol 952: DynamicQuorumParamsCheckpoint memory cp = quorumParamsCheckpoints[center];
The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace 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.
There are 11 instances of this issue:
File: /contracts/governance/NounsDAOLogicV2.sol /// @audit proposal.proposer on line 351 352: nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold,
File: /contracts/governance/NounsDAOLogicV1.sol /// @audit proposal.proposer on line 340 341: nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold,
File: contracts/governance/NounsDAOLogicV1.sol /// @audit vetoer on line 364 365: require(msg.sender == vetoer, 'NounsDAO::veto: only vetoer'); /// @audit vetoer on line 638 640: emit NewVetoer(vetoer, newVetoer); /// @audit votingDelay on line 536 538: emit VotingDelaySet(oldVotingDelay, votingDelay); /// @audit votingPeriod on line 552 554: emit VotingPeriodSet(oldVotingPeriod, votingPeriod); /// @audit proposalThresholdBPS on line 570 572: emit ProposalThresholdBPSSet(oldProposalThresholdBPS, proposalThresholdBPS); /// @audit quorumVotesBPS on line 587 589: emit QuorumVotesBPSSet(oldQuorumVotesBPS, quorumVotesBPS); /// @audit proposalCount on line 216 217: Proposal storage newProposal = proposals[proposalCount]; /// @audit proposalCount on line 217 219: newProposal.id = proposalCount;
File: contracts/governance/NounsDAOProxy.sol /// @audit implementation on line 83 85: emit NewImplementation(oldImplementation, implementation);
The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage
or calldata
variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations. Caching an array's struct avoids recalculating the array offsets into memory/calldata
There are 2 instances of this issue:
File: contracts/governance/NounsDAOLogicV2.sol /// @audit quorumParamsCheckpoints[<etc>] on line 935 936: return quorumParamsCheckpoints[len - 1].params; /// @audit quorumParamsCheckpoints[<etc>] on line 967 968: quorumParamsCheckpoints[pos - 1].params = params;
internal
functions only called once can be inlined to save gasNot inlining costs 20 to 40 gas because of two extra JUMP
instructions and additional stack operations needed for function calls.
There are 7 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 294 function queueOrRevertInternal( 295 address target, 296 uint256 value, 297 string memory signature, 298 bytes memory data, 299: uint256 eta 676: function getChainIdInternal() internal view returns (uint256) {
File: contracts/governance/NounsDAOLogicV2.sol 305 function queueOrRevertInternal( 306 address target, 307 uint256 value, 308 string memory signature, 309 bytes memory data, 310: uint256 eta 866: function proposalCreationBlock(Proposal storage proposal) internal view returns (uint256) { 974: function _refundGas(uint256 startGas) internal { 1010: function getChainIdInternal() internal view returns (uint256) {
File: contracts/governance/NounsDAOProxy.sol 94: function delegateTo(address callee, bytes memory data) internal {
unchecked {}
for subtractions where the operands cannot underflow because of a previous require()
or if
-statementrequire(a <= b); x = b - a
=> require(a <= b); unchecked { x = b - a }
There is 1 instance of this issue:
File: contracts/base/ERC721Checkpointable.sol /// @audit require() on line 278 279: return a - b;
<array>.length
should not be looked up in every loop of a for
-loopThe overheads outlined below are PER LOOP, excluding the first loop
MLOAD
(3 gas)CALLDATALOAD
(3 gas)Caching the length changes each of these to a DUP<N>
(3 gas), and gets rid of the extra DUP<N>
needed to store the stack offset
There are 8 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 281: for (uint256 i = 0; i < proposal.targets.length; i++) { 319: for (uint256 i = 0; i < proposal.targets.length; i++) { 346: for (uint256 i = 0; i < proposal.targets.length; i++) { 371: for (uint256 i = 0; i < proposal.targets.length; i++) {
File: contracts/governance/NounsDAOLogicV2.sol 292: for (uint256 i = 0; i < proposal.targets.length; i++) { 330: for (uint256 i = 0; i < proposal.targets.length; i++) { 357: for (uint256 i = 0; i < proposal.targets.length; i++) { 382: for (uint256 i = 0; i < proposal.targets.length; i++) {
++i
/i++
should be unchecked{++i}
/unchecked{i++}
when it is not possible for them to overflow, as is the case when used in for
- and while
-loopsThe unchecked
keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop
There are 8 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 281: for (uint256 i = 0; i < proposal.targets.length; i++) { 319: for (uint256 i = 0; i < proposal.targets.length; i++) { 346: for (uint256 i = 0; i < proposal.targets.length; i++) { 371: for (uint256 i = 0; i < proposal.targets.length; i++) {
File: contracts/governance/NounsDAOLogicV2.sol 292: for (uint256 i = 0; i < proposal.targets.length; i++) { 330: for (uint256 i = 0; i < proposal.targets.length; i++) { 357: for (uint256 i = 0; i < proposal.targets.length; i++) { 382: for (uint256 i = 0; i < proposal.targets.length; i++) {
require()
/revert()
strings longer than 32 bytes cost extra gasEach extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas
There are 86 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 140: require(signatory != address(0), 'ERC721Checkpointable::delegateBySig: invalid signature'); 141: require(nonce == nonces[signatory]++, 'ERC721Checkpointable::delegateBySig: invalid nonce'); 142: require(block.timestamp <= expiry, 'ERC721Checkpointable::delegateBySig: signature expired'); 164: require(blockNumber < block.number, 'ERC721Checkpointable::getPriorVotes: not yet determined');
File: contracts/base/ERC721Enumerable.sol 62: require(index < ERC721.balanceOf(owner), 'ERC721Enumerable: owner index out of bounds'); 77: require(index < ERC721Enumerable.totalSupply(), 'ERC721Enumerable: global index out of bounds');
File: contracts/governance/NounsDAOLogicV1.sol 122: require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once'); 124: require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address'); 125: require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address'); 126 require( 127 votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 128 'NounsDAO::initialize: invalid voting period' 129: ); 130 require( 131 votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 132 'NounsDAO::initialize: invalid voting delay' 133: ); 134 require( 135 proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 136 'NounsDAO::initialize: invalid proposal threshold' 137: ); 138 require( 139 quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS, 140 'NounsDAO::initialize: invalid proposal threshold' 141: ); 187 require( 188 nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold, 189 'NounsDAO::propose: proposer votes below proposal threshold' 190: ); 191 require( 192 targets.length == values.length && 193 targets.length == signatures.length && 194 targets.length == calldatas.length, 195 'NounsDAO::propose: proposal function information arity mismatch' 196: ); 197: require(targets.length != 0, 'NounsDAO::propose: must provide actions'); 198: require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions'); 203 require( 204 proposersLatestProposalState != ProposalState.Active, 205 'NounsDAO::propose: one live proposal per proposer, found an already active proposal' 206: ); 207 require( 208 proposersLatestProposalState != ProposalState.Pending, 209 'NounsDAO::propose: one live proposal per proposer, found an already pending proposal' 210: ); 275 require( 276 state(proposalId) == ProposalState.Succeeded, 277 'NounsDAO::queue: proposal can only be queued if it is succeeded' 278: ); 301 require( 302 !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), 303 'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta' 304: ); 313 require( 314 state(proposalId) == ProposalState.Queued, 315 'NounsDAO::execute: proposal can only be executed if it is queued' 316: ); 336: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); 339 require( 340 msg.sender == proposal.proposer || 341 nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold, 342 'NounsDAO::cancel: proposer above threshold' 343: ); 364: require(vetoer != address(0), 'NounsDAO::veto: veto power burned'); 366: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal'); 422: require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id'); 485: require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature'); 501: require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed'); 502: require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type'); 505: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted'); 530: require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only'); 531 require( 532 newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 533 'NounsDAO::_setVotingDelay: invalid voting delay' 534: ); 546: require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only'); 547 require( 548 newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 549 'NounsDAO::_setVotingPeriod: invalid voting period' 550: ); 563: require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only'); 564 require( 565 newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 566 newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 567 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 568: ); 581: require(msg.sender == admin, 'NounsDAO::_setQuorumVotesBPS: admin only'); 582 require( 583 newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS, 584 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 585: ); 599: require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only'); 617: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only'); 638: require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only'); 651: require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');
File: contracts/governance/NounsDAOLogicV2.sol 133: require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once'); 135: require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address'); 136: require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address'); 137 require( 138 votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 139 'NounsDAO::initialize: invalid voting period' 140: ); 141 require( 142 votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 143 'NounsDAO::initialize: invalid voting delay' 144: ); 145 require( 146 proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 147 'NounsDAO::initialize: invalid proposal threshold bps' 148: ); 197 require( 198 nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold, 199 'NounsDAO::propose: proposer votes below proposal threshold' 200: ); 201 require( 202 targets.length == values.length && 203 targets.length == signatures.length && 204 targets.length == calldatas.length, 205 'NounsDAO::propose: proposal function information arity mismatch' 206: ); 207: require(targets.length != 0, 'NounsDAO::propose: must provide actions'); 208: require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions'); 213 require( 214 proposersLatestProposalState != ProposalState.Active, 215 'NounsDAO::propose: one live proposal per proposer, found an already active proposal' 216: ); 217 require( 218 proposersLatestProposalState != ProposalState.Pending, 219 'NounsDAO::propose: one live proposal per proposer, found an already pending proposal' 220: ); 286 require( 287 state(proposalId) == ProposalState.Succeeded, 288 'NounsDAO::queue: proposal can only be queued if it is succeeded' 289: ); 312 require( 313 !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), 314 'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta' 315: ); 324 require( 325 state(proposalId) == ProposalState.Queued, 326 'NounsDAO::execute: proposal can only be executed if it is queued' 327: ); 347: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); 350 require( 351 msg.sender == proposal.proposer || 352 nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold, 353 'NounsDAO::cancel: proposer above threshold' 354: ); 375: require(vetoer != address(0), 'NounsDAO::veto: veto power burned'); 377: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal'); 433: require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id'); 577: require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature'); 593: require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed'); 594: require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type'); 597: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted'); 622: require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only'); 623 require( 624 newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 625 'NounsDAO::_setVotingDelay: invalid voting delay' 626: ); 638: require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only'); 639 require( 640 newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 641 'NounsDAO::_setVotingPeriod: invalid voting period' 642: ); 655: require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only'); 656 require( 657 newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 658 newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 659 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' 660: ); 674: require(msg.sender == admin, 'NounsDAO::_setMinQuorumVotesBPS: admin only'); 677 require( 678 newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND && 679 newMinQuorumVotesBPS <= MIN_QUORUM_VOTES_BPS_UPPER_BOUND, 680 'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps' 681: ); 682 require( 683 newMinQuorumVotesBPS <= params.maxQuorumVotesBPS, 684 'NounsDAO::_setMinQuorumVotesBPS: min quorum votes bps greater than max' 685: ); 702: require(msg.sender == admin, 'NounsDAO::_setMaxQuorumVotesBPS: admin only'); 705 require( 706 newMaxQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS_UPPER_BOUND, 707 'NounsDAO::_setMaxQuorumVotesBPS: invalid max quorum votes bps' 708: ); 709 require( 710 params.minQuorumVotesBPS <= newMaxQuorumVotesBPS, 711 'NounsDAO::_setMaxQuorumVotesBPS: min quorum votes bps greater than max' 712: ); 727: require(msg.sender == admin, 'NounsDAO::_setQuorumCoefficient: admin only'); 801: require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only'); 819: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only'); 840: require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only'); 853: require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');
File: contracts/governance/NounsDAOProxy.sol 79: require(msg.sender == admin, 'NounsDAOProxy::_setImplementation: admin only'); 80: require(implementation_ != address(0), 'NounsDAOProxy::_setImplementation: invalid implementation address');
public
/external
function names and public
member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
There are 5 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol /// @audit votesToDelegate() 39: abstract contract ERC721Checkpointable is ERC721Enumerable {
File: contracts/governance/NounsDAOInterfaces.sol /// @audit delay(), GRACE_PERIOD(), acceptAdmin(), queuedTransactions(), queueTransaction(), cancelTransaction(), executeTransaction() 402: interface INounsDAOExecutor {
File: contracts/governance/NounsDAOLogicV1.sol /// @audit initialize(), propose(), queue(), execute(), cancel(), veto(), getActions(), getReceipt(), state(), castVote(), castVoteWithReason(), castVoteBySig(), _setVotingDelay(), _setVotingPeriod(), _setProposalThresholdBPS(), _setQuorumVotesBPS(), _setVetoer(), _burnVetoPower(), proposalThreshold(), quorumVotes() 65: contract NounsDAOLogicV1 is NounsDAOStorageV1, NounsDAOEvents {
File: contracts/governance/NounsDAOLogicV2.sol /// @audit initialize(), propose(), queue(), execute(), cancel(), veto(), getActions(), getReceipt(), state(), proposals(), castVote(), castRefundableVote(), castRefundableVoteWithReason(), castVoteWithReason(), castVoteBySig(), _setVotingDelay(), _setVotingPeriod(), _setProposalThresholdBPS(), _setMinQuorumVotesBPS(), _setMaxQuorumVotesBPS(), _setQuorumCoefficient(), _setDynamicQuorumParams(), _withdraw(), _setVetoer(), _burnVetoPower(), proposalThreshold(), quorumVotes(), dynamicQuorumVotes(), getDynamicQuorumParamsAt(), minQuorumVotes(), maxQuorumVotes() 57: contract NounsDAOLogicV2 is NounsDAOStorageV2, NounsDAOEventsV2 {
File: contracts/governance/NounsDAOProxy.sol /// @audit _setImplementation() 40: contract NounsDAOProxy is NounsDAOProxyStorage, NounsDAOEvents {
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining
Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require()
strings
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
There is 1 instance of this issue:
File: contracts/base/ERC721Enumerable.sol 28: pragma solidity ^0.8.0;
++i
costs less gas than i++
, especially when it's used in for
-loops (--i
/i--
too)Saves 5 gas per loop
There are 10 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 216: proposalCount++; 281: for (uint256 i = 0; i < proposal.targets.length; i++) { 319: for (uint256 i = 0; i < proposal.targets.length; i++) { 346: for (uint256 i = 0; i < proposal.targets.length; i++) { 371: for (uint256 i = 0; i < proposal.targets.length; i++) {
File: contracts/governance/NounsDAOLogicV2.sol 226: proposalCount++; 292: for (uint256 i = 0; i < proposal.targets.length; i++) { 330: for (uint256 i = 0; i < proposal.targets.length; i++) { 357: for (uint256 i = 0; i < proposal.targets.length; i++) { 382: for (uint256 i = 0; i < proposal.targets.length; i++) {
require()
statements that use &&
saves gasSee this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by 3 gas
There are 19 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 126 require( 127 votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 128 'NounsDAO::initialize: invalid voting period' 129: ); 130 require( 131 votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 132 'NounsDAO::initialize: invalid voting delay' 133: ); 134 require( 135 proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 136 'NounsDAO::initialize: invalid proposal threshold' 137: ); 138 require( 139 quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS, 140 'NounsDAO::initialize: invalid proposal threshold' 141: ); 191 require( 192 targets.length == values.length && 193 targets.length == signatures.length && 194 targets.length == calldatas.length, 195 'NounsDAO::propose: proposal function information arity mismatch' 196: ); 531 require( 532 newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 533 'NounsDAO::_setVotingDelay: invalid voting delay' 534: ); 547 require( 548 newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 549 'NounsDAO::_setVotingPeriod: invalid voting period' 550: ); 564 require( 565 newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 566 newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 567 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 568: ); 582 require( 583 newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS, 584 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 585: ); 617: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
File: contracts/governance/NounsDAOLogicV2.sol 137 require( 138 votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 139 'NounsDAO::initialize: invalid voting period' 140: ); 141 require( 142 votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 143 'NounsDAO::initialize: invalid voting delay' 144: ); 145 require( 146 proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 147 'NounsDAO::initialize: invalid proposal threshold bps' 148: ); 201 require( 202 targets.length == values.length && 203 targets.length == signatures.length && 204 targets.length == calldatas.length, 205 'NounsDAO::propose: proposal function information arity mismatch' 206: ); 623 require( 624 newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 625 'NounsDAO::_setVotingDelay: invalid voting delay' 626: ); 639 require( 640 newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 641 'NounsDAO::_setVotingPeriod: invalid voting period' 642: ); 656 require( 657 newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 658 newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 659 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' 660: ); 677 require( 678 newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND && 679 newMinQuorumVotesBPS <= MIN_QUORUM_VOTES_BPS_UPPER_BOUND, 680 'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps' 681: ); 819: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadWhen using elements that are smaller than 32 bytes, your contractโs gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html
Each operation involving a uint8
costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8
) as compared to ones involving uint256
, due to the compiler having to clear the higher bits of the memory word before operating on the uint8
, as well as the associated stack operations of doing so. Use a larger size then downcast where needed
There is 1 instance of this issue:
File: contracts/base/ERC721Checkpointable.sol /// @audit uint32 upper 191: upper = center - 1;
private
rather than public
for constants, saves gasIf needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
There are 31 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 41: uint8 public constant decimals = 0; 59 bytes32 public constant DOMAIN_TYPEHASH = 60: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); 63 bytes32 public constant DELEGATION_TYPEHASH = 64: keccak256('Delegation(address delegatee,uint256 nonce,uint256 expiry)');
File: contracts/governance/NounsDAOLogicV1.sol 67: string public constant name = 'Nouns DAO'; 70: uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1; // 1 basis point or 0.01% 73: uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000; // 1,000 basis points or 10% 76: uint256 public constant MIN_VOTING_PERIOD = 5_760; // About 24 hours 79: uint256 public constant MAX_VOTING_PERIOD = 80_640; // About 2 weeks 82: uint256 public constant MIN_VOTING_DELAY = 1; 85: uint256 public constant MAX_VOTING_DELAY = 40_320; // About 1 week 88: uint256 public constant MIN_QUORUM_VOTES_BPS = 200; // 200 basis points or 2% 91: uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20% 94: uint256 public constant proposalMaxOperations = 10; // 10 actions 97 bytes32 public constant DOMAIN_TYPEHASH = 98: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); 101: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
File: contracts/governance/NounsDAOLogicV2.sol 59: string public constant name = 'Nouns DAO'; 62: uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1; // 1 basis point or 0.01% 65: uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000; // 1,000 basis points or 10% 68: uint256 public constant MIN_VOTING_PERIOD = 5_760; // About 24 hours 71: uint256 public constant MAX_VOTING_PERIOD = 80_640; // About 2 weeks 74: uint256 public constant MIN_VOTING_DELAY = 1; 77: uint256 public constant MAX_VOTING_DELAY = 40_320; // About 1 week 80: uint256 public constant MIN_QUORUM_VOTES_BPS_LOWER_BOUND = 200; // 200 basis points or 2% 83: uint256 public constant MIN_QUORUM_VOTES_BPS_UPPER_BOUND = 2_000; // 2,000 basis points or 20% 86: uint256 public constant MAX_QUORUM_VOTES_BPS_UPPER_BOUND = 6_000; // 4,000 basis points or 60% 89: uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20% 92: uint256 public constant proposalMaxOperations = 10; // 10 actions 95: uint256 public constant MAX_REFUND_PRIORITY_FEE = 2 gwei; 98: uint256 public constant REFUND_BASE_GAS = 36000; 101 bytes32 public constant DOMAIN_TYPEHASH = 102: keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)'); 105: bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
if (<x> == true)
=> if (<x>)
, if (<x> == false)
=> if (!<x>)
There are 2 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol 505: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');
File: contracts/governance/NounsDAOLogicV2.sol 597: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');
<x> / 2
is the same as <x> >> 1
. While the compiler uses the SHR
opcode to accomplish both, the version that uses division incurs an overhead of 20 gas due to JUMP
s to and from a compiler utility function that introduces checks which can be avoided by using unchecked {}
around the division by two
There are 2 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 184: uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
File: contracts/governance/NounsDAOLogicV2.sol 951: uint256 center = upper - (upper - lower) / 2;
require()
or revert()
statements that check input arguments should be at the top of the functionChecks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.
There are 3 instances of this issue:
File: contracts/governance/NounsDAOLogicV1.sol /// @audit expensive op on line 501 502: require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type');
File: contracts/governance/NounsDAOLogicV2.sol /// @audit expensive op on line 593 594: require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type'); /// @audit expensive op on line 703 705 require( 706 newMaxQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS_UPPER_BOUND, 707 'NounsDAO::_setMaxQuorumVotesBPS: invalid max quorum votes bps' 708: );
The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the contract is meant to be extended, the contract should be abstract
and the function signatures be added without any default implementation. If the block is an empty if
-statement block to avoid doing subsequent checks in the else-if/else conditions, the else-if/else conditions should be nested under the negation of the if-statement, because they involve different classes of checks, which may lead to the introduction of errors when the code is later modified (if(x){}else if(y){...}else{...}
=> if(!x){if(y){...}else{...}}
). Empty receive()
/fallback() payable
functions that are not used, can be removed to save deployment gas.
There is 1 instance of this issue:
File: contracts/governance/NounsDAOLogicV2.sol 1030: receive() external payable {}
revert()
/require()
strings to save gasCustom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
There are 95 instances of this issue:
File: contracts/base/ERC721Checkpointable.sol 140: require(signatory != address(0), 'ERC721Checkpointable::delegateBySig: invalid signature'); 141: require(nonce == nonces[signatory]++, 'ERC721Checkpointable::delegateBySig: invalid nonce'); 142: require(block.timestamp <= expiry, 'ERC721Checkpointable::delegateBySig: signature expired'); 164: require(blockNumber < block.number, 'ERC721Checkpointable::getPriorVotes: not yet determined'); 254: require(n < 2**32, errorMessage); 259: require(n < 2**96, errorMessage); 269: require(c >= a, errorMessage); 278: require(b <= a, errorMessage);
File: contracts/base/ERC721Enumerable.sol 62: require(index < ERC721.balanceOf(owner), 'ERC721Enumerable: owner index out of bounds'); 77: require(index < ERC721Enumerable.totalSupply(), 'ERC721Enumerable: global index out of bounds');
File: contracts/governance/NounsDAOLogicV1.sol 122: require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once'); 123: require(msg.sender == admin, 'NounsDAO::initialize: admin only'); 124: require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address'); 125: require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address'); 126 require( 127 votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 128 'NounsDAO::initialize: invalid voting period' 129: ); 130 require( 131 votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 132 'NounsDAO::initialize: invalid voting delay' 133: ); 134 require( 135 proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 136 'NounsDAO::initialize: invalid proposal threshold' 137: ); 138 require( 139 quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS, 140 'NounsDAO::initialize: invalid proposal threshold' 141: ); 187 require( 188 nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold, 189 'NounsDAO::propose: proposer votes below proposal threshold' 190: ); 191 require( 192 targets.length == values.length && 193 targets.length == signatures.length && 194 targets.length == calldatas.length, 195 'NounsDAO::propose: proposal function information arity mismatch' 196: ); 197: require(targets.length != 0, 'NounsDAO::propose: must provide actions'); 198: require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions'); 203 require( 204 proposersLatestProposalState != ProposalState.Active, 205 'NounsDAO::propose: one live proposal per proposer, found an already active proposal' 206: ); 207 require( 208 proposersLatestProposalState != ProposalState.Pending, 209 'NounsDAO::propose: one live proposal per proposer, found an already pending proposal' 210: ); 275 require( 276 state(proposalId) == ProposalState.Succeeded, 277 'NounsDAO::queue: proposal can only be queued if it is succeeded' 278: ); 301 require( 302 !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), 303 'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta' 304: ); 313 require( 314 state(proposalId) == ProposalState.Queued, 315 'NounsDAO::execute: proposal can only be executed if it is queued' 316: ); 336: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); 339 require( 340 msg.sender == proposal.proposer || 341 nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold, 342 'NounsDAO::cancel: proposer above threshold' 343: ); 364: require(vetoer != address(0), 'NounsDAO::veto: veto power burned'); 365: require(msg.sender == vetoer, 'NounsDAO::veto: only vetoer'); 366: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal'); 422: require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id'); 485: require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature'); 501: require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed'); 502: require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type'); 505: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted'); 530: require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only'); 531 require( 532 newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 533 'NounsDAO::_setVotingDelay: invalid voting delay' 534: ); 546: require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only'); 547 require( 548 newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 549 'NounsDAO::_setVotingPeriod: invalid voting period' 550: ); 563: require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only'); 564 require( 565 newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 566 newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 567 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 568: ); 581: require(msg.sender == admin, 'NounsDAO::_setQuorumVotesBPS: admin only'); 582 require( 583 newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS, 584 'NounsDAO::_setProposalThreshold: invalid proposal threshold' 585: ); 599: require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only'); 617: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only'); 638: require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only'); 651: require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');
File: contracts/governance/NounsDAOLogicV2.sol 133: require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once'); 134: require(msg.sender == admin, 'NounsDAO::initialize: admin only'); 135: require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address'); 136: require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address'); 137 require( 138 votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, 139 'NounsDAO::initialize: invalid voting period' 140: ); 141 require( 142 votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, 143 'NounsDAO::initialize: invalid voting delay' 144: ); 145 require( 146 proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS, 147 'NounsDAO::initialize: invalid proposal threshold bps' 148: ); 197 require( 198 nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold, 199 'NounsDAO::propose: proposer votes below proposal threshold' 200: ); 201 require( 202 targets.length == values.length && 203 targets.length == signatures.length && 204 targets.length == calldatas.length, 205 'NounsDAO::propose: proposal function information arity mismatch' 206: ); 207: require(targets.length != 0, 'NounsDAO::propose: must provide actions'); 208: require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions'); 213 require( 214 proposersLatestProposalState != ProposalState.Active, 215 'NounsDAO::propose: one live proposal per proposer, found an already active proposal' 216: ); 217 require( 218 proposersLatestProposalState != ProposalState.Pending, 219 'NounsDAO::propose: one live proposal per proposer, found an already pending proposal' 220: ); 286 require( 287 state(proposalId) == ProposalState.Succeeded, 288 'NounsDAO::queue: proposal can only be queued if it is succeeded' 289: ); 312 require( 313 !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), 314 'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta' 315: ); 324 require( 325 state(proposalId) == ProposalState.Queued, 326 'NounsDAO::execute: proposal can only be executed if it is queued' 327: ); 347: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); 350 require( 351 msg.sender == proposal.proposer || 352 nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold, 353 'NounsDAO::cancel: proposer above threshold' 354: ); 375: require(vetoer != address(0), 'NounsDAO::veto: veto power burned'); 376: require(msg.sender == vetoer, 'NounsDAO::veto: only vetoer'); 377: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal'); 433: require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id'); 577: require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature'); 593: require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed'); 594: require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type'); 597: require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted'); 622: require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only'); 623 require( 624 newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, 625 'NounsDAO::_setVotingDelay: invalid voting delay' 626: ); 638: require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only'); 639 require( 640 newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, 641 'NounsDAO::_setVotingPeriod: invalid voting period' 642: ); 655: require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only'); 656 require( 657 newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && 658 newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 659 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' 660: ); 674: require(msg.sender == admin, 'NounsDAO::_setMinQuorumVotesBPS: admin only'); 677 require( 678 newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND && 679 newMinQuorumVotesBPS <= MIN_QUORUM_VOTES_BPS_UPPER_BOUND, 680 'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps' 681: ); 682 require( 683 newMinQuorumVotesBPS <= params.maxQuorumVotesBPS, 684 'NounsDAO::_setMinQuorumVotesBPS: min quorum votes bps greater than max' 685: ); 702: require(msg.sender == admin, 'NounsDAO::_setMaxQuorumVotesBPS: admin only'); 705 require( 706 newMaxQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS_UPPER_BOUND, 707 'NounsDAO::_setMaxQuorumVotesBPS: invalid max quorum votes bps' 708: ); 709 require( 710 params.minQuorumVotesBPS <= newMaxQuorumVotesBPS, 711 'NounsDAO::_setMaxQuorumVotesBPS: min quorum votes bps greater than max' 712: ); 727: require(msg.sender == admin, 'NounsDAO::_setQuorumCoefficient: admin only'); 801: require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only'); 819: require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only'); 840: require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only'); 853: require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only'); 1019: require(n <= type(uint32).max, errorMessage);
File: contracts/governance/NounsDAOProxy.sol 79: require(msg.sender == admin, 'NounsDAOProxy::_setImplementation: admin only'); 80: require(implementation_ != address(0), 'NounsDAOProxy::_setImplementation: invalid implementation address');