diff --git a/deploy/deploy_contracts.ts b/deploy/deploy_contracts.ts index 47528f1b..feb9561e 100644 --- a/deploy/deploy_contracts.ts +++ b/deploy/deploy_contracts.ts @@ -65,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() diff --git a/deploy/deploy_delta_upgrade.ts b/deploy/deploy_delta_upgrade.ts index d5ea31f2..053e7ded 100644 --- a/deploy/deploy_delta_upgrade.ts +++ b/deploy/deploy_delta_upgrade.ts @@ -39,29 +39,27 @@ const setContractInfoAction = ( // 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 {deploy} = deployments // the deployments object itself contains the deploy function 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 contractDeployer = new ContractDeployer( - deploy, - deployer, - deployments, - skipRegister - ) - const controller = await contractDeployer.fetchDeployedController() + const deploy = skipRegister ? + contractDeployer.deploy.bind(contractDeployer) : + contractDeployer.deployAndRegister.bind(contractDeployer) const gitCommitHash = await contractDeployer.getGitHeadCommitHash() const governanceActions = [] // Deploy contracts - await contractDeployer.deployAndRegister({ + await deploy({ contract: "BondingVotes", name: "BondingVotes", args: [controller.address], @@ -72,7 +70,7 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) { setContractInfoAction(network, gitCommitHash, "BondingVotes") ) - const treasury = await contractDeployer.deployAndRegister({ + const treasury = await deploy({ contract: "Treasury", name: "Treasury", args: [], @@ -95,7 +93,7 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) { deployer // temporary admin role for deployer ).then(tx => tx.wait()) - const livepeerGovernor = await contractDeployer.deployAndRegister({ + const livepeerGovernor = await deploy({ contract: "LivepeerGovernor", name: "LivepeerGovernor", args: [controller.address], @@ -108,7 +106,7 @@ const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) { const llDeployment = await deployments.get("SortedDoublyLL") - await contractDeployer.deployAndRegister({ + await deploy({ contract: "BondingManager", name: "BondingManagerTarget", libraries: { diff --git a/deploy/deploy_poll.ts b/deploy/deploy_poll.ts index f6253c5d..a52c89ec 100644 --- a/deploy/deploy_poll.ts +++ b/deploy/deploy_poll.ts @@ -5,11 +5,11 @@ import ContractDeployer from "../utils/deployer" const func: DeployFunction = async function(hre: HardhatRuntimeEnvironment) { const {deployments, getNamedAccounts} = hre // Get the deployments and getNamedAccounts which are provided by hardhat-deploy - const {deploy, get} = deployments // the deployments object itself contains the deploy function + const {get} = deployments // the deployments object itself contains the deploy function const {deployer} = await getNamedAccounts() // Fetch named accounts from hardhat.config.ts - const contractDeployer = new ContractDeployer(deploy, deployer, deployments) + const contractDeployer = new ContractDeployer(deployer, deployments) const bondingManager = await get("BondingManager") diff --git a/utils/deployer.ts b/utils/deployer.ts index b0f00775..63072076 100644 --- a/utils/deployer.ts +++ b/utils/deployer.ts @@ -3,39 +3,28 @@ import childProcess from "child_process" const exec = util.promisify(childProcess.exec) import {ethers} from "hardhat" -import { - DeployOptions, - DeployResult, - DeploymentsExtension -} from "hardhat-deploy/types" +import {DeployResult, DeploymentsExtension} from "hardhat-deploy/types" import {deployments} from "hardhat" import {Controller} from "../typechain" import {Libraries} from "hardhat/types" +export type DeployConfig = { + contract: string + name: string + proxy?: boolean + args: Array + libraries?: Libraries | undefined +} + export default class ContractDeployer { - deploy: (name: string, options: DeployOptions) => Promise deployer: string deployments: DeploymentsExtension controller: Controller | undefined - /** - * skipRegister is used to skip the registration on the protocol controller (`setContractInfo`) of contracts - * deployed with this deployer instance. It is used when the deployer account is not the controller owner, which is - * the case in prod networks. In production, the registration has to be done through proceeding governance actions. - */ - readonly skipRegister: boolean - - constructor( - deploy: (name: string, options: DeployOptions) => Promise, - deployer: string, - deployments: DeploymentsExtension, - skipRegister = false - ) { - this.deploy = deploy + constructor(deployer: string, deployments: DeploymentsExtension) { this.deployer = deployer this.deployments = deployments this.controller = undefined - this.skipRegister = skipRegister } async getGitHeadCommitHash(): Promise { @@ -50,22 +39,13 @@ export default class ContractDeployer { return ethers.utils.solidityKeccak256(["string"], [name]) } - async register(name: string, address: string) { - const gitHash = await this.getGitHeadCommitHash() - await ( - await this.controller?.setContractInfo( - this.contractId(name), - address, - gitHash - ) - )?.wait() - } - async deployController(): Promise { if (this.controller && (await deployments.get("Controller"))) { console.log("Controller already deployed") } else { - const controller = await this.deploy("Controller", { + const {deploy} = this.deployments // the deployments object itself contains the deploy function + + const controller = await deploy("Controller", { from: this.deployer, // msg.sender overwrite, use named account args: [], // constructor arguments log: true // display the address and gas used in the console (not when run in test though) @@ -87,34 +67,46 @@ export default class ContractDeployer { return this.controller } - async deployAndRegister(config: { - contract: string - name: string - proxy?: boolean - args: Array - libraries?: Libraries | undefined - }): Promise { - const {contract, name, proxy, args, libraries} = config + async deployAndRegister(config: DeployConfig): Promise { + const {name, proxy} = config - const shouldRegister = this.controller && !this.skipRegister - const gitHash = await this.getGitHeadCommitHash() + if (!this.controller) { + throw new Error("Controller not initialized for registration") + } + + const deploy = await this.deploy(config) + + if (proxy) { + // deploy function only returns the proxy deployment in this case, so fetch the deployed target info + const targetName = `${name}Target` + const target = await this.deployments.get(targetName) + if (!target) { + throw new Error(`${targetName} not found`) + } + + // target has to be registered with a Target suffix + await this.register(targetName, target.address) + } + + // proxy gets registered as the actual contract name + await this.register(name, deploy.address) + + return deploy + } + + async deploy(config: DeployConfig) { + const {contract, name, proxy, args, libraries} = config + const {deploy} = this.deployments // the deployments object itself contains the deploy function // if there's no proxy, the target is just the contract itself const targetName = proxy ? `${name}Target` : name - const target = await this.deploy(targetName, { + const target = await deploy(targetName, { contract, from: this.deployer, log: true, args: [...args], libraries: libraries }) - if (shouldRegister) { - await this.controller!.setContractInfo( - this.contractId(targetName), - target.address, - gitHash - ).then(tx => tx.wait()) - } if (!proxy) { return target @@ -124,26 +116,27 @@ export default class ContractDeployer { } // proxy == true, proceed with proxy deployment and registration as the actual contract `name` - const managerProxy = await this.deploy(name, { + const managerProxy = await deploy(name, { contract: "ManagerProxy", from: this.deployer, log: true, - args: [this.controller?.address, this.contractId(targetName)] + args: [this.controller.address, this.contractId(targetName)] }) - if (shouldRegister) { - await this.controller - .setContractInfo( - this.contractId(name), - managerProxy.address, - gitHash - ) - .then(tx => tx.wait()) - } - // additionally, save the proxy deployment with a "Proxy" suffix await deployments.save(`${name}Proxy`, managerProxy) return managerProxy } + + async register(name: string, address: string) { + const gitHash = await this.getGitHeadCommitHash() + await ( + await this.controller?.setContractInfo( + this.contractId(name), + address, + gitHash + ) + )?.wait() + } }