Nouns DAO contest - rbserver's results

A DAO-driven NFT project on Ethereum.

General Information

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

Nouns DAO

Findings Distribution

Researcher Performance

Rank: 1/160

Findings: 3

Award: $10,624.99

🌟 Selected for report: 1

🚀 Solo Findings: 1

Findings Information

🌟 Selected for report: rbserver

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

10558.0076 USDC - $10,558.01

External Links

Lines of code

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

Vulnerability details

Impact

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.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L184-L279

    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.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L346-L368

    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);
    }

Proof of Concept

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);
  });

Tools Used

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.

[L-01] SETTING MAX_REFUND_PRIORITY_FEE TO 2 gwei CAN DISCOURAGE USERS FROM VOTING

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.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L974-L986

    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);
        }
    }

[L-02] HARDCODED REFUND_BASE_GAS MIGHT NOT BE FUTURE-PROOF

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.

[L-03] MISSING RETURN VALUE CHECK FOR call()

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.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L783-L792

    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);
    }

[L-04] MISSING ZERO-ADDRESS CHECK FOR CRITICAL ADDRESSES

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;
    }

[N-01] executeTransaction of INounsDAOExecutor IS PAYABLE BUT CORRESPONDING FUNCTION IS NOT

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.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOInterfaces.sol#L427-L433

    function executeTransaction(
        address target,
        uint256 value,
        string calldata signature,
        bytes calldata data,
        uint256 eta
    ) external payable returns (bytes memory);

[N-02] MAX_QUORUM_VOTES_BPS CAN BE REMOVED FROM NounsDAOLogicV2 CONTRACT

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.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L89

    uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20%

[N-03] proposalMaxOperations CAN BE NAMED USING CAPITAL LETTERS AND UNDERSCORES

Because the following proposalMaxOperations is a constant, it can be named using capital letters and underscores by convention, which improves readability and maintainability.

https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L92

uint256 public constant proposalMaxOperations = 10; // 10 actions

[N-04] CONSTANTS CAN BE USED INSTEAD OF MAGIC NUMBERS

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;

[G-01] STATE VARIABLES THAT ARE ACCESSED FOR MULTIPLE TIMES CAN BE CACHED IN MEMORY

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;

[G-02] VARIABLE DOES NOT NEED TO BE INITIALIZED TO ITS DEFAULT VALUE

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++) {

[G-03] ARRAY LENGTH CAN BE CACHED OUTSIDE OF LOOP

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++) {

[G-04] ++VARIABLE CAN BE USED INSTEAD OF VARIABLE++

++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++) {

[G-05] ARITHMETIC OPERATIONS THAT DO NOT OVERFLOW CAN BE UNCHECKED

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++) {

[G-06] REVERT REASON STRINGS CAN BE SHORTENED TO FIT IN 32 BYTES

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');  
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter