Platform: Code4rena
Start Date: 22/08/2022
End Date: 27/08/2022
Period: 5 days
Status: Completed
Pot Size: $50,000 USDC
Participants: 160
Reporter: itsmetechjay
Judge: gzeon
Id: 155
League: ETH
rbserver | 1/160 | $10,624.99 | 3 | 0 | 0 | 1 | 1 | - | - | 0 |
Respx | 2/160 | $10,610.11 | 3 | 0 | 0 | 1 | 1 | - | - | 0 |
Lambda | 3/160 | $1,850.85 | 3 | 1 | 0 | 0 | 0 | - | - | 0 |
KIntern_NA | 4/160 | $1,735.39 | 3 | 1 | 0 | 0 | 0 | - | - | 0 |
csanuragjain | 5/160 | $1,718.74 | 2 | 1 | 0 | 0 | 0 | - | 0 | 0 |
cccz | 6/160 | $1,718.74 | 2 | 1 | 0 | 0 | 0 | - | 0 | 0 |
berndartmueller | 7/160 | $1,718.74 | 2 | 1 | 0 | 0 | 0 | - | 0 | 0 |
zzzitron | 8/160 | $1,718.73 | 2 | 1 | 0 | 0 | 0 | - | 0 | 0 |
bin2chen | 9/160 | $1,683.29 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
jayphbee | 10/160 | $1,683.29 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Auditor per page
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.
yarn
yarn test
forge install forge test -vvv --ffi
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.
The focus of this audit is on:
votingDelay
is used (bug report)NounsDAOStorageV2
, NounsDAOStorageV1Adjusted
, NounsDAOProxyStorage
(found in the file NounsDAOInterfaces.sol
)getPriorVotes
and totalSupply
, which are implemented in ERC721Checkpointable
and ERC721Enumerable
respectively_setImplementation
on the DAO proxy with the address of the V2 logic contract_setDynamicQuorumParams
on the DAO proxy to set up dynamic quorum with parameters the DAO decided onKey risks we’d like you to explore:
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.
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 |
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 |
There are 3 skipped hardhat tests it's ok to ignore for this audit:
At this point you should have sufficient context to dive into V2:
NounsDAOLogicV2
and NounsDAOLogicV1
We'll be around the C4 Discord. Please let us know if we can help in any way!
Name | Discord | Timezone | |
---|---|---|---|
elad | elad | ⌐◨-◨#6192 | eladmallel | GMT-5 |
solimander | solimander#7468 | solimander | GMT-6 |