Skip to content

Commit

Permalink
Add proposal to add Guardian to Timelock (#2021)
Browse files Browse the repository at this point in the history
* Add proposal to add Guardian to Timelock

* Update comment

* Update deployment and add test script

* Remove deployment file

* Do a full proposal test

* Add one more test

* Create proposal

* Prettify

* Revert forceSkip on buyback
  • Loading branch information
shahthepro authored May 8, 2024
1 parent bbbd918 commit 1d37d30
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 1 deletion.
1 change: 1 addition & 0 deletions brownie/abi/timelock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs": [{"internalType": "address[]","name": "proposers","type": "address[]"},{"internalType": "address[]","name": "executors","type": "address[]"}],"stateMutability": "nonpayable","type": "constructor","name": "constructor"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "bytes32","name": "id","type": "bytes32"},{"indexed": true,"internalType": "uint256","name": "index","type": "uint256"},{"indexed": false,"internalType": "address","name": "target","type": "address"},{"indexed": false,"internalType": "uint256","name": "value","type": "uint256"},{"indexed": false,"internalType": "bytes","name": "data","type": "bytes"}],"name": "CallExecuted","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "bytes32","name": "id","type": "bytes32"},{"indexed": true,"internalType": "uint256","name": "index","type": "uint256"},{"indexed": false,"internalType": "address","name": "target","type": "address"},{"indexed": false,"internalType": "uint256","name": "value","type": "uint256"},{"indexed": false,"internalType": "bytes","name": "data","type": "bytes"},{"indexed": false,"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"indexed": false,"internalType": "uint256","name": "delay","type": "uint256"}],"name": "CallScheduled","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "Cancelled","type": "event"},{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "oldDuration","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "newDuration","type": "uint256"}],"name": "MinDelayChange","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "bytes32","name": "role","type": "bytes32"},{"indexed": true,"internalType": "bytes32","name": "previousAdminRole","type": "bytes32"},{"indexed": true,"internalType": "bytes32","name": "newAdminRole","type": "bytes32"}],"name": "RoleAdminChanged","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "bytes32","name": "role","type": "bytes32"},{"indexed": true,"internalType": "address","name": "account","type": "address"},{"indexed": true,"internalType": "address","name": "sender","type": "address"}],"name": "RoleGranted","type": "event"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "bytes32","name": "role","type": "bytes32"},{"indexed": true,"internalType": "address","name": "account","type": "address"},{"indexed": true,"internalType": "address","name": "sender","type": "address"}],"name": "RoleRevoked","type": "event"},{"inputs": [],"name": "CANCELLER_ROLE","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"stateMutability": "view","type": "function"},{"inputs": [],"name": "DEFAULT_ADMIN_ROLE","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"stateMutability": "view","type": "function"},{"inputs": [],"name": "EXECUTOR_ROLE","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"stateMutability": "view","type": "function"},{"inputs": [],"name": "PROPOSER_ROLE","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"stateMutability": "view","type": "function"},{"inputs": [],"name": "TIMELOCK_ADMIN_ROLE","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "cancel","outputs": [],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "address","name": "target","type": "address"},{"internalType": "uint256","name": "value","type": "uint256"},{"internalType": "bytes","name": "data","type": "bytes"},{"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"internalType": "bytes32","name": "salt","type": "bytes32"}],"name": "execute","outputs": [],"stateMutability": "payable","type": "function"},{"inputs": [{"internalType": "address[]","name": "targets","type": "address[]"},{"internalType": "uint256[]","name": "values","type": "uint256[]"},{"internalType": "bytes[]","name": "payloads","type": "bytes[]"},{"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"internalType": "bytes32","name": "salt","type": "bytes32"}],"name": "executeBatch","outputs": [],"stateMutability": "payable","type": "function"},{"inputs": [],"name": "getMinDelay","outputs": [{"internalType": "uint256","name": "duration","type": "uint256"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "role","type": "bytes32"}],"name": "getRoleAdmin","outputs": [{"internalType": "bytes32","name": "","type": "bytes32"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "getTimestamp","outputs": [{"internalType": "uint256","name": "timestamp","type": "uint256"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "role","type": "bytes32"},{"internalType": "address","name": "account","type": "address"}],"name": "grantRole","outputs": [],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "bytes32","name": "role","type": "bytes32"},{"internalType": "address","name": "account","type": "address"}],"name": "hasRole","outputs": [{"internalType": "bool","name": "","type": "bool"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "address","name": "target","type": "address"},{"internalType": "uint256","name": "value","type": "uint256"},{"internalType": "bytes","name": "data","type": "bytes"},{"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"internalType": "bytes32","name": "salt","type": "bytes32"}],"name": "hashOperation","outputs": [{"internalType": "bytes32","name": "hash","type": "bytes32"}],"stateMutability": "pure","type": "function"},{"inputs": [{"internalType": "address[]","name": "targets","type": "address[]"},{"internalType": "uint256[]","name": "values","type": "uint256[]"},{"internalType": "bytes[]","name": "payloads","type": "bytes[]"},{"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"internalType": "bytes32","name": "salt","type": "bytes32"}],"name": "hashOperationBatch","outputs": [{"internalType": "bytes32","name": "hash","type": "bytes32"}],"stateMutability": "pure","type": "function"},{"inputs": [{"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "isOperation","outputs": [{"internalType": "bool","name": "pending","type": "bool"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "isOperationDone","outputs": [{"internalType": "bool","name": "done","type": "bool"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "isOperationPending","outputs": [{"internalType": "bool","name": "pending","type": "bool"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "bytes32","name": "id","type": "bytes32"}],"name": "isOperationReady","outputs": [{"internalType": "bool","name": "ready","type": "bool"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "address","name": "","type": "address"},{"internalType": "address","name": "","type": "address"},{"internalType": "uint256[]","name": "","type": "uint256[]"},{"internalType": "uint256[]","name": "","type": "uint256[]"},{"internalType": "bytes","name": "","type": "bytes"}],"name": "onERC1155BatchReceived","outputs": [{"internalType": "bytes4","name": "","type": "bytes4"}],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "address","name": "","type": "address"},{"internalType": "address","name": "","type": "address"},{"internalType": "uint256","name": "","type": "uint256"},{"internalType": "uint256","name": "","type": "uint256"},{"internalType": "bytes","name": "","type": "bytes"}],"name": "onERC1155Received","outputs": [{"internalType": "bytes4","name": "","type": "bytes4"}],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "address","name": "","type": "address"},{"internalType": "address","name": "","type": "address"},{"internalType": "uint256","name": "","type": "uint256"},{"internalType": "bytes","name": "","type": "bytes"}],"name": "onERC721Received","outputs": [{"internalType": "bytes4","name": "","type": "bytes4"}],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "bytes32","name": "role","type": "bytes32"},{"internalType": "address","name": "account","type": "address"}],"name": "renounceRole","outputs": [],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "bytes32","name": "role","type": "bytes32"},{"internalType": "address","name": "account","type": "address"}],"name": "revokeRole","outputs": [],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "address","name": "target","type": "address"},{"internalType": "uint256","name": "value","type": "uint256"},{"internalType": "bytes","name": "data","type": "bytes"},{"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"internalType": "bytes32","name": "salt","type": "bytes32"},{"internalType": "uint256","name": "delay","type": "uint256"}],"name": "schedule","outputs": [],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "address[]","name": "targets","type": "address[]"},{"internalType": "uint256[]","name": "values","type": "uint256[]"},{"internalType": "bytes[]","name": "payloads","type": "bytes[]"},{"internalType": "bytes32","name": "predecessor","type": "bytes32"},{"internalType": "bytes32","name": "salt","type": "bytes32"},{"internalType": "uint256","name": "delay","type": "uint256"}],"name": "scheduleBatch","outputs": [],"stateMutability": "nonpayable","type": "function"},{"inputs": [{"internalType": "bytes4","name": "interfaceId","type": "bytes4"}],"name": "supportsInterface","outputs": [{"internalType": "bool","name": "","type": "bool"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "uint256","name": "newDelay","type": "uint256"}],"name": "updateDelay","outputs": [],"stateMutability": "nonpayable","type": "function"},{"stateMutability": "payable","type": "receive"}]
122 changes: 122 additions & 0 deletions brownie/scripts/test_multisig_as_governor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from world import *
from brownie import chain

def main():
with TemporaryFork():
calldata = vault_oeth_admin.setRedeemFeeBps.encode_input(0)

print("Creating a test proposal on Timelock...")
# Check if multisig can create proposals on Timelock
tx = timelock_contract.schedule(
OETH_VAULT, # Target
0, # Value
calldata,
"",
"",
24 * 60 * 60, # delay
{'from': GOV_MULTISIG}
)
tx.info()

print("Waiting until it's ready for execution...")
# Fast forward to execute
chain.sleep(24 * 60 * 60 + 10)

print("Executing...")
# Test execution
tx = timelock_contract.execute(
OETH_VAULT, # Target
0, # Value
calldata,
"",
"",
{'from': GOV_MULTISIG}
)
tx.info()

if vault_oeth_admin.redeemFeeBps() != 0:
raise Exception("Action not updated")

print("All Good!")

print("-------------------------------------------")
with TemporaryFork():
calldata = vault_oeth_admin.setRedeemFeeBps.encode_input(1)

print("\n\nCreating another test proposal on Timelock...")
tx = timelock_contract.schedule(
OETH_VAULT, # Target
0, # Value
calldata,
"",
"",
24 * 60 * 60, # delay
{'from': GOV_MULTISIG}
)
tx.info()

action_hash = timelock_contract.hashOperation(
OETH_VAULT, # Target
0, # Value
calldata,
"",
""
)

print("Cancelling tx...")
# Make sure the multisig can cancel txs as well
tx = timelock_contract.cancel(action_hash, {'from': GOV_MULTISIG})
tx.info()

if timelock_contract.isOperation(action_hash):
raise Exception("Failed to cancel op")

print("All Good!")

print("-------------------------------------------")
with TemporaryFork():
print("\n\nMaking sure that it's possible to grant role...")
calldata = timelock_contract.grantRole.encode_input(
# keccak256("TIMELOCK_ADMIN_ROLE")
"0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1",
"0x0000000000000000000000000000000000000011"
)

print("\n\nCreating another test proposal on Timelock...")
tx = timelock_contract.schedule(
TIMELOCK, # Target
0, # Value
calldata,
"",
"",
24 * 60 * 60, # delay
{'from': GOV_MULTISIG}
)
tx.info()

print("Waiting until it's ready for execution...")
# Fast forward to execute
chain.sleep(24 * 60 * 60 + 10)

print("Executing...")
# Test execution
tx = timelock_contract.execute(
TIMELOCK, # Target
0, # Value
calldata,
"",
"",
{'from': GOV_MULTISIG}
)
tx.info()

if not timelock_contract.hasRole(
"0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1",
"0x0000000000000000000000000000000000000011"
):
raise Exception("Failed to grant roles")

print("All Good!")



1 change: 1 addition & 0 deletions brownie/world.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def load_contract(name, address):
gova = brownie.accounts.at(GOVERNOR, force=True)
governor = load_contract('governor', GOVERNOR)
governor_five = load_contract('governor_five', GOVERNOR_FIVE)
timelock_contract = load_contract('timelock', TIMELOCK)
rewards_source = load_contract('rewards_source', REWARDS_SOURCE)


Expand Down
9 changes: 9 additions & 0 deletions contracts/contracts/interfaces/ITimelockController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity ^0.8.0;

interface ITimelockController {
function grantRole(bytes32 role, address account) external;

function revokeRole(bytes32 role, address account) external;

function renounceRole(bytes32 role, address account) external;
}
50 changes: 50 additions & 0 deletions contracts/deploy/mainnet/094_add_multisig_to_timelock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const addresses = require("../../utils/addresses");
const { deploymentWithGovernanceProposal } = require("../../utils/deploy");

module.exports = deploymentWithGovernanceProposal(
{
deployName: "094_add_multisig_to_timelock",
forceDeploy: false,
// forceSkip: true,
// onlyOnFork: true, // this is only executed in forked environment
reduceQueueTime: true, // just to solve the issue of later active proposals failing
proposalId:
"76565349657922140684589379459208028078547529916278436872532296348233972897503",
},
async ({ ethers }) => {
const PROPOSER_ROLE =
"0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1"; // keccak256("PROPOSER_ROLE");
const EXECUTOR_ROLE =
"0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63"; // keccak256("EXECUTOR_ROLE");
const CANCELLER_ROLE =
"0xfd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f783"; // keccak256("CANCELLER_ROLE");
const cTimelock = await ethers.getContractAt(
"ITimelockController",
addresses.mainnet.Timelock
);

return {
name: "Add Guardian Multisig to Timelock\n\
\n\
Part of the OGN/OGV merger. This proposal adds the Origin's 5 of 8 Multisig to the Timelock to provide a backup governance during the transition between governance systems during the token merger. This permission will be revoked once the merger is complete. \
",
actions: [
{
contract: cTimelock,
signature: "grantRole(bytes32,address)",
args: [PROPOSER_ROLE, addresses.mainnet.Guardian],
},
{
contract: cTimelock,
signature: "grantRole(bytes32,address)",
args: [EXECUTOR_ROLE, addresses.mainnet.Guardian],
},
{
contract: cTimelock,
signature: "grantRole(bytes32,address)",
args: [CANCELLER_ROLE, addresses.mainnet.Guardian],
},
],
};
}
);
3 changes: 2 additions & 1 deletion contracts/deployments/mainnet/.migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,6 @@
"090_disable_compound": 1711469659,
"091_simplified_oeth_vault": 1714138519,
"092_woeth_ccip_zapper": 1714111493,
"093_disable_frxeth_strategies": 1714495720
"093_disable_frxeth_strategies": 1714495720,
"094_add_multisig_to_timelock": 1715176032
}

0 comments on commit 1d37d30

Please sign in to comment.