Nouns DAO contest - 0xDjango'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: 14/160

Findings: 3

Award: $1,117.27

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: TomJ

Also found by: 0xDjango, 0xSmartContract, Aymen0909, Ch_301, Deivitto

Labels

bug
duplicate
2 (Med Risk)
upgraded by judge

Awards

1039.0663 USDC - $1,039.07

External Links

Judge has assessed an item in Issue #264 as Medium risk. The relevant finding follows:

[L-05] Single-step Vetoer transfer is unsafe

If the newVetoer is set to the incorrect address, the functionality will be unretrievable. 1 instance of this issue has been found:

[L-05] NounsDAOLogicV2.sol#L839-L845


function _setVetoer(address newVetoer) public {
    require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only');


    emit NewVetoer(vetoer, newVetoer);


    vetoer = newVetoer;
}

Low Risk Findings Overview

FindingInstances
[L-01]Floating Pragma1
[L-02]Confusing function name “delegates()”1
[L-03]Proposal Threshold is not inclusive1
[L-04]Lack of validation on quorum params setter1
[L-05]Single-step Vetoer transfer is unsafe1
[L-06]Empty fallback()/receive() function1

Non-critical Findings Overview

FindingInstances
[N-01]The use of magic numbers is not recommended2
[N-02]Incorrect spec2
[N-03]Typos2

QA overview per contract

ContractTotal InstancesTotal FindingsLow FindingsLow InstancesNC FindingsNC Instances
NounsDAOLogicV2.sol553322
NounsDAOLogicV1.sol330033
NounsDAOLogicV1.sol221111
ERC721Checkpointable.sol111100
NounsDAOLogicV2.sol111100

Low Risk Findings

[L-01] Floating Pragma

A floating pragma might result in contract being tested/deployed with different compiler versions possibly leading to unexpected behaviour. 1 instance of this issue has been found:

[L-01] NounsDAOLogicV2.sol#L53-L54


pragma solidity ^0.8.6;

[L-02] Confusing function name “delegates()”

It seems confusing to name the function delegates() (plural) when a delegator will only have, at most, 1 delegatee at a time. 1 instance of this issue has been found:

[L-02] ERC721Checkpointable.sol#L88


function delegates(address delegator) public view returns (address) {
    address current = _delegates[delegator];
    return current == address(0) ? delegator : current;
}

[L-03] Proposal Threshold is not inclusive

Proposers with the exact number of votes required by the proposal threshold should not be reverted. 1 instance of this issue has been found:

[L-03] NounsDAOLogicV1.sol#L187-L190


require(
    nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold,
    'NounsDAO::propose: proposer votes below proposal threshold'
);

[L-04] Lack of validation on quorum params setter

The quorum coeefficient should be "a fixed point integer with 6 decimals" but its value is not validated. This could have unexpected effects on the dynamic quorum calculation. 1 instance of this issue has been found:

[L-04] NounsDAOLogicV2.sol#L726


function _setQuorumCoefficient(uint32 newQuorumCoefficient) external {
    require(msg.sender == admin, 'NounsDAO::_setQuorumCoefficient: admin only');
    DynamicQuorumParams memory params = getDynamicQuorumParamsAt(block.number);


    uint32 oldQuorumCoefficient = params.quorumCoefficient;
    params.quorumCoefficient = newQuorumCoefficient;


    _writeQuorumParamsCheckpoint(params);


    emit QuorumCoefficientSet(oldQuorumCoefficient, newQuorumCoefficient);
}

[L-05] Single-step Vetoer transfer is unsafe

If the newVetoer is set to the incorrect address, the functionality will be unretrievable. 1 instance of this issue has been found:

[L-05] NounsDAOLogicV2.sol#L839-L845


function _setVetoer(address newVetoer) public {
    require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only');


    emit NewVetoer(vetoer, newVetoer);


    vetoer = newVetoer;
}

[L-06] Empty fallback()/receive() function

If intended functionality is to make use of ETH the receive() function should implement some operation, if not it should revert the transaction. 1 instance of this issue has been found:

[L-06] NounsDAOLogicV2.sol#L1030-L1031


    receive() external payable {}

Non-critical Findings

[N-01] The use of magic numbers is not recommended

Consider setting constant numbers as a constant variable for better readability and clarity. 2 instances of this issue have been found:

[N-01] NounsDAOLogicV2.sol#L908-L909


        uint256 againstVotesBPS = (10000 * againstVotes) / totalSupply;

[N-01b] NounsDAOLogicV1.sol#L673-L674


        return (number * bps) / 10000;

[N-02] Incorrect spec

Remove or correct the comment so it refelcts the actual and intended functionality of the code. 2 instances of this issue have been found:

[N-02] NounsDAOLogicV2.sol#L851-L856


    function _burnVetoPower() public {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');

        _setVetoer(address(0));
    }

[N-02b] NounsDAOLogicV1.sol#L649-L654


    function _burnVetoPower() public {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');

        _setVetoer(address(0));
    }

[N-03] Typos

Please fix typos. 2 instances of this issue have been found:

[N-03] NounsDAOLogicV1.sol#L46


// - Veto ability which allows `veteor` to halt any proposal at any stage unless -> vetoer

[N-03b] NounsDAOLogicV1.sol#L104-L105


     * @notice Used to initialize the contract during delegator contructor -> constructor

Gas Optimizations

FindingInstances
[G-01]Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate2
[G-02]Cache array.length in require() statement2
[G-03]for loop increments should be unchecked{} if overflow is not possible8
[G-04]array.length should be cached in for loop8
[G-05]Using prefix(++i) instead of postfix (i++) saves gas8
[G-06]Use custom errors rather than revert()/require() strings to save gas2
[G-07]Unnecessary inits to 010
[G-08]require()/revert() checks used multiples times should be turned into a function or modifier4
[G-09]String longer than 32 bytes cost more gas64
[G-10]bool is gas inefficient when used in storage5

Gas overview per contract

Gas Optimizations

[G-01] Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate

Saves 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 operatio 2 instances of this issue have been found:

[G-01] ERC721Checkpointable.sol#L44-L45


    mapping(address => address) private _delegates;

[G-01b] ERC721Checkpointable.sol#L53-L56


mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;

/// @notice The number of checkpoints for each account
mapping(address => uint32) public numCheckpoints;

[G-02] Cache array.length in require() statement

targets.length is used multiple times. Caching it in memory can decrease gas usage from every read. 2 instances of this issue have been found:

[G-02] NounsDAOLogicV2.sol#L201-L208


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

[G-02b] NounsDAOLogicV1.sol#L191-L198


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

[G-03] for loop increments should be unchecked{} if overflow is not possible

From Solidity 0.8.0 onwards using the unchecked keyword saves 30 to 40 gas per loop. Example:

uint length = array.length;
for (uint i; i < length;){
		uncheck { ++i }
}

8 instances of this issue have been found:

[G-03] NounsDAOLogicV2.sol#L382-L383


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03b] NounsDAOLogicV2.sol#L330-L331


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03c] NounsDAOLogicV2.sol#L292-L293


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03d] NounsDAOLogicV1.sol#L371-L372


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03e] NounsDAOLogicV1.sol#L346-L347


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03f] NounsDAOLogicV1.sol#L319-L320


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03g] NounsDAOLogicV1.sol#L281-L282


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-03h] NounsDAOLogicV2.sol#L357-L358


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04] array.length should be cached in for loop

Caching the length array would save gas on each iteration by not having to read from memory or storage multiple times. Example:

uint length = array.length;
for (uint i; i < length;){
		uncheck { ++i }
}

8 instances of this issue have been found:

[G-04] NounsDAOLogicV2.sol#L382-L383


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04b] NounsDAOLogicV2.sol#L330-L331


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04c] NounsDAOLogicV2.sol#L292-L293


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04d] NounsDAOLogicV1.sol#L371-L372


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04e] NounsDAOLogicV1.sol#L346-L347


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04f] NounsDAOLogicV1.sol#L319-L320


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04g] NounsDAOLogicV1.sol#L281-L282


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-04h] NounsDAOLogicV2.sol#L357-L358


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05] Using prefix(++i) instead of postfix (i++) saves gas

It saves 6 gas per iteration. 8 instances of this issue have been found:

[G-05] NounsDAOLogicV2.sol#L382-L383


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05b] NounsDAOLogicV2.sol#L330-L331


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05c] NounsDAOLogicV2.sol#L292-L293


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05d] NounsDAOLogicV1.sol#L371-L372


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05e] NounsDAOLogicV1.sol#L346-L347


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05f] NounsDAOLogicV1.sol#L319-L320


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05g] NounsDAOLogicV1.sol#L281-L282


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-05h] NounsDAOLogicV2.sol#L357-L358


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-06] Use custom errors rather than revert()/require() strings to save gas

Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Only one instance was given but the issue can be attributed to all require()/revert() functions that do not implement custom errors in the contract. 2 instances of this issue have been found:

[G-06] NounsDAOLogicV2.sol#L133-L134


        require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once');

[G-06b] NounsDAOLogicV1.sol#L122-L123


        require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once');

[G-07] Unnecessary inits to 0

These values are set by default. 10 instances of this issue have been found:

[G-07] ERC721Checkpointable.sol#L41-L42


    uint8 public constant decimals = 0;

[G-07b] NounsDAOLogicV2.sol#L382-L383


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07c] NounsDAOLogicV2.sol#L357-L358


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07d] NounsDAOLogicV2.sol#L330-L331


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07e] NounsDAOLogicV2.sol#L292-L293


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07f] NounsDAOLogicV1.sol#L371-L372


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07g] NounsDAOLogicV1.sol#L346-L347


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07h] NounsDAOLogicV1.sol#L319-L320


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07i] NounsDAOLogicV1.sol#L281-L282


        for (uint256 i = 0; i < proposal.targets.length; i++) {

[G-07j] NounsDAOLogicV1.sol#L230-L235


newProposal.forVotes = 0;
newProposal.againstVotes = 0;
newProposal.abstainVotes = 0;
newProposal.canceled = false;
newProposal.executed = false;
newProposal.vetoed = false;

[G-08] require()/revert() checks used multiples times should be turned into a function or modifier

Doing so increases code readability decreases number of instructions for the compiler. 4 instances of this issue have been found:

[G-08] NounsDAOLogicV2.sol#L801


require(msg.sender == admin -> repeated 8 times

[G-08b] NounsDAOLogicV2.sol#L853


require(msg.sender == vetoer -> repeated 3 times

[G-08c] NounsDAOLogicV1.sol#L365


require(msg.sender == vetoer -> repeated 3 times

[G-08d] NounsDAOLogicV1.sol#L599


require(msg.sender == admin -> repeated 6 times

[G-09] String longer than 32 bytes cost more gas

" Each 32 byte chunk of the string requires an extra mstore. That is, additional cost for mstore, memory expansion costs, as well as stack operations. Note that, this runtime cost is only relevant when the revert condition is met." Source 64 instances of this issue have been found:

[G-09] NounsDAOProxy.sol#L80-L81


        require(implementation_ != address(0), 'NounsDAOProxy::_setImplementation: invalid implementation address');

[G-09b] NounsDAOProxy.sol#L79-L80


        require(msg.sender == admin, 'NounsDAOProxy::_setImplementation: admin only');

[G-09c] ERC721Enumerable.sol#L77-L78


        require(index < ERC721Enumerable.totalSupply(), 'ERC721Enumerable: global index out of bounds');

[G-09d] ERC721Enumerable.sol#L62-L63


        require(index < ERC721.balanceOf(owner), 'ERC721Enumerable: owner index out of bounds');

[G-09e] ERC721Checkpointable.sol#L292-L293


 "ERC721Checkpointable::_writeCheckpoint: block number exceeds 32 bits"
        );

[G-09f] ERC721Checkpointable.sol#L277-L278


  "ERC721Checkpointable::_moveDelegates: amount overflows"
                );

[G-09g] ERC721Checkpointable.sol#L264-L265


   "ERC721Checkpointable::_moveDelegates: amount underflows"
                );

[G-09h] ERC721Checkpointable.sol#L201-L204


require(
            blockNumber < block.number,
            "ERC721Checkpointable::getPriorVotes: not yet determined"
        );

[G-09i] ERC721Checkpointable.sol#L163-L174


require(
            signatory != address(0),
            "ERC721Checkpointable::delegateBySig: invalid signature"
        );
        require(
            nonce == nonces[signatory]++,
            "ERC721Checkpointable::delegateBySig: invalid nonce"
        );
        require(
            block.timestamp <= expiry,
            "ERC721Checkpointable::delegateBySig: signature expired"
        );

[G-09j] ERC721Checkpointable.sol#L91-L94


safe96(
                balanceOf(delegator),
                "ERC721Checkpointable::votesToDelegate: amount exceeds 96 bits"
            );

[G-09k] NounsDAOLogicV2.sol#L923-L924


        uint32 blockNumber = safe32(blockNumber_, 'NounsDAO::getDynamicQuorumParamsAt: block number exceeds 32 bits');

[G-09l] NounsDAOLogicV2.sol#L853-L854


        require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');

[G-09m] NounsDAOLogicV2.sol#L840-L841


        require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only');

[G-09n] NounsDAOLogicV2.sol#L819-L820


        require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');

[G-09o] NounsDAOLogicV2.sol#L801-L802


        require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only');

[G-09p] NounsDAOLogicV2.sol#L727-L728


        require(msg.sender == admin, 'NounsDAO::_setQuorumCoefficient: admin only');

[G-09q] NounsDAOLogicV2.sol#L702-L703


        require(msg.sender == admin, 'NounsDAO::_setMaxQuorumVotesBPS: admin only');

[G-09r] NounsDAOLogicV2.sol#L705-L712


require(
            newMaxQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS_UPPER_BOUND,
            'NounsDAO::_setMaxQuorumVotesBPS: invalid max quorum votes bps'
        );
        require(
            params.minQuorumVotesBPS <= newMaxQuorumVotesBPS,
            'NounsDAO::_setMaxQuorumVotesBPS: min quorum votes bps greater than max'
        );

[G-09s] NounsDAOLogicV2.sol#L674-L685


require(msg.sender == admin, 'NounsDAO::_setMinQuorumVotesBPS: admin only');
        DynamicQuorumParams memory params = getDynamicQuorumParamsAt(block.number);

        require(
            newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND &&
                newMinQuorumVotesBPS <= MIN_QUORUM_VOTES_BPS_UPPER_BOUND,
            'NounsDAO::_setMinQuorumVotesBPS: invalid min quorum votes bps'
        );
        require(
            newMinQuorumVotesBPS <= params.maxQuorumVotesBPS,
            'NounsDAO::_setMinQuorumVotesBPS: min quorum votes bps greater than max'
        );

[G-09t] NounsDAOLogicV2.sol#L655-L660


require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only');
        require(
            newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS &&
                newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS,
            'NounsDAO::_setProposalThreshold: invalid proposal threshold bps'
        );

[G-09u] NounsDAOLogicV2.sol#L638-L642


require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only');
        require(
            newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
            'NounsDAO::_setVotingPeriod: invalid voting period'
        );

[G-09v] NounsDAOLogicV2.sol#L623-L626


require(
            newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
            'NounsDAO::_setVotingDelay: invalid voting delay'
        );

[G-09w] NounsDAOLogicV2.sol#L622-L623


        require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only');

[G-09x] NounsDAOLogicV2.sol#L593-L594


require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed');
        require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type');

[G-09y] NounsDAOLogicV2.sol#L597-L598


        require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');

[G-09z] NounsDAOLogicV2.sol#L433-L434


        require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id');

[G-09{] NounsDAOLogicV2.sol#L577-L578


        require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature');

[G-09|] NounsDAOLogicV2.sol#L375-L376


        require(vetoer != address(0), 'NounsDAO::veto: veto power burned');

[G-09}] NounsDAOLogicV2.sol#L377-L378


        require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal');

[G-09~] NounsDAOLogicV2.sol#L347-L348


        require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal');

[G-09] NounsDAOLogicV2.sol#L312-L315


require(
            !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
            'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta'
        );

[G-09€] NounsDAOLogicV2.sol#L286-L289


require(
            state(proposalId) == ProposalState.Succeeded,
            'NounsDAO::queue: proposal can only be queued if it is succeeded'
        );

[G-09] NounsDAOLogicV2.sol#L213-L220


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

[G-09‚] NounsDAOLogicV2.sol#L197-L209


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

[G-09ƒ] NounsDAOLogicV2.sol#L133-L134


        require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once');

[G-09„] NounsDAOLogicV2.sol#L135-L148


require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address');
        require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address');
        require(
            votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
            'NounsDAO::initialize: invalid voting period'
        );
        require(
            votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
            'NounsDAO::initialize: invalid voting delay'
        );
        require(
            proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS,
            'NounsDAO::initialize: invalid proposal threshold bps'
        );

[G-09…] NounsDAOLogicV2.sol#L105-L106


    bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');

[G-09†] NounsDAOLogicV2.sol#L102-L103


        keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

[G-09‡] NounsDAOLogicV1.sol#L651-L652


        require(msg.sender == vetoer, 'NounsDAO::_burnVetoPower: vetoer only');

[G-09ˆ] NounsDAOLogicV1.sol#L638-L639


        require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only');

[G-09‰] NounsDAOLogicV1.sol#L617-L618


        require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');

[G-09Š] NounsDAOLogicV1.sol#L599-L600


        require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only');

[G-09‹] NounsDAOLogicV1.sol#L581-L585


require(msg.sender == admin, 'NounsDAO::_setQuorumVotesBPS: admin only');
        require(
            newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS,
            'NounsDAO::_setProposalThreshold: invalid proposal threshold'
        );

[G-09Œ] NounsDAOLogicV1.sol#L563-L568


require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only');
        require(
            newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS &&
                newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS,
            'NounsDAO::_setProposalThreshold: invalid proposal threshold'
        );

[G-09] NounsDAOLogicV1.sol#L546-L550


require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only');
        require(
            newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
            'NounsDAO::_setVotingPeriod: invalid voting period'
        );

[G-09Ž] NounsDAOLogicV1.sol#L530-L534


require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only');
        require(
            newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
            'NounsDAO::_setVotingDelay: invalid voting delay'
        );

[G-09] NounsDAOLogicV1.sol#L505-L506


        require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');

[G-09] NounsDAOLogicV1.sol#L501-L502


        require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed');
        require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type');

[G-09‘] NounsDAOLogicV1.sol#L485-L486


        require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature');

[G-09’] NounsDAOLogicV1.sol#L422-L423


        require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id');

[G-09“] NounsDAOLogicV1.sol#L366-L367


        require(state(proposalId) != ProposalState.Executed, 'NounsDAO::veto: cannot veto executed proposal');

[G-09”] NounsDAOLogicV1.sol#L364-L365


        require(vetoer != address(0), 'NounsDAO::veto: veto power burned');

[G-09•] NounsDAOLogicV1.sol#L339-L343


require(
            msg.sender == proposal.proposer ||
                nouns.getPriorVotes(proposal.proposer, block.number - 1) < proposal.proposalThreshold,
            'NounsDAO::cancel: proposer above threshold'
        );

[G-09–] NounsDAOLogicV1.sol#L336-L337


        require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal');

[G-09—] NounsDAOLogicV1.sol#L313-L316


require(
            state(proposalId) == ProposalState.Queued,
            'NounsDAO::execute: proposal can only be executed if it is queued'
        );

[G-09˜] NounsDAOLogicV1.sol#L301-L304


require(
            !timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
            'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta'
        );

[G-09™] NounsDAOLogicV1.sol#L275-L278


require(
            state(proposalId) == ProposalState.Succeeded,
            'NounsDAO::queue: proposal can only be queued if it is succeeded'
        );

[G-09š] NounsDAOLogicV1.sol#L203-L210


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

[G-09›] NounsDAOLogicV1.sol#L197-L198


        require(targets.length != 0, 'NounsDAO::propose: must provide actions');
        require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions');

[G-09œ] NounsDAOLogicV1.sol#L191-L196


    require(
            targets.length == values.length &&
                targets.length == signatures.length &&
                targets.length == calldatas.length,
            'NounsDAO::propose: proposal function information arity mismatch'
        );

[G-09] NounsDAOLogicV1.sol#L187-L190


   require(
            nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold,
            'NounsDAO::propose: proposer votes below proposal threshold'
        );

[G-09ž] NounsDAOLogicV1.sol#L124-L141


require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address');
        require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address');
        require(
            votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD,
            'NounsDAO::initialize: invalid voting period'
        );
        require(
            votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY,
            'NounsDAO::initialize: invalid voting delay'
        );
        require(
            proposalThresholdBPS_ >= MIN_PROPOSAL_THRESHOLD_BPS && proposalThresholdBPS_ <= MAX_PROPOSAL_THRESHOLD_BPS,
            'NounsDAO::initialize: invalid proposal threshold'
        );
        require(
            quorumVotesBPS_ >= MIN_QUORUM_VOTES_BPS && quorumVotesBPS_ <= MAX_QUORUM_VOTES_BPS,
            'NounsDAO::initialize: invalid proposal threshold'
        );

[G-09Ÿ] NounsDAOLogicV1.sol#L122-L123


        require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once');

[G-09 ] NounsDAOLogicV1.sol#L101-L102


    bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');

[G-10] bool is gas inefficient when used in storage

Instead use uint256 values to represent true/false instead. Reference 5 instances of this issue have been found:

[G-10] NounsDAOInterfaces.sol#L390-L394


bool canceled;
        /// @notice Flag marking whether the proposal has been vetoed
        bool vetoed;
        /// @notice Flag marking whether the proposal has been executed
        bool executed;

[G-10b] NounsDAOInterfaces.sol#L320-L321


        bool hasVoted;

[G-10c] NounsDAOInterfaces.sol#L304-L308


bool canceled;
        /// @notice Flag marking whether the proposal has been vetoed
        bool vetoed;
        /// @notice Flag marking whether the proposal has been executed
        bool executed;

[G-10d] NounsDAOInterfaces.sol#L215-L220


/// @notice Whether or not a vote has been cast
        bool hasVoted;
        /// @notice Whether or not the voter supports the proposal or abstains
        uint8 support;
        /// @notice The number of votes the voter had, which were cast
        uint96 votes;

[G-10e] NounsDAOInterfaces.sol#L204-L208


bool canceled;
        /// @notice Flag marking whether the proposal has been vetoed
        bool vetoed;
        /// @notice Flag marking whether the proposal has been executed
        bool executed;
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