Skip to content

Commit 971e010

Browse files
author
Franck
authored
Upgrade governor and send proposal to transfer governance to it for all contracts (#602)
* checkpoint * prettier * added governance claiming * Fixes and new governors task * copies * Review feedback * Remove Rinkeby specific migration code - it won't work * Remove v1 option in executeProposal * Remove unused var
1 parent 628b491 commit 971e010

5 files changed

Lines changed: 250 additions & 23 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// 1. Deploy the latest governor contract and sets its ownership to the new multisig safe wallet.
2+
// 2. Submit a proposal on the old governor to call transferGovernance() on all governable contracts.
3+
// 3. Submit a proposal on the new governor to call claimGovernance() on all governable contracts.
4+
5+
const {
6+
isMainnet,
7+
isFork,
8+
isRinkeby,
9+
isSmokeTest,
10+
} = require("../test/helpers.js");
11+
const {
12+
log,
13+
deployWithConfirmation,
14+
withConfirmation,
15+
executeProposal,
16+
sendProposal,
17+
} = require("../utils/deploy");
18+
const { proposeArgs } = require("../utils/governor");
19+
const { getTxOpts } = require("../utils/tx");
20+
const addresses = require("../utils/addresses");
21+
22+
const deployName = "018_upgrade_governor";
23+
24+
const runDeployment = async (hre) => {
25+
console.log(`Running ${deployName} deployment...`);
26+
27+
const { governorAddr } = await hre.getNamedAccounts();
28+
29+
const cOUSDProxy = await ethers.getContract("OUSDProxy");
30+
const cVaultProxy = await ethers.getContract("VaultProxy");
31+
const cCompoundStrategyProxy = await ethers.getContract(
32+
"CompoundStrategyProxy"
33+
);
34+
const cThreePoolStrategyProxy = await ethers.getContract(
35+
"ThreePoolStrategyProxy"
36+
);
37+
const cAaveStrategyProxy = await ethers.getContract("AaveStrategyProxy");
38+
const cBuyback = await ethers.getContract("Buyback");
39+
const cOGNStakingProxy = await ethers.getContract("OGNStakingProxy");
40+
const cCompensationClaim = await ethers.getContract("CompensationClaims");
41+
42+
// Deploy a new governor contract.
43+
// The governor's admin is the guardian account (e.g. the multi-sig).
44+
// Set a min delay of 60sec for executing proposals.
45+
const dGovernor = await deployWithConfirmation("Governor", [
46+
addresses.mainnet.Guardian,
47+
60,
48+
]);
49+
50+
// Proposal for tranferring governance from the old to new governor.
51+
const propTransferDescription = "Transfer governance";
52+
const propTransferArgs = await proposeArgs([
53+
{
54+
contract: cOUSDProxy,
55+
signature: "transferGovernance(address)",
56+
args: [dGovernor.address],
57+
},
58+
{
59+
contract: cVaultProxy,
60+
signature: "transferGovernance(address)",
61+
args: [dGovernor.address],
62+
},
63+
{
64+
contract: cCompoundStrategyProxy,
65+
signature: "transferGovernance(address)",
66+
args: [dGovernor.address],
67+
},
68+
{
69+
contract: cThreePoolStrategyProxy,
70+
signature: "transferGovernance(address)",
71+
args: [dGovernor.address],
72+
},
73+
{
74+
contract: cAaveStrategyProxy,
75+
signature: "transferGovernance(address)",
76+
args: [dGovernor.address],
77+
},
78+
{
79+
contract: cBuyback,
80+
signature: "transferGovernance(address)",
81+
args: [dGovernor.address],
82+
},
83+
{
84+
contract: cOGNStakingProxy,
85+
signature: "transferGovernance(address)",
86+
args: [dGovernor.address],
87+
},
88+
{
89+
contract: cCompensationClaim,
90+
signature: "transferGovernance(address)",
91+
args: [dGovernor.address],
92+
},
93+
]);
94+
95+
// Proposal for claiming governance by the new governor.
96+
const propClaimDescription = "Claim governance";
97+
const propClaimArgs = await proposeArgs([
98+
{
99+
contract: cOUSDProxy,
100+
signature: "claimGovernance()",
101+
},
102+
{
103+
contract: cVaultProxy,
104+
signature: "claimGovernance()",
105+
},
106+
{
107+
contract: cCompoundStrategyProxy,
108+
signature: "claimGovernance()",
109+
},
110+
{
111+
contract: cThreePoolStrategyProxy,
112+
signature: "claimGovernance()",
113+
},
114+
{
115+
contract: cAaveStrategyProxy,
116+
signature: "claimGovernance()",
117+
},
118+
{
119+
contract: cBuyback,
120+
signature: "claimGovernance()",
121+
},
122+
{
123+
contract: cOGNStakingProxy,
124+
signature: "claimGovernance()",
125+
},
126+
{
127+
contract: cCompensationClaim,
128+
signature: "claimGovernance()",
129+
},
130+
]);
131+
132+
if (isMainnet) {
133+
// On Mainnet, only propose. The enqueue and execution are handled manually via multi-sig.
134+
log("Sending transfer proposal to old governor...");
135+
await sendProposal(propTransferArgs, propTransferDescription, {
136+
governorAddr,
137+
});
138+
log("Transfer proposal sent.");
139+
140+
log("Sending claim proposal to new governor...");
141+
await sendProposal(propClaimArgs, propClaimDescription);
142+
log("Claim proposal sent.");
143+
} else if (isFork) {
144+
// On Fork we can send the proposal then impersonate the guardian to execute it.
145+
log("Sending and executing transfer proposal...");
146+
// Note: we send the proposal to the old governor by passing explicitly its address.
147+
await executeProposal(propTransferArgs, propTransferDescription, {
148+
governorAddr,
149+
});
150+
151+
log("Sending and executing claim proposal...");
152+
await executeProposal(propClaimArgs, propClaimDescription, {
153+
guardianAddr: addresses.mainnet.Guardian,
154+
});
155+
}
156+
157+
return true;
158+
};
159+
160+
const main = async (hre) => {
161+
console.log(`Running ${deployName} deployment...`);
162+
if (!hre) {
163+
hre = require("hardhat");
164+
}
165+
await runDeployment(hre);
166+
console.log(`${deployName} deploy done.`);
167+
return true;
168+
};
169+
170+
main.id = deployName;
171+
main.dependencies = ["017_3pool_strategy_update"];
172+
main.skip = () => !(isMainnet || isRinkeby || isFork) || isSmokeTest;
173+
174+
module.exports = main;

contracts/hardhat.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ require("@openzeppelin/hardhat-upgrades");
1010
const { accounts, fund, mint, redeem, transfer } = require("./tasks/account");
1111
const { debug } = require("./tasks/debug");
1212
const { env } = require("./tasks/env");
13-
const { execute, executeOnFork, proposal } = require("./tasks/governance");
13+
const {
14+
execute,
15+
executeOnFork,
16+
proposal,
17+
governors,
18+
} = require("./tasks/governance");
1419
const { balance } = require("./tasks/ousd");
1520
const { smokeTest, smokeTestCheck } = require("./tasks/smokeTest");
1621
const {
@@ -114,6 +119,9 @@ task("executeOnFork", "Enqueue and execute a proposal on the Fork")
114119
task("proposal", "Dumps the state of a proposal")
115120
.addParam("id", "Id of the proposal")
116121
.setAction(proposal);
122+
task("governors", "Get list of governors for all contracts").setAction(
123+
governors
124+
);
117125

118126
// Compensation tasks
119127
task("isAdjusterLocked", "Is adjuster on Compensation claims locked").setAction(

contracts/tasks/governance.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,44 @@ async function proposal(taskArguments, hre) {
9494
console.log(" actions: ", JSON.stringify(actions, null, 2));
9595
}
9696

97+
// Dumps the governor address for all the known contracts in the system.
98+
async function governors() {
99+
const cOUSDProxy = await ethers.getContract("OUSDProxy");
100+
const cVaultProxy = await ethers.getContract("VaultProxy");
101+
const cCompoundStrategyProxy = await ethers.getContract(
102+
"CompoundStrategyProxy"
103+
);
104+
const cThreePoolStrategyProxy = await ethers.getContract(
105+
"ThreePoolStrategyProxy"
106+
);
107+
const cAaveStrategyProxy = await ethers.getContract("AaveStrategyProxy");
108+
const cBuyback = await ethers.getContract("Buyback");
109+
const cOGNStakingProxy = await ethers.getContract("OGNStakingProxy");
110+
const cCompensationClaim = await ethers.getContract("CompensationClaims");
111+
const cFlipper = await ethers.getContract("Flipper");
112+
113+
console.log("Governor addresses:");
114+
console.log("===================");
115+
console.log("OUSDProxy: ", await cOUSDProxy.governor());
116+
console.log("VaultProxy: ", await cVaultProxy.governor());
117+
console.log(
118+
"CompoundStrategyProxy: ",
119+
await cCompoundStrategyProxy.governor()
120+
);
121+
console.log(
122+
"ThreePoolStrategyProxy: ",
123+
await cThreePoolStrategyProxy.governor()
124+
);
125+
console.log("AaveStrategyProxy: ", await cAaveStrategyProxy.governor());
126+
console.log("Buyback: ", await cBuyback.governor());
127+
console.log("OGNSTakingProxy: ", await cOGNStakingProxy.governor());
128+
console.log("CompensationClaim: ", await cCompensationClaim.governor());
129+
console.log("Flipper: ", await cFlipper.governor());
130+
}
131+
97132
module.exports = {
98133
execute,
99134
executeOnFork,
100135
proposal,
136+
governors,
101137
};

contracts/utils/addresses.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ addresses.mainnet.chainlinkUSDT_ETH =
6262
// WETH Token
6363
addresses.mainnet.WETH = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
6464
// Deployed OUSD contracts
65+
addresses.mainnet.Guardian = "0xbe2AB3d3d8F6a32b96414ebbd865dBD276d3d899"; // ERC 20 owner multisig.
6566
addresses.mainnet.VaultProxy = "0x277e80f3E14E7fB3fc40A9d6184088e0241034bD";
6667
addresses.mainnet.Vault = "0xf251Cb9129fdb7e9Ca5cad097dE3eA70caB9d8F9";
6768
addresses.mainnet.OUSDProxy = "0x2A8e1E676Ec238d8A992307B495b45B3fEAa5e86";

contracts/utils/deploy.js

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,15 @@ const withConfirmation = async (deployOrTransactionPromise) => {
7676
/**
7777
* Impersonate the guardian. Only applicable on Fork.
7878
*/
79-
const impersonateGuardian = async () => {
79+
const impersonateGuardian = async (optGuardianAddr = null) => {
8080
if (!isFork) {
8181
throw new Error("impersonateGuardian only works on Fork");
8282
}
8383

84-
const { guardianAddr } = await hre.getNamedAccounts();
84+
// If an address is passed, use that otherwise default to
85+
// the guardian address from the default hardhat accounts.
86+
const guardianAddr =
87+
optGuardianAddr || (await hre.getNamedAccounts()).guardianAddr;
8588

8689
// Send some ETH to the Guardian account to pay for gas fees.
8790
await hre.network.provider.request({
@@ -108,41 +111,40 @@ const impersonateGuardian = async () => {
108111
*
109112
* @param {Array<Object>} proposalArgs
110113
* @param {string} description
111-
* @param {boolean} whether to use the V1 governor (e.g. MinuteTimelock)
114+
* @param {opts} Options
115+
* governorAddr: address of the governor contract to send the proposal to
116+
* guardianAddr: address of the guardian (aka the governor's admin) to use for sending the queue and execute tx
112117
* @returns {Promise<void>}
113118
*/
114-
const executeProposal = async (proposalArgs, description, v1 = false) => {
119+
const executeProposal = async (proposalArgs, description, opts = {}) => {
115120
if (isMainnet || isRinkeby) {
116121
throw new Error("executeProposal only works on local test network");
117122
}
118123

119-
const { deployerAddr, guardianAddr } = await hre.getNamedAccounts();
124+
const namedAccounts = await hre.getNamedAccounts();
125+
const deployerAddr = namedAccounts.deployerAddr;
126+
const guardianAddr = opts.guardianAddr || namedAccounts.guardianAddr;
127+
120128
const sGuardian = hre.ethers.provider.getSigner(guardianAddr);
121129
const sDeployer = hre.ethers.provider.getSigner(deployerAddr);
122130

123131
if (isFork) {
124-
await impersonateGuardian();
132+
await impersonateGuardian(opts.guardianAddr);
125133
}
126134

127135
let governorContract;
128-
if (v1) {
129-
const v1GovernorAddr = "0x8a5fF78BFe0de04F5dc1B57d2e1095bE697Be76E";
130-
const v1GovernorAbi = [
131-
"function propose(address[],uint256[],string[],bytes[],string) returns (uint256)",
132-
"function proposalCount() view returns (uint256)",
133-
"function queue(uint256)",
134-
"function execute(uint256)",
135-
];
136-
proposalArgs = [proposalArgs[0], [0], proposalArgs[1], proposalArgs[2]];
137-
governorContract = new ethers.Contract(
138-
v1GovernorAddr,
139-
v1GovernorAbi,
140-
hre.ethers.provider
136+
if (opts.governorAddr) {
137+
governorContract = await ethers.getContractAt(
138+
"Governor",
139+
opts.governorAddr
141140
);
142-
log(`Using V1 governor contract at ${v1GovernorAddr}`);
143141
} else {
144142
governorContract = await ethers.getContract("Governor");
145143
}
144+
const admin = await governorContract.admin();
145+
log(
146+
`Using governor contract at ${governorContract.address} with admin ${admin}`
147+
);
146148

147149
const txOpts = await getTxOpts();
148150

@@ -207,15 +209,21 @@ const executeProposalOnFork = async (proposalId, executeGasLimit = null) => {
207209
* @param {string} description
208210
* @returns {Promise<void>}
209211
*/
210-
const sendProposal = async (proposalArgs, description) => {
212+
const sendProposal = async (proposalArgs, description, opts = {}) => {
211213
if (!isMainnet && !isFork) {
212214
throw new Error("sendProposal only works on Mainnet and Fork networks");
213215
}
214216

215217
const { deployerAddr } = await hre.getNamedAccounts();
216218
const sDeployer = hre.ethers.provider.getSigner(deployerAddr);
217219

218-
const governor = await ethers.getContract("Governor");
220+
let governor;
221+
if (opts.governorAddr) {
222+
governor = await ethers.getContractAt("Governor", opts.governorAddr);
223+
log(`Using governor contract at ${opts.governorAddr}`);
224+
} else {
225+
governor = await ethers.getContract("Governor");
226+
}
219227

220228
log(`Submitting proposal for ${description} to governor ${governor.address}`);
221229
log(`Args: ${JSON.stringify(proposalArgs, null, 2)}`);

0 commit comments

Comments
 (0)