Skip to content

code-423n4/2022-08-nounsdao

Repository files navigation

Nouns DAO contest details

About Nouns DAO

Nouns is a generative NFT project on Ethereum, where a new Noun is minted and auctioned off every day, all proceeds go to the DAO treasury, and each token represents one vote. To date more than 350 Nouns have been sold on auction, and the DAO has funded a variety of cool builders, including a recent 500 ETH grant to theprotocolguild.eth.

More intro information can be found on Nouns Center.

Developer guide

Setup

yarn

Running tests

Hardhat tests

yarn test

Forge tests

forge install
forge test -vvv --ffi

Possible Solc error

If you encounter the following error tring to build or run tests:

Solc Error: dyld[32338]: Library not loaded: '/opt/homebrew/opt/z3/lib/libz3.dylib'

Install the z3 library and try again. On MacOs you can run

brew install z3

The dependency on z3 is mentioned in forge build docs.

Audit scope

The focus of this audit is on:

  • The NounsDAOLogicV2 contract, which introduces two new features:
    • Dynamic quorum (spec)
    • Voting gas refund (spec)
    • A fix to how votingDelay is used (bug report)
  • Parent contracts which hold state variables: NounsDAOStorageV2, NounsDAOStorageV1Adjusted, NounsDAOProxyStorage (found in the file NounsDAOInterfaces.sol)
  • NounsToken's functions getPriorVotes and totalSupply, which are implemented in ERC721Checkpointable and ERC721Enumerable respectively
  • The upgrade process from v1 to v2
    • The NounsDAOLogicV1 contract for context
    • The upgrade will take place via a proposal; the proposal will include the following transactions:
      • call _setImplementation on the DAO proxy with the address of the V2 logic contract
      • call _setDynamicQuorumParams on the DAO proxy to set up dynamic quorum with parameters the DAO decided on
    • Proposals that were created on V1 and will remain active post-upgrade, should behave the same (e.g. maintain their quorum setting)
    • More generally, should ensure that there are no hidden storage issues when migrating from V1 → V2

Key risks we’d like you to explore:

  • Bricking the DAO: this upgrade should not result in a non-functional DAO that cannot execute any additional proposals.
  • Security: this upgrade should not introduce any new cost-effective attack vectors.

Clarification on getPriorVotes, votingDelay and proposalCreationBlock

In castVoteInternal there’s a change that fixes how we use votingDelay such that moving forward it can be edited without impacting vote snapshots for any active proposals.

Nouns DAO uses vote snapshots from the block of proposal creation. In V1 proposal creation block was calculated thus: proposal.startBlock - votingDelay in the castVoteInternal function, so if votingDelay was edited, vote counts would become inconsistent with the proposal’s intended snapshot. In V2 the fix is storing the proposals creationBlock on the proposal struct, which removes the dependency on votingDelay.

What might be confusing is the function proposalCreationBlock:

function proposalCreationBlock(Proposal storage proposal) internal view returns (uint256) {
    if (proposal.creationBlock == 0) {
        return proposal.startBlock - votingDelay;
    }
    return proposal.creationBlock;
}

This logic handles the case of proposals that were created using V1 logic, and remain active after the upgrade to V2; such proposals would not have creationBlock set, so we continue to calculate their creation block the same as V1. Based on the current DAO configuration about a week after V2 is deployed all V1 proposals should reach their final state, and from that moment on proposalCreationBlock would rely solely on proposal.creationBlock.

The risky point we’re aware of is that we should not attempt to edit votingDelay during this transition week when V1 proposals are still active.

Files in scope

File blank comment code
contracts/governance/NounsDAOLogicV2.sol 125 276 630
contracts/governance/NounsDAOLogicV1.sol 81 199 403
contracts/governance/NounsDAOInterfaces.sol 66 164 210
contracts/governance/NounsDAOProxy.sol 18 56 67
contracts/base/ERC721Checkpointable.sol 42 82 165
contracts/base/ERC721Enumerable.sol 27 91 69

All other source contracts (not in scope)

File blank comment code
contracts/libs/Inflate.sol 81 171 618
contracts/NounsArt.sol 61 175 214
contracts/NounsDescriptorV2.sol 56 213 201
contracts/base/ERC721.sol 55 202 198
contracts/NounsDescriptor.sol 51 140 160
contracts/SVGRenderer.sol 29 47 154
contracts/NounsAuctionHouse.sol 45 85 131
contracts/governance/NounsDAOExecutor.sol 33 28 128
contracts/NounsToken.sol 45 99 119
contracts/libs/MultiPartRLEToSVG.sol 17 30 111
contracts/interfaces/INounsArt.sol 49 14 89
contracts/interfaces/INounsDescriptorV2.sol 43 14 84
contracts/test/Multicall2.sol 17 7 77
contracts/governance/NounsDAOProxyV2.sol 19 58 67
contracts/test/WETH.sol 16 3 54
contracts/interfaces/INounsDescriptor.sol 40 14 45
contracts/libs/SSTORE2.sol 19 37 44
contracts/test/NounsTokenHarness.sol 8 1 40
contracts/libs/NFTDescriptor.sol 8 21 38
contracts/libs/NFTDescriptorV2.sol 8 21 38
contracts/test/NounsDAOImmutable.sol 6 1 37
contracts/NounsSeeder.sol 7 18 32
contracts/test/MaliciousVoter.sol 6 1 29
contracts/test/NounsDAOLogicV2Harness.sol 4 1 27
contracts/interfaces/INounsAuctionHouse.sol 19 20 26
contracts/test/NounsDAOExecutorHarness.sol 9 1 26
contracts/interfaces/INounsToken.sol 23 14 25
contracts/test/NounsDAOLogicV1Harness.sol 4 1 23
contracts/interfaces/ISVGRenderer.sol 8 14 14
contracts/interfaces/INounsSeeder.sol 6 14 12
contracts/test/MaliciousBidder.sol 4 1 12
contracts/interfaces/INounsDescriptorMinimal.sol 13 20 11
contracts/proxies/NounsAuctionHouseProxy.sol 5 14 9
contracts/Inflator.sol 5 22 8
contracts/interfaces/IWETH.sol 4 1 6
contracts/interfaces/IInflator.sol 5 14 5
contracts/external/opensea/IProxyRegistry.sol 2 1 4
contracts/proxies/NounsAuctionHouseProxyAdmin.sol 5 15 3
  • counted using cloc

Skipped tests

There are 3 skipped hardhat tests it's ok to ignore for this audit:

  • NounsDescriptor
    • should generate valid token uri metadata for all supported parts when data uris are enabled
  • NounsDescriptorV2
    • should generate valid token uri metadata when data uris are enabled [ @skip-on-coverage ]
    • should generate valid token uri metadata for all supported parts when data uris are enabled

How to get context

At this point you should have sufficient context to dive into V2:

Contact info ☎️

We'll be around the C4 Discord. Please let us know if we can help in any way!

Name Discord Twitter Timezone
elad elad | ⌐◨-◨#6192 eladmallel GMT-5
solimander solimander#7468 solimander GMT-6

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •