Skip to content

Commit

Permalink
Upgrade governor and send proposal to transfer governance to it for a…
Browse files Browse the repository at this point in the history
…ll 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
  • Loading branch information
Franck authored Jun 22, 2021
1 parent 628b491 commit 971e010
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 23 deletions.
174 changes: 174 additions & 0 deletions contracts/deploy/018_upgrade_governor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// 1. Deploy the latest governor contract and sets its ownership to the new multisig safe wallet.
// 2. Submit a proposal on the old governor to call transferGovernance() on all governable contracts.
// 3. Submit a proposal on the new governor to call claimGovernance() on all governable contracts.

const {
isMainnet,
isFork,
isRinkeby,
isSmokeTest,
} = require("../test/helpers.js");
const {
log,
deployWithConfirmation,
withConfirmation,
executeProposal,
sendProposal,
} = require("../utils/deploy");
const { proposeArgs } = require("../utils/governor");
const { getTxOpts } = require("../utils/tx");
const addresses = require("../utils/addresses");

const deployName = "018_upgrade_governor";

const runDeployment = async (hre) => {
console.log(`Running ${deployName} deployment...`);

const { governorAddr } = await hre.getNamedAccounts();

const cOUSDProxy = await ethers.getContract("OUSDProxy");
const cVaultProxy = await ethers.getContract("VaultProxy");
const cCompoundStrategyProxy = await ethers.getContract(
"CompoundStrategyProxy"
);
const cThreePoolStrategyProxy = await ethers.getContract(
"ThreePoolStrategyProxy"
);
const cAaveStrategyProxy = await ethers.getContract("AaveStrategyProxy");
const cBuyback = await ethers.getContract("Buyback");
const cOGNStakingProxy = await ethers.getContract("OGNStakingProxy");
const cCompensationClaim = await ethers.getContract("CompensationClaims");

// Deploy a new governor contract.
// The governor's admin is the guardian account (e.g. the multi-sig).
// Set a min delay of 60sec for executing proposals.
const dGovernor = await deployWithConfirmation("Governor", [
addresses.mainnet.Guardian,
60,
]);

// Proposal for tranferring governance from the old to new governor.
const propTransferDescription = "Transfer governance";
const propTransferArgs = await proposeArgs([
{
contract: cOUSDProxy,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cVaultProxy,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cCompoundStrategyProxy,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cThreePoolStrategyProxy,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cAaveStrategyProxy,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cBuyback,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cOGNStakingProxy,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
{
contract: cCompensationClaim,
signature: "transferGovernance(address)",
args: [dGovernor.address],
},
]);

// Proposal for claiming governance by the new governor.
const propClaimDescription = "Claim governance";
const propClaimArgs = await proposeArgs([
{
contract: cOUSDProxy,
signature: "claimGovernance()",
},
{
contract: cVaultProxy,
signature: "claimGovernance()",
},
{
contract: cCompoundStrategyProxy,
signature: "claimGovernance()",
},
{
contract: cThreePoolStrategyProxy,
signature: "claimGovernance()",
},
{
contract: cAaveStrategyProxy,
signature: "claimGovernance()",
},
{
contract: cBuyback,
signature: "claimGovernance()",
},
{
contract: cOGNStakingProxy,
signature: "claimGovernance()",
},
{
contract: cCompensationClaim,
signature: "claimGovernance()",
},
]);

if (isMainnet) {
// On Mainnet, only propose. The enqueue and execution are handled manually via multi-sig.
log("Sending transfer proposal to old governor...");
await sendProposal(propTransferArgs, propTransferDescription, {
governorAddr,
});
log("Transfer proposal sent.");

log("Sending claim proposal to new governor...");
await sendProposal(propClaimArgs, propClaimDescription);
log("Claim proposal sent.");
} else if (isFork) {
// On Fork we can send the proposal then impersonate the guardian to execute it.
log("Sending and executing transfer proposal...");
// Note: we send the proposal to the old governor by passing explicitly its address.
await executeProposal(propTransferArgs, propTransferDescription, {
governorAddr,
});

log("Sending and executing claim proposal...");
await executeProposal(propClaimArgs, propClaimDescription, {
guardianAddr: addresses.mainnet.Guardian,
});
}

return true;
};

const main = async (hre) => {
console.log(`Running ${deployName} deployment...`);
if (!hre) {
hre = require("hardhat");
}
await runDeployment(hre);
console.log(`${deployName} deploy done.`);
return true;
};

main.id = deployName;
main.dependencies = ["017_3pool_strategy_update"];
main.skip = () => !(isMainnet || isRinkeby || isFork) || isSmokeTest;

module.exports = main;
10 changes: 9 additions & 1 deletion contracts/hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ require("@openzeppelin/hardhat-upgrades");
const { accounts, fund, mint, redeem, transfer } = require("./tasks/account");
const { debug } = require("./tasks/debug");
const { env } = require("./tasks/env");
const { execute, executeOnFork, proposal } = require("./tasks/governance");
const {
execute,
executeOnFork,
proposal,
governors,
} = require("./tasks/governance");
const { balance } = require("./tasks/ousd");
const { smokeTest, smokeTestCheck } = require("./tasks/smokeTest");
const {
Expand Down Expand Up @@ -114,6 +119,9 @@ task("executeOnFork", "Enqueue and execute a proposal on the Fork")
task("proposal", "Dumps the state of a proposal")
.addParam("id", "Id of the proposal")
.setAction(proposal);
task("governors", "Get list of governors for all contracts").setAction(
governors
);

// Compensation tasks
task("isAdjusterLocked", "Is adjuster on Compensation claims locked").setAction(
Expand Down
36 changes: 36 additions & 0 deletions contracts/tasks/governance.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,44 @@ async function proposal(taskArguments, hre) {
console.log(" actions: ", JSON.stringify(actions, null, 2));
}

// Dumps the governor address for all the known contracts in the system.
async function governors() {
const cOUSDProxy = await ethers.getContract("OUSDProxy");
const cVaultProxy = await ethers.getContract("VaultProxy");
const cCompoundStrategyProxy = await ethers.getContract(
"CompoundStrategyProxy"
);
const cThreePoolStrategyProxy = await ethers.getContract(
"ThreePoolStrategyProxy"
);
const cAaveStrategyProxy = await ethers.getContract("AaveStrategyProxy");
const cBuyback = await ethers.getContract("Buyback");
const cOGNStakingProxy = await ethers.getContract("OGNStakingProxy");
const cCompensationClaim = await ethers.getContract("CompensationClaims");
const cFlipper = await ethers.getContract("Flipper");

console.log("Governor addresses:");
console.log("===================");
console.log("OUSDProxy: ", await cOUSDProxy.governor());
console.log("VaultProxy: ", await cVaultProxy.governor());
console.log(
"CompoundStrategyProxy: ",
await cCompoundStrategyProxy.governor()
);
console.log(
"ThreePoolStrategyProxy: ",
await cThreePoolStrategyProxy.governor()
);
console.log("AaveStrategyProxy: ", await cAaveStrategyProxy.governor());
console.log("Buyback: ", await cBuyback.governor());
console.log("OGNSTakingProxy: ", await cOGNStakingProxy.governor());
console.log("CompensationClaim: ", await cCompensationClaim.governor());
console.log("Flipper: ", await cFlipper.governor());
}

module.exports = {
execute,
executeOnFork,
proposal,
governors,
};
1 change: 1 addition & 0 deletions contracts/utils/addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ addresses.mainnet.chainlinkUSDT_ETH =
// WETH Token
addresses.mainnet.WETH = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
// Deployed OUSD contracts
addresses.mainnet.Guardian = "0xbe2AB3d3d8F6a32b96414ebbd865dBD276d3d899"; // ERC 20 owner multisig.
addresses.mainnet.VaultProxy = "0x277e80f3E14E7fB3fc40A9d6184088e0241034bD";
addresses.mainnet.Vault = "0xf251Cb9129fdb7e9Ca5cad097dE3eA70caB9d8F9";
addresses.mainnet.OUSDProxy = "0x2A8e1E676Ec238d8A992307B495b45B3fEAa5e86";
Expand Down
52 changes: 30 additions & 22 deletions contracts/utils/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@ const withConfirmation = async (deployOrTransactionPromise) => {
/**
* Impersonate the guardian. Only applicable on Fork.
*/
const impersonateGuardian = async () => {
const impersonateGuardian = async (optGuardianAddr = null) => {
if (!isFork) {
throw new Error("impersonateGuardian only works on Fork");
}

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

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

const { deployerAddr, guardianAddr } = await hre.getNamedAccounts();
const namedAccounts = await hre.getNamedAccounts();
const deployerAddr = namedAccounts.deployerAddr;
const guardianAddr = opts.guardianAddr || namedAccounts.guardianAddr;

const sGuardian = hre.ethers.provider.getSigner(guardianAddr);
const sDeployer = hre.ethers.provider.getSigner(deployerAddr);

if (isFork) {
await impersonateGuardian();
await impersonateGuardian(opts.guardianAddr);
}

let governorContract;
if (v1) {
const v1GovernorAddr = "0x8a5fF78BFe0de04F5dc1B57d2e1095bE697Be76E";
const v1GovernorAbi = [
"function propose(address[],uint256[],string[],bytes[],string) returns (uint256)",
"function proposalCount() view returns (uint256)",
"function queue(uint256)",
"function execute(uint256)",
];
proposalArgs = [proposalArgs[0], [0], proposalArgs[1], proposalArgs[2]];
governorContract = new ethers.Contract(
v1GovernorAddr,
v1GovernorAbi,
hre.ethers.provider
if (opts.governorAddr) {
governorContract = await ethers.getContractAt(
"Governor",
opts.governorAddr
);
log(`Using V1 governor contract at ${v1GovernorAddr}`);
} else {
governorContract = await ethers.getContract("Governor");
}
const admin = await governorContract.admin();
log(
`Using governor contract at ${governorContract.address} with admin ${admin}`
);

const txOpts = await getTxOpts();

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

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

const governor = await ethers.getContract("Governor");
let governor;
if (opts.governorAddr) {
governor = await ethers.getContractAt("Governor", opts.governorAddr);
log(`Using governor contract at ${opts.governorAddr}`);
} else {
governor = await ethers.getContract("Governor");
}

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

0 comments on commit 971e010

Please sign in to comment.