Skip to content

Commit

Permalink
deploy: Delta production deployment scripts (#631)
Browse files Browse the repository at this point in the history
* hardhat: Fix .env files

* hardhat: Migrate to newer etherscan verify plugin

* tasks: Create task to verify all deployed contracts

* doc: Add instructions on how to deploy to a network

* deploy: Move treasury deploy to deploy_contracts

* deploy: Update deployer to support delta deployment

- Allows deploying proxy without registering them on controller
- Fixes the names of the deployments that are saved/overridden
 - Deploying only the target will not set the "main" deployment file
 - Deploying with a proxy will not set the ManagerProxy deployment

* deploy: Create delta deployment script

* deploy: Allow only governor to execute treasury proposals

The Governor already exposes an execute() function that
will call the timelock (treasury) internally, so we don't
need to expose the treasury directly to calls by anyone.

* utils: Split deployAndRegister func from ContractDeployer

* deploy,doc: Address other PR comments

* deploy: Fix deploy_poll script

It doesnt want registration so just call deploy()

* tasks: Create task to renounce admin role post-deploy

Good to already have pre-tested scripts for the deployment steps.

* doc: Add docs on delta deploy steps

* doc: Address comments on deploy steps doc
  • Loading branch information
victorges authored Oct 11, 2023
1 parent e1f8b36 commit dbc25bc
Show file tree
Hide file tree
Showing 17 changed files with 1,010 additions and 190 deletions.
80 changes: 78 additions & 2 deletions deploy/deploy_contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
RoundsManager,
TicketBroker,
LivepeerToken,
Governor
Governor,
LivepeerGovernor,
Treasury
} from "../typechain"

import ContractDeployer from "../utils/deployer"
Expand Down Expand Up @@ -63,7 +65,7 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) {

const config = getNetworkConfig(hre.network.name)

const contractDeployer = new ContractDeployer(deploy, deployer, deployments)
const contractDeployer = new ContractDeployer(deployer, deployments)

const Controller: Controller = await contractDeployer.deployController()

Expand Down Expand Up @@ -159,6 +161,8 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) {
proxy: true,
args: [Controller.address]
})
// tests expect it to be saved as AdjustableRoundsManager as well
await deployments.save("AdjustableRoundsManager", roundsManager)
} else {
roundsManager = await contractDeployer.deployAndRegister({
contract: "RoundsManager",
Expand Down Expand Up @@ -212,6 +216,21 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) {
await (await Governor.stage(transferOwnershipUpdate, 0)).wait()
await (await Governor.execute(transferOwnershipUpdate)).wait()

// Treasury (LivepeerGovernor)

const treasury = await contractDeployer.deployAndRegister({
contract: "Treasury",
name: "Treasury",
args: []
})

const livepeerGovernor = await contractDeployer.deployAndRegister({
contract: "LivepeerGovernor",
name: "LivepeerGovernor",
args: [Controller.address],
proxy: true
})

// Set BondingManager parameters
const BondingManager: BondingManager = (await ethers.getContractAt(
"BondingManager",
Expand All @@ -229,6 +248,21 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) {
)
).wait()

if (config.bondingManager.treasuryRewardCutRate) {
await (
await BondingManager.setTreasuryRewardCutRate(
config.bondingManager.treasuryRewardCutRate
)
).wait()
}
if (config.bondingManager.treasuryBalanceCeiling) {
await (
await BondingManager.setTreasuryBalanceCeiling(
config.bondingManager.treasuryBalanceCeiling
)
).wait()
}

// Set RoundsManager parameters
const RoundsManager: RoundsManager = (await ethers.getContractAt(
"RoundsManager",
Expand Down Expand Up @@ -259,6 +293,48 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) {
}
}

// Initialize Treasury and LivepeerGovernor

const Treasury: Treasury = await ethers.getContractAt(
"Treasury",
treasury.address
)

await Treasury.initialize(
config.treasury.minDelay,
[], // governor will be added as a proposer later
[], // governor will be added as an executor later
deployer // temporary admin role for deployer
).then(tx => tx.wait())

const LivepeerGovernor: LivepeerGovernor = await ethers.getContractAt(
"LivepeerGovernor",
livepeerGovernor.address
)

await LivepeerGovernor.initialize(
config.livepeerGovernor.initialVotingDelay,
config.livepeerGovernor.initialVotingPeriod,
config.livepeerGovernor.initialProposalThreshold,
config.livepeerGovernor.initialQuorum,
config.livepeerGovernor.quota
).then(tx => tx.wait())

// Now grant proposer and executor roles to governor and renounce deployer admin role
const roles = {
proposer: await Treasury.PROPOSER_ROLE(),
canceller: await Treasury.CANCELLER_ROLE(),
executor: await Treasury.EXECUTOR_ROLE(),
admin: await Treasury.TIMELOCK_ADMIN_ROLE()
}
for (const role of [roles.proposer, roles.canceller, roles.executor]) {
await Treasury.grantRole(role, LivepeerGovernor.address).then(tx =>
tx.wait()
)
}

await Treasury.renounceRole(roles.admin, deployer).then(tx => tx.wait())

// Set TicketBroker parameters
const Broker: TicketBroker = (await ethers.getContractAt(
"TicketBroker",
Expand Down
207 changes: 207 additions & 0 deletions deploy/deploy_delta_upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import {HardhatRuntimeEnvironment} from "hardhat/types"
import {DeployFunction} from "hardhat-deploy/types"

import ContractDeployer from "../utils/deployer"
import {ethers} from "hardhat"
import {BondingManager, LivepeerGovernor, Treasury} from "../typechain"
import getNetworkConfig from "./migrations.config"
import {contractId} from "../utils/helpers"

const PROD_NETWORKS = ["mainnet", "arbitrumMainnet"]

const isProdNetwork = (name: string): boolean => {
return PROD_NETWORKS.indexOf(name) > -1
}

// Returns a reference to the given contract in the format used by the governor-scripts repo (e.g.
// "ADDRESSES.arbitrumMainnet.controller"). Notice that on the serialized JSON, this will come out as a string while in
// the governance script it should be de-stringified (remove quotes) to reference an imported ADDRESS object.
const contractAddressRef = (network: string, name: string) => {
const lowerName = name[0].toLowerCase() + name.slice(1)
return `ADDRESSES.${network}.${lowerName}`
}

// Returns a governance action spec in the format expected by the governor-scripts to call the setContractInfo function
// on the controller to register a contract in the protocol.
const setContractInfoAction = (
network: string,
gitCommitHash: string,
name: string
) => ({
target: contractAddressRef(network, "Controller"),
value: "0",
contract: "Controller",
name: "setContractInfo",
params: [contractId(name), contractAddressRef(network, name), gitCommitHash]
})

// Deploys the Livepeer Delta protocol upgrade from the previous version (962107f). This deploys only the targets
// for already existing contracts, and skips registering them in the controller in case it's a production network.
const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre // Get the deployments and getNamedAccounts which are provided by hardhat-deploy

const network = hre.network.name
const config = getNetworkConfig(network)

const {deployer} = await getNamedAccounts() // Fetch named accounts from hardhat.config.ts

const contractDeployer = new ContractDeployer(deployer, deployments)
const controller = await contractDeployer.fetchDeployedController()

// on prod networks, deploy contracts here but registration is done later through governance
// on test networks, the deployer must also be the controller owner so we can register here
const skipRegister = isProdNetwork(network)
const deploy = skipRegister ?
contractDeployer.deploy.bind(contractDeployer) :
contractDeployer.deployAndRegister.bind(contractDeployer)

const gitCommitHash = await contractDeployer.getGitHeadCommitHash()
const governanceActions = []

// Deploy contracts
await deploy({
contract: "BondingVotes",
name: "BondingVotes",
args: [controller.address],
proxy: true
})
governanceActions.push(
setContractInfoAction(network, gitCommitHash, "BondingVotesTarget"),
setContractInfoAction(network, gitCommitHash, "BondingVotes")
)

const treasury = await deploy({
contract: "Treasury",
name: "Treasury",
args: [],
proxy: false
})
governanceActions.push(
setContractInfoAction(network, gitCommitHash, "Treasury")
)

const Treasury: Treasury = await ethers.getContractAt(
"Treasury",
treasury.address
)

// We should already initialize the treasury since it's not a proxy
await Treasury.initialize(
config.treasury.minDelay,
[], // governor will be added as a proposer later
[], // governor will be added as an executor later
deployer // temporary admin role for deployer
).then(tx => tx.wait())

const livepeerGovernor = await deploy({
contract: "LivepeerGovernor",
name: "LivepeerGovernor",
args: [controller.address],
proxy: true
})
governanceActions.push(
setContractInfoAction(network, gitCommitHash, "LivepeerGovernorTarget"),
setContractInfoAction(network, gitCommitHash, "LivepeerGovernor")
)

const llDeployment = await deployments.get("SortedDoublyLL")

await deploy({
contract: "BondingManager",
name: "BondingManagerTarget",
libraries: {
SortedDoublyLL: llDeployment.address
},
args: [controller.address],
proxy: false // we're deploying the Target directly, so proxy is false
})
governanceActions.push(
setContractInfoAction(network, gitCommitHash, "BondingManagerTarget")
)

// Setup/initialize contracts (or print the required governance actions)

// Grant proposer and executor roles to governor and renounce deployer admin role
const roles = {
proposer: await Treasury.PROPOSER_ROLE(),
canceller: await Treasury.CANCELLER_ROLE(),
executor: await Treasury.EXECUTOR_ROLE(),
admin: await Treasury.TIMELOCK_ADMIN_ROLE()
}
for (const role of [roles.proposer, roles.canceller, roles.executor]) {
await Treasury.grantRole(role, livepeerGovernor.address).then(tx =>
tx.wait()
)
}

const LivepeerGovernor: LivepeerGovernor = await ethers.getContractAt(
"LivepeerGovernor",
livepeerGovernor.address
)
const governorInitParams = [
config.livepeerGovernor.initialVotingDelay,
config.livepeerGovernor.initialVotingPeriod,
config.livepeerGovernor.initialProposalThreshold,
config.livepeerGovernor.initialQuorum,
config.livepeerGovernor.quota
] as Parameters<typeof LivepeerGovernor.initialize>

if (!skipRegister) {
await LivepeerGovernor.initialize(...governorInitParams).then(tx =>
tx.wait()
)
} else {
governanceActions.push({
target: contractAddressRef(network, "LivepeerGovernor"),
value: "0",
contract: "LivepeerGovernor",
name: "initialize",
params: governorInitParams
})
}

if (!skipRegister) {
// We need to refetch the BondingManager contract as we only deploy the Target above
const bondingManager = await deployments.get("BondingManager")
const BondingManager: BondingManager = await ethers.getContractAt(
"BondingManager",
bondingManager.address
)

await BondingManager.setTreasuryRewardCutRate(
config.bondingManager.treasuryRewardCutRate
).then(tx => tx.wait())
await BondingManager.setTreasuryBalanceCeiling(
config.bondingManager.treasuryBalanceCeiling
).then(tx => tx.wait())
} else {
governanceActions.push(
{
target: contractAddressRef(network, "BondingManager"),
value: "0",
contract: "BondingManager",
name: "setTreasuryRewardCutRate",
params: [config.bondingManager.treasuryRewardCutRate]
},
{
target: contractAddressRef(network, "BondingManager"),
value: "0",
contract: "BondingManager",
name: "setTreasuryBalanceCeiling",
params: [config.bondingManager.treasuryBalanceCeiling]
}
)
}

// Helper print out to validate the pending governance actions that will be required by the protocol owner
if (skipRegister) {
console.log("Pending governance actions:")
console.log(JSON.stringify(governanceActions, null, 2))
}

console.log("Deployer must renounce ADMIN role from the Treasury with:")
console.log(`npx hardhat treasury-renounce-admin-role --network ${network}`)
}

func.tags = ["DELTA_UPGRADE"]
export default func
Loading

0 comments on commit dbc25bc

Please sign in to comment.