Chicken-or-the-egg problem re: deploying TimelockController and GovernorTimelockControl


I am building a governance system that uses a new ERC20 for voting and delegating and has a timelock on all actions. Our community already has a multisig of trusted stewards, so it would be nice to have the option to give the multisig some extra privileges like canceling a proposal or something, but that's not a must have and not related to the question at hand. I've thoroughly read through the governance docs, the governance contracts and have used the contracts wizard, and it seems obvious that the OS governance contracts, namely GovernorTimelockControl and TimelockController, are exactly what I need. However, I'm finding some confusing notes about those contracts in the docs. I have specific questions which you can see below, but it would also be sufficient just to see a complete generic example of how to use GovernorTimelockControl and TimelockController together (including how they are meant to be deployed in tandem and what to pass in for constructor args). It would also be great to see how I could deploy them in a way that allows our multisig to be a kind of admin that can step in when necessary, but again, not the most pressing issue.


In the docs I see

Setting up the TimelockController to have additional proposers besides the governor is very risky

which I interpret to mean that the GovernorTimelockControl contract should be the only proposer passed into the TimelockController constructor. That implies that the GovernorTimelockControl must be deployed first and TimelockController second. The problem is, the GovernorTimelockControl takes a TimelockController as a constructor arg. Chicken-or-the-egg problem. What's the intended way to deploy these contracts? The TimelockController proposers array seems like it cannot be updated after construction. I do see that GovernorTimelockControl has a function updateTimelock, but only the current timelock can call it, so I still don't understand what to pass in at first.

  • Do I need to use CREATE2 to preocompute the address of the GovernorTimelockControl contract, deploy TimelockController first with that precomputed address in the proposers array, and finally deploy GovernorTimelockControl passing in the deployed TimelockController address? How else could I possibly deploy these contracts together?

  • I'm also wondering what should be passed in as the executors array in addition to the proposers array, and whether it makes sense to include our multisig as a proposer or executor.

I also saw somewhere in the docs

A common use case is to position this TimelockController as the owner of a smart contract, with a multisig or a DAO as the sole proposer.

I'm surprised that the "common use case" has nothing to do with GovernorTimelockControl (GovernorTimelockControl is not Ownable, and as I said above it's not clear how to deploy them such that it is the sole proposer). I would really appreciate an example and more clear explanation about how the two should relate to each other. They seem like they were literally made for each other, and yet they also seem incompatible.

Can someone please help me make sense of this? Much appreciated!

You need to deploy the timelock contract first before the governor contract and also to give admin rights to your multisig to be able to cancel proposers you can add them as executors when deploying the timelock contract

Hi @jgeary and apologies for some of the confusion in the docs. There are some recent updates that I want to mention.

At the end of this warning there is a note that says "This risk will be mitigated in a future release". In fact this mitigation was merged recently and will be released in a few weeks. That said, this warning is about the final setup and does not apply to an intermediate state while you're setting up the system. So what you can do is:

  1. Deploy TimelockController without any proposers. At deployment the deployer account receives an admin role that can be used to add a proposer later (see the TimelockController Roles docs section).
  2. Deploy Governor with GovernorTimelockControl, connected to the timelock that was just deployed.
  3. Add the Governor as a proposer and renounce the timelock admin role from the deployer account.

As a side note, we've built a prototype of an Access Control Explorer that makes it very easy to audit the roles post-deployment.

IMO it definitely makes sense for the multisig to be executor, and with the upcoming mitigations it could also make sense for the multisig to be proposer, but I'm not sure, you should think further about the implications. Another change coming up in the next release is a separate timelock role for cancelling (currently all proposers can cancel, which can lead to DoS situations with multiple proposers), so you can decide if the multisig or governor are able to cancel, but most likely not both.

No this description is precisely that. If you have an upgradeable contract for example, and the owner can upgrade it, the owner would be a TimelockController instance, and this instance would have a GovernorTimelockControl as a proposer.

This is something we've been thinking about and discussing with the folks at Tally who have a closer view of DAO needs and procedures. We don't have a solution out of the box for this but it may be possible to build with our building blocks.

1 Like

Hey @frangio thank you so much, this makes sense. It’s also what my teammate told me after he looked into this for a while, but glad to have confirmation.

Yeah, I was poking around the current main branch last night and noticed the new canceller - this is perfect!

Thanks again.

1 Like