Governor governing another governor pattern

I am trying to implement the following governance pattern but facing issues so wanted to check if this is supported

We have a GovernorA with TimelockX and GovernorB with TimelockY and would like to have GovernorA with TimelockX be the governance mechanism for the GovernorSettings of GovernorB.
Following the contracts, I can override the _executor() in GovernorB and set it to TimelockX. However when I execute a successful proposal via this route, I get a TimelockController: underlying transaction reverted error back from the TimelockX.execute call. Sample foundry trace below:

    ├─ [0] VM::warp(172802)
    │   └─ ← ()
    ├─ [47462] GovernorA::execute([0x5CF7F96627F3C9903763d128A1cc5D97556A6b99], [0], [0xea0217cf0000000000000000000000000000000000000000000000000000000000127500], 0xb563a2869291343337aaeab3a57ff4c6e38fd571fc1cdd379e3d20a9b063f380)
    │   ├─ [513] TimelockX::isOperationDone(0x7b5fb92a16190479219625855830b94cec8d64039c80942659db74aaae3a939a) [staticcall]
    │   │   └─ ← false
    │   ├─ [562] TimelockX::isOperationPending(0x7b5fb92a16190479219625855830b94cec8d64039c80942659db74aaae3a939a) [staticcall]
    │   │   └─ ← true
    │   ├─ emit ProposalExecuted(proposalId: 42987237326974571082764542944064450636849269689767936086714136449699457781009)
    │   ├─ [15808] TimelockX::executeBatch([0x5CF7F96627F3C9903763d128A1cc5D97556A6b99], [0], [0xea0217cf0000000000000000000000000000000000000000000000000000000000127500], 0x0000000000000000000000000000000000000000000000000000000000000000, 0xb563a2869291343337aaeab3a57ff4c6e38fd571fc1cdd379e3d20a9b063f380)
    │   │   ├─ [5104] GovernorB::setVotingPeriod(1209600)
    │   │   │   └─ ← "Empty()"
    │   │   └─ ← "TimelockController: underlying transaction reverted"
    │   └─ ← "TimelockController: underlying transaction reverted"
    └─ ← "TimelockController: underlying transaction reverted"

In the above example I am trying to GovernorB::setVotingPeriod and I know for a fact this call is successful because if I read the return data of TimelockController._execute() I get the event back for a successful new VotingPeriod set.

I suspect this scenario is not supported but wanted to double check with you. Thanks

I think it depends on your contract, so if possible, you can share your code at here.

It's just the default OZ Governor implementation e.g. both GovernorA and GovernorB have the following inheritance definition. Nothing else is custom.

contract GovernorB is

I think the TimelockX can control GovernorA and GovernorB at the same time, and, always write some test cases for the new changes.

Sorry I don't think you understood the initial question so I've demonstrated with an example, see commit here

to relate this code to the original example,
GovernorA with TimelockX = ZeroExProtocolGovernor with ZeroExTimelock instance for protocol
and GovernorB with TimelockY = ZeroExTreasuryGovernor with ZeroExTimelock instance for treasury.

You can see how the tests for updating the GovernanceSettings in ZeroExTreasuryGovernor fail.

It seems like you add a new variable protocolTimelock in ZeroExTreasuryGovernor.sol, so there are two timelock variables in your ZeroExTreasuryGovernor.sol contract, they are protocolTimelock and _timelock, I do not think it can work as well as you expect, in your description, GovernorA with TimelockX and GovernorB with TimelockY, I think you can call TimelockY. updateTimelock(TimelockX), so then TimelockX can control GovernorA and GovernorB at the same time.

Sorry but you are still not understanding even though I've pushed what you've suggested here and still doesn't work.
Can maybe @Amxx or @frangio comment here?
Basically my question is, can the GovernanceSettings be updatable via a different governor than the one inheriting the settings? This is to enforce different voting period, duration and proposal thresholds than the one in the governor they are part of. :pray:

The reason that this is failing is that the "Settings" module uses the onlyGovernance modifier. This modifier doesn't just check that the sender is _executor, it also has a mechanism to make sure the transactions actually go through the Governor's own governance protocol. This mechanism is what's failing in your transaction trace.

I think your best option is to fork the GovernorSettings contract and use a modifier other than onlyGovernance.

1 Like