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: 1/160
Findings: 3
Award: $10,624.99
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: rbserver
10558.0076 USDC - $10,558.01
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L184-L279 https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L346-L368
When User B calls the following propose
function for creating a proposal, it checks that User B's prior number of votes at the relevant block is larger than the proposal threshold through executing nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold
. This means that User B cannot create the proposal when the prior number of votes and the proposal threshold are the same.
function propose( address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description ) public returns (uint256) { ProposalTemp memory temp; temp.totalSupply = nouns.totalSupply(); temp.proposalThreshold = bps2Uint(proposalThresholdBPS, temp.totalSupply); require( nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold, 'NounsDAO::propose: proposer votes below proposal threshold' ); require( targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, 'NounsDAO::propose: proposal function information arity mismatch' ); require(targets.length != 0, 'NounsDAO::propose: must provide actions'); require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions'); temp.latestProposalId = latestProposalIds[msg.sender]; if (temp.latestProposalId != 0) { ProposalState proposersLatestProposalState = state(temp.latestProposalId); require( proposersLatestProposalState != ProposalState.Active, 'NounsDAO::propose: one live proposal per proposer, found an already active proposal' ); require( proposersLatestProposalState != ProposalState.Pending, 'NounsDAO::propose: one live proposal per proposer, found an already pending proposal' ); } temp.startBlock = block.number + votingDelay; temp.endBlock = temp.startBlock + votingPeriod; proposalCount++; Proposal storage newProposal = _proposals[proposalCount]; newProposal.id = proposalCount; newProposal.proposer = msg.sender; newProposal.proposalThreshold = temp.proposalThreshold; newProposal.eta = 0; newProposal.targets = targets; newProposal.values = values; newProposal.signatures = signatures; newProposal.calldatas = calldatas; newProposal.startBlock = temp.startBlock; newProposal.endBlock = temp.endBlock; newProposal.forVotes = 0; newProposal.againstVotes = 0; newProposal.abstainVotes = 0; newProposal.canceled = false; newProposal.executed = false; newProposal.vetoed = false; newProposal.totalSupply = temp.totalSupply; newProposal.creationBlock = block.number; latestProposalIds[newProposal.proposer] = newProposal.id; /// @notice Maintains backwards compatibility with GovernorBravo events emit ProposalCreated( newProposal.id, msg.sender, targets, values, signatures, calldatas, newProposal.startBlock, newProposal.endBlock, description ); /// @notice Updated event with `proposalThreshold` and `minQuorumVotes` /// @notice `minQuorumVotes` is always zero since V2 introduces dynamic quorum with checkpoints emit ProposalCreatedWithRequirements( newProposal.id, msg.sender, targets, values, signatures, calldatas, newProposal.startBlock, newProposal.endBlock, newProposal.proposalThreshold, minQuorumVotes(), description ); return newProposal.id; }
After User B's proposal is created, User A can call the following cancel
function to cancel it. When calling cancel
, it checks that User B's prior number of votes at the relevant block is less than the proposal threshold through executing nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold
. When User B's prior number of votes and the proposal threshold are the same, User A cannot cancel this proposal of User B. However, this contradicts the fact User B actually cannot create this proposal when the same condition holds true. In other words, if User B cannot create this proposal when the prior number of votes and the proposal threshold are the same, User A should be able to cancel User B's proposal under the same condition but it is not true. The functionality for canceling User B's proposal in this situation becomes unavailable for User A.
function cancel(uint256 proposalId) external { require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); Proposal storage proposal = _proposals[proposalId]; require( msg.sender == proposal.proposer || nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold, 'NounsDAO::cancel: proposer above threshold' ); proposal.canceled = true; for (uint256 i = 0; i < proposal.targets.length; i++) { timelock.cancelTransaction( proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta ); } emit ProposalCanceled(proposalId); }
Please append the following test in the NounsDAOV2#inflationHandling
describe
block in test\governance\NounsDAO\V2\inflationHandling.test.ts
. This test should pass to demonstrate the described scenario.
it("User A cannot cancel User B's proposal when User B's prior number of votes at relevant block is same as proposal threshold, which contradicts the fact that User B actually cannot create the proposal when the prior number of votes is same as proposal threshold", async () => { // account1 has 3 tokens at the beginning // account1 gains 2 more to own 5 tokens in total await token.transferFrom(deployer.address, account1.address, 11); await token.transferFrom(deployer.address, account1.address, 12); await mineBlock(); // account1 cannot create a proposal when owning 5 tokens in total await expect( gov.connect(account1).propose(targets, values, signatures, callDatas, 'do nothing'), ).to.be.revertedWith('NounsDAO::propose: proposer votes below proposal threshold'); // account1 gains 1 more to own 6 tokens in total await token.transferFrom(deployer.address, account1.address, 13); await mineBlock(); // account1 can create a proposal when owning 6 tokens in total await gov.connect(account1).propose(targets, values, signatures, callDatas, 'do nothing'); const proposalId = await gov.latestProposalIds(account1.address); expect(await gov.state(proposalId)).to.equal(0); // other user cannot cancel account1's proposal at this moment await expect( gov.cancel(proposalId, {gasLimit: 1e6}) ).to.be.revertedWith('NounsDAO::cancel: proposer above threshold'); // account1 removes 1 token to own 5 tokens in total await token.connect(account1).transferFrom(account1.address, deployer.address, 13); await mineBlock(); // other user still cannot cancel account1's proposal when account1 owns 5 tokens in total // this contradicts the fact that account1 cannot create a proposal when owning 5 tokens in total await expect( gov.cancel(proposalId, {gasLimit: 1e6}) ).to.be.revertedWith('NounsDAO::cancel: proposer above threshold'); // account1 removes another token to own 4 tokens in total await token.connect(account1).transferFrom(account1.address, deployer.address, 12); await mineBlock(); // other user can now cancel account1's proposal when account1 owns 4 tokens in total await gov.cancel(proposalId, {gasLimit: 1e6}) expect(await gov.state(proposalId)).to.equal(2); });
VSCode
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L197-L200 can be changed to the following code.
require( nouns.getPriorVotes(msg.sender, block.number - 1) >= temp.proposalThreshold, 'NounsDAO::propose: proposer votes below proposal threshold' );
or
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L350-L354 can be changed to the following code.
require( msg.sender == proposal.proposer || nouns.getPriorVotes(proposal.proposer, block.number - 1) <= proposal.proposalThreshold, 'NounsDAO::cancel: proposer above threshold' );
but not both.
#0 - eladmallel
2022-08-30T18:25:32Z
We agree that the case of prior votes equal to proposalThreshold is missed, and plan to include that state in what is cancelable.
🌟 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
50.2965 USDC - $50.30
Posts like https://www.blocknative.com/blog/eip-1559-fees and https://www.liquity.org/blog/how-eip-1559-changes-the-transaction-fees-of-ethereum commonly recommend setting the max priority fee to be 2 gwei or higher to ensure that transactions to be included and executed promptly.
Users who want to vote might need to set the max priority fee to be at least 2 gwei as a common practice but will be likely to get a refund that is less than their actual transaction cost for gas usage due to the MAX_REFUND_PRIORITY_FEE
cap being set to 2 gwei for being used in the following _refundGas
function. Hence, this can discourage users from voting. Please consider increasing MAX_REFUND_PRIORITY_FEE
to a moderately higher amount.
function _refundGas(uint256 startGas) internal { unchecked { uint256 balance = address(this).balance; if (balance == 0) { return; } uint256 gasPrice = min(tx.gasprice, block.basefee + MAX_REFUND_PRIORITY_FEE); uint256 gasUsed = startGas - gasleft() + REFUND_BASE_GAS; uint256 refundAmount = min(gasPrice * gasUsed, balance); (bool refundSent, ) = msg.sender.call{ value: refundAmount }(''); emit RefundableVote(msg.sender, refundAmount, refundSent); } }
Gas cost can change in the future. The hardcoded REFUND_BASE_GAS
, which is used in the _refundGas
function above, can inflate or deflate the gas calculation used for voting when comparing to the actual gas usage in the future. Please consider adding a time-locked function for adjusting REFUND_BASE_GAS
that is only callable by the NounsDAOExecutor
contract.
When calling the following _withdraw
function, sent
returned by msg.sender.call{ value: amount }('')
is not checked. This can cause the failed ETH transfer to revert silently. Please consider checking it to prevent silent failures that can later surprise users unexpectedly.
function _withdraw() external { if (msg.sender != admin) { revert AdminOnly(); } uint256 amount = address(this).balance; (bool sent, ) = msg.sender.call{ value: amount }(''); emit Withdraw(amount, sent); }
To prevent unintended behaviors, the critical address inputs should be checked against address(0)
.
Please consider checking vetoer_
in the following function.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L124-L132
function initialize( address timelock_, address nouns_, address vetoer_, uint256 votingPeriod_, uint256 votingDelay_, uint256 proposalThresholdBPS_, DynamicQuorumParams calldata dynamicQuorumParams_ ) public virtual {
Please consider checking newPendingAdmin
in the following function.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L799-L811
function _setPendingAdmin(address newPendingAdmin) external { // Check caller = admin require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only'); // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); }
Please consider checking newVetoer
in the following function.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L839-L845
function _setVetoer(address newVetoer) public { require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only'); emit NewVetoer(vetoer, newVetoer); vetoer = newVetoer; }
The following executeTransaction
of INounsDAOExecutor
is payable
but the corresponding function is not. Please consider removing payable
from executeTransaction
of INounsDAOExecutor
for improving readability and maintainability.
function executeTransaction( address target, uint256 value, string calldata signature, bytes calldata data, uint256 eta ) external payable returns (bytes memory);
The MAX_QUORUM_VOTES_BPS
constant is no longer used in the NounsDAOLogicV2
contract. Please remove it from the contract for better readability and maintainability.
uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20%
Because the following proposalMaxOperations
is a constant, it can be named using capital letters and underscores by convention, which improves readability and maintainability.
uint256 public constant proposalMaxOperations = 10; // 10 actions
To improve readability and maintainability, constants can be used instead of the magic numbers. Please consider replacing the magic numbers used in the following code with constants.
contracts\base\ERC721Checkpointable.sol 254: require(n < 2**32, errorMessage); 259: require(n < 2**96, errorMessage); contracts\governance\NounsDAOLogicV1.sol 673: return (number * bps) / 10000; contracts\governance\NounsDAOLogicV2.sol 908: uint256 againstVotesBPS = (10000 * againstVotes) / totalSupply; 909: uint256 quorumAdjustmentBPS = (params.quorumCoefficient * againstVotesBPS) / 1e6; 1007: return (number * bps) / 10000;
🌟 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
16.6802 USDC - $16.68
State variables that are accessed for multiple times can be cached in memory to save gas by using mload
instead of sload
.
newProposal.id
can be cached in memory for the following code.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L247-L278
latestProposalIds[newProposal.proposer] = newProposal.id; /// @notice Maintains backwards compatibility with GovernorBravo events emit ProposalCreated( newProposal.id, msg.sender, targets, values, signatures, calldatas, newProposal.startBlock, newProposal.endBlock, description ); /// @notice Updated event with `proposalThreshold` and `minQuorumVotes` /// @notice `minQuorumVotes` is always zero since V2 introduces dynamic quorum with checkpoints emit ProposalCreatedWithRequirements( newProposal.id, msg.sender, targets, values, signatures, calldatas, newProposal.startBlock, newProposal.endBlock, newProposal.proposalThreshold, minQuorumVotes(), description ); return newProposal.id;
proposal.proposer
can be cached in memory for the following code.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L350-L354
require( msg.sender == proposal.proposer || nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold, 'NounsDAO::cancel: proposer above threshold' );
proposal.id
can be cached in memory for the following code.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L466-L469
id: proposal.id, proposer: proposal.proposer, proposalThreshold: proposal.proposalThreshold, quorumVotes: quorumVotes(proposal.id),
proposal.creationBlock
can be cached in memory for the following code.
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L867-L870
if (proposal.creationBlock == 0) { return proposal.startBlock - votingDelay; } return proposal.creationBlock;
Explicitly initializing a variable with its default value costs more gas than uninitializing it. For example, uint256 i
can be used instead of uint256 i = 0
in the following code.
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++) { 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++) {
Caching the array length outside of the loop and using the cached length in the loop costs less gas than reading the array length for each iteration. For example, proposal.targets.length
in the following code can be cached outside of the loop like uint256 proposalTargetsLength = proposal.targets.length
, and i < proposalTargetsLength
can be used for each iteration.
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++) { 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++) {
++variable costs less gas than variable++. For example, i++
can be changed to ++i
in the following code.
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++) { 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++) {
Explicitly unchecking arithmetic operations that do not overflow by wrapping these in unchecked {}
costs less gas than implicitly checking these.
For the following loops, unchecked {++i}
at the end of the loop block can be used, where i
is the counter variable.
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++) { 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++) {
Revert reason strings that are longer than 32 bytes need more memory space and more mstore operation(s) than these that are shorter than 32 bytes when reverting. Please consider shortening the following revert reason strings.
contracts\governance\NounsDAOLogicV1.sol 303: 'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta' 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'); 139: 'NounsDAO::initialize: invalid voting period' 143: 'NounsDAO::initialize: invalid voting delay' 147: 'NounsDAO::initialize: invalid proposal threshold bps' 199: 'NounsDAO::propose: proposer votes below proposal threshold' 205: 'NounsDAO::propose: proposal function information arity mismatch' 207: require(targets.length != 0, 'NounsDAO::propose: must provide actions'); 208: require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions'); 215: 'NounsDAO::propose: one live proposal per proposer, found an already active proposal' 219: 'NounsDAO::propose: one live proposal per proposer, found an already pending proposal' 288: 'NounsDAO::queue: proposal can only be queued if it is succeeded' 314: 'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta' 326: 'NounsDAO::execute: proposal can only be executed if it is queued' 347: require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal'); 353: 'NounsDAO::cancel: proposer above threshold' 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'); 625: 'NounsDAO::_setVotingDelay: invalid voting delay' 638: require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only'); 641: 'NounsDAO::_setVotingPeriod: invalid voting period' 655: require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only'); 659: 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' 674: require(msg.sender == admin, 'NounsDAO::_setMinQuorumVotesBPS: admin only'); 680: 'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps' 684: 'NounsDAO::_setMinQuorumVotesBPS: min quorum votes bps greater than max' 702: require(msg.sender == admin, 'NounsDAO::_setMaxQuorumVotesBPS: admin only'); 707: 'NounsDAO::_setMaxQuorumVotesBPS: invalid max quorum votes bps' 711: 'NounsDAO::_setMaxQuorumVotesBPS: min quorum votes bps greater than max' 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'); 923: uint32 blockNumber = safe32(blockNumber_, 'NounsDAO::getDynamicQuorumParamsAt: block number exceeds 32 bits'); contracts\governance\NounsDAOProxy.sol 79: require(msg.sender == admin, 'NounsDAOProxy::_setImplementation: admin only'); 80: require(implementation_ != address(0), 'NounsDAOProxy::_setImplementation: invalid implementation address');