Nouns Governor: a significant design challenge

Intro & TLDR

Since our last update on Nouns Governor we’ve been working on speccing and implementing the design described as “Approach 1: Delegation NFTs”. While working on the proposed design for $nouns and soliciting feedback from Nouners (thanks 9999!) we discovered a flaw in the Nouns Gov design we were working towards.

The flaw enables an attacker to use the voting power of swappable Nouns sitting idle in a contract. We describe it in detail below.

All the solutions we could come up with to mitigate this attack require the Nouner to deposit their Noun(s) in a new contract, which is a requirement we were hoping to avoid.

If we choose to accept this requirement, then we think there’s a simpler design which enables granular delegation, without switching to an NFT based voting approach. We will describe it in a followup post.

Removing checkpoints

When switching to NFT based voting we decided to not use the voting power checkpointing system used today. Voting by NFT eliminates the ability to double vote with the same token because we record each time an NFT is used to vote.

Unfortunately, removing the checkpoints also opens up a new attack vector.

The attack

Setting up the stage:

  • A contract (pool) holds 20 Nouns and enables permissionless swapping.

  • This could be the case with the proposed $nouns contract for example.

  • A Nouns proposal is up for voting.

Attack steps:

  1. The attacker has one Noun; they vote with their Noun.

  2. They swap their Noun with a Noun from the pool; they use the pool Noun to vote as well.

  3. Repeat for all the Nouns available for swapping in the pool.

This enables the attacker to use the voting power of the Nouns in the pool as their own. This attack is not possible today because the Nouns contract keeps checkpoints of the voting power of each account at each block. Unless the attacker buys more Nouns, their swapping with the pool can only give them a single vote, because they can only hold a single Noun per block.
Unfortunately, the Nouns contract does not keep track of which account controlled which Noun in its checkpointing system, and the contract is not upgradeable, so the most straightforward solution - having said tracking native to Nouns token - is not possible. We detail below the alternatives we’ve come up with so far.

Possible mitigations

Checkpointing NDT ownership

One of the ideas we were exploring as a mitigation to this attack was to have checkpoints for Noun Delegation Tokens (NDTs). NDTs are one-to-one NFTs which a Nouner can mint to delegate their voting power of that Noun to someone else. Any time an NDT changes ownership, we would record the block. This would allows us to answer the following question in the contract: was this user the owner of this NDT when this proposal’s voting started?

Unfortunately this does not solve the problem. One could perform a similar sequence to the one above, just before a proposal’s voting starts:

  1. Swap their Noun with a Noun from the pool.

  2. Mint themselves an NDT.

  3. Repeat for all the Nouns in the pool.

This ensures that they will be the owner of all the NDTs at the proposal’s snapshot block. The only scenario in which this wouldn’t be the case is if another attacker backruns them and does the same thing. In any case, the problem remains.

The root of the problem is that we can’t hook into Nouns transfers. And since the Nouns token contract is not upgradeable, we can’t add those hooks either.

Storage proofs

We could use storage proofs to prove that a Noun was owned by a specific account at a specific block; federation.wtf does something similar. Besides the additional complexity of requiring users to generate these proofs, it would probably also require relying on a third party to have access to historic block hashes. This level of complexity is not a good fit for the core governance contracts.

Depositing Nouns in custody

We could keep track of Nouns transfers if we require users to deposit their Nouns into a custody contract. This would allow us to keep ownership info checkpoints at the Noun level, and not just the NDT. For example, we could create a “WrappedNoun” contract (similar to wrapped punks). In this WrappedNoun contract, we could keep track of the owner per block.

The main drawback here is that Nouners would have to deposit their Nouns into a new contract, which is something we were hoping to avoid.

In the solution space of depositing Nouns in custody there’s another possible solution we’re currently exploring. We could have a delegator contract which keeps each of your Nouns in a separate custody wallet. This would allow you to delegate each Noun to a different address. One advantage of this solution is that it doesn’t require any changes in the current governor contracts. We’ll publish another post describing this solution in detail soon.

Next steps

We aren’t happy with any of the solutions we came up with so far. We think this issue is risky enough to be a showstopper for the Nouns Governor design. We will soon publish a design for granular delegation that works with the current governor contract. If you think differently, or if you have other solutions in mind, we’d love to hear from you.

We will start a conversation about this in the new nouns-protocol channel on farcaster. We are experimenting with using rounds.wtf to incentivize and reward good ideas & feedback.

Subscribe to verbs
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.