From f76ce4dee1ee61c94125a90526dbd19397cc30e1 Mon Sep 17 00:00:00 2001 From: y0sher Date: Sun, 3 Nov 2024 17:44:40 +0200 Subject: [PATCH] add option to deploy contracts with machine readable output (json) --- hardhat.config.ts | 29 +++++++++++- tasks/deploy.ts | 116 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 107 insertions(+), 38 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index aac45bb0..fd8d828f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -3,7 +3,6 @@ import { NetworkUserConfig } from 'hardhat/types'; import 'dotenv/config'; -import '@nomicfoundation/hardhat-toolbox-viem'; import '@nomicfoundation/hardhat-chai-matchers'; import '@openzeppelin/hardhat-upgrades'; @@ -54,7 +53,10 @@ const config: HardhatUserConfig = { }, }, etherscan: { - apiKey: process.env.ETHERSCAN_KEY, + apiKey: { + 'holesky': process.env.ETHERSCAN_KEY, + 'devnet': 'x', + }, customChains: [ { network: 'holesky', @@ -64,6 +66,14 @@ const config: HardhatUserConfig = { browserURL: 'https://holesky.etherscan.io', }, }, + { + network: 'devnet', + chainId: 3151908, + urls: { + apiURL: `${process.env.BLOCKSCOUT_URL}/api`, + browserURL: process.env.BLOCKSCOUT_URL, + }, + } ], }, contractSizer: { @@ -86,6 +96,7 @@ const config: HardhatUserConfig = { }, }; + if (process.env.HOLESKY_ETH_NODE_URL && process.env.HOLESKY_OWNER_PRIVATE_KEY) { const sharedConfig = { url: `${process.env.HOLESKY_ETH_NODE_URL}${process.env.NODE_PROVIDER_KEY}`, @@ -135,4 +146,18 @@ if (process.env.FORK_TESTING_ENABLED) { }; } +if (process.env.DEVNET_ETH_NODE_URL) { + config.networks = { + devnet: { + chainId: 3151908, + url: `${process.env.DEVNET_ETH_NODE_URL}`, + accounts: [ + "39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d", + ], + gasPrice: +(process.env.GAS_PRICE || ''), + gas: +(process.env.GAS || ''), + } as NetworkUserConfig, + }; +} + export default config; diff --git a/tasks/deploy.ts b/tasks/deploy.ts index 13dc1155..f7ad1e43 100644 --- a/tasks/deploy.ts +++ b/tasks/deploy.ts @@ -1,6 +1,7 @@ import { task, subtask, types } from 'hardhat/config'; import { SSVModules } from './config'; + /** @title Hardhat task to deploy all required contracts for SSVNetwork. This task deploys the main SSVNetwork and SSVNetworkViews contracts, along with their associated modules. @@ -15,18 +16,25 @@ The deployer account used will be the first one returned by ethers.getSigners(). Therefore, it should be appropriately configured in your Hardhat network configuration. This task assumes that the SSVModules enum and deployment tasks for individual contracts have been properly defined. */ -task('deploy:all', 'Deploy SSVNetwork, SSVNetworkViews and module contracts').setAction(async ({}, hre) => { - // Triggering compilation - await hre.run('compile'); +task('deploy:all', 'Deploy SSVNetwork, SSVNetworkViews and module contracts') +.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean) +.setAction(async (args, hre) => { + if (!args.machine) { + // Triggering compilation if this is manual + await hre.run('compile'); + } - const [deployer] = await ethers.getSigners(); - console.log(`Deploying contracts with the account:${deployer.address}`); + if (!args.machine){ + const [deployer] = await ethers.getSigners(); + deployer.address = deployer.address.toLowerCase(); + console.log(`Deploying contracts with the account:${deployer.address}`); + } - const ssvTokenAddress = await hre.run('deploy:mock-token'); - const operatorsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVOperators] }); - const clustersModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVClusters] }); - const daoModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVDAO] }); - const viewsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVViews] }); + const ssvTokenAddress = await hre.run('deploy:token', { machine: args.machine }); + const operatorsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVOperators], machine: args.machine }); + const clustersModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVClusters], machine: args.machine }); + const daoModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVDAO], machine: args.machine }); + const viewsModAddress = await hre.run('deploy:module', { module: SSVModules[SSVModules.SSVViews], machine: args.machine }); const { ssvNetworkProxyAddress: ssvNetworkAddress } = await hre.run('deploy:ssv-network', { operatorsModAddress, @@ -34,11 +42,25 @@ task('deploy:all', 'Deploy SSVNetwork, SSVNetworkViews and module contracts').se daoModAddress, viewsModAddress, ssvTokenAddress, + machine: args.machine, }); await hre.run('deploy:ssv-network-views', { ssvNetworkAddress, + machine: args.machine, }); + + if (args.machine) { + const jsonData = { + ssvTokenAddress, + operatorsModAddress, + clustersModAddress, + daoModAddress, + viewsModAddress, + ssvNetworkAddress, + } + console.log(JSON.stringify(jsonData)); + } }); /** @@ -92,27 +114,35 @@ This subtask uses the "deploy:impl" subtask for the actual deployment. */ subtask('deploy:module', 'Deploys a new module contract') .addParam('module', 'SSV Module', null, types.string) - .setAction(async ({ module }, hre) => { + .addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean) + .setAction(async ({ module, machine }, hre) => { const moduleValues = Object.values(SSVModules); if (!moduleValues.includes(module)) { throw new Error(`Invalid SSVModule: ${module}. Expected one of: ${moduleValues.join(', ')}`); } - const moduleAddress = await hre.run('deploy:impl', { contract: module }); + const moduleAddress = await hre.run('deploy:impl', { contract: module, machine: machine }); return moduleAddress; }); -task('deploy:token', 'Deploys SSV Token').setAction(async ({}, hre) => { - // Triggering compilation - await hre.run('compile'); - - console.log('Deploying SSV Network Token'); +task('deploy:token', 'Deploys SSV Token') +.addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean) +.setAction(async (args, hre) => { + if (!args.machine){ + // Triggering compilation if this is manual + await hre.run('compile'); + console.log('Deploying SSV Network Token'); + } const ssvTokenFactory = await ethers.getContractFactory('SSVToken'); const ssvToken = await ssvTokenFactory.deploy(); - await ssvToken.deployed(); + await ssvToken.waitForDeployment(); - console.log(`SSV Network Token deployed to: ${ssvToken.address}`); + if (!args.machine){ + console.log(`SSV Network Token deployed to: ${ssvToken.address}`); + } + + return await ssvToken.getAddress() }); /** @@ -127,11 +157,11 @@ subtask('deploy:mock-token', 'Deploys / fetch SSV Token').setAction(async ({}, h // Local networks, deploy mock token // const ssvToken = await hre.viem.deployContract('SSVToken'); - const ssvTokenFactory = await ethers.getContractFactory('SSVTokenMock'); - const ssvToken = await ssvTokenFactory.deploy(); + const ssvTokenFactory = await ethers.getContractFactory('SSVToken'); + const ssvToken = await ssvTokenFactory.deployContract(); await ssvToken.waitForDeployment(); - return ssvToken.address; + return await ssvToken.getAddress(); }); /** @@ -147,17 +177,22 @@ The contract specified should be already compiled and exist in the 'artifacts' d */ subtask('deploy:impl', 'Deploys an implementation contract') .addParam('contract', 'New contract implemetation', null, types.string) - .setAction(async ({ contract }, hre) => { - // Triggering compilation - await hre.run('compile'); - + .addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean) + .setAction(async (args, hre) => { + if (!args.machine){ + // Triggering compilation if this is manual + await hre.run('compile'); + } // Deploy implemetation contract - const contractFactory = await ethers.getContractFactory(contract); + const contractFactory = await ethers.getContractFactory(args.contract); const contractImpl = await contractFactory.deploy(); await contractImpl.waitForDeployment(); - console.log(`${contract} implementation deployed to: ${contractImpl.address}`); - return contractImpl.address; + if (!args.machine){ + console.log(`${args.contract} implementation deployed to: ${await contractImpl.getAddress()}`); + } + + return await contractImpl.getAddress(); }); /** @@ -183,11 +218,15 @@ subtask('deploy:ssv-network', 'Deploys SSVNetwork contract') .addPositionalParam('daoModAddress', 'DAO module address', null, types.string) .addPositionalParam('viewsModAddress', 'Views module address', null, types.string) .addPositionalParam('ssvTokenAddress', 'SSV Token address', null, types.string) - .setAction(async ({ operatorsModAddress, clustersModAddress, daoModAddress, viewsModAddress, ssvTokenAddress }) => { + .addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean) + .setAction(async ({ operatorsModAddress, clustersModAddress, daoModAddress, viewsModAddress, ssvTokenAddress, machine }) => { const ssvNetworkFactory = await ethers.getContractFactory('SSVNetwork'); // deploy SSVNetwork - console.log(`Deploying SSVNetwork with ssvToken ${ssvTokenAddress}`); + if (!machine){ + console.log(`Deploying SSVNetwork with ssvToken ${ssvTokenAddress}`); + } + const ssvNetwork = await upgrades.deployProxy( ssvNetworkFactory, [ @@ -212,8 +251,10 @@ subtask('deploy:ssv-network', 'Deploys SSVNetwork contract') const ssvNetworkProxyAddress = await ssvNetwork.getAddress(); const ssvNetworkImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetworkProxyAddress); - console.log(`SSVNetwork proxy deployed to: ${ssvNetworkProxyAddress}`); - console.log(`SSVNetwork implementation deployed to: ${ssvNetworkImplAddress}`); + if (!machine) { + console.log(`SSVNetwork proxy deployed to: ${ssvNetworkProxyAddress}`); + console.log(`SSVNetwork implementation deployed to: ${ssvNetworkImplAddress}`); + } return { ssvNetworkProxyAddress, ssvNetworkImplAddress }; }); @@ -231,7 +272,8 @@ The 'SSVNetworkViews' contract specified should be already compiled and exist in */ subtask('deploy:ssv-network-views', 'Deploys SSVNetworkViews contract') .addParam('ssvNetworkAddress', 'SSVNetwork address', null, types.string) - .setAction(async ({ ssvNetworkAddress }) => { + .addOptionalParam('machine', 'outputs the machine readable output', false, types.boolean) + .setAction(async ({ ssvNetworkAddress, machine }) => { const ssvNetworkViewsFactory = await ethers.getContractFactory('SSVNetworkViews'); // deploy SSVNetwork @@ -243,8 +285,10 @@ subtask('deploy:ssv-network-views', 'Deploys SSVNetworkViews contract') const ssvNetworkViewsProxyAddress = await ssvNetworkViews.getAddress(); const ssvNetworkViewsImplAddress = await upgrades.erc1967.getImplementationAddress(ssvNetworkViewsProxyAddress); - console.log(`SSVNetworkViews proxy deployed to: ${ssvNetworkViewsProxyAddress}`); - console.log(`SSVNetworkViews implementation deployed to: ${ssvNetworkViewsImplAddress}`); + if (!machine){ + console.log(`SSVNetworkViews proxy deployed to: ${ssvNetworkViewsProxyAddress}`); + console.log(`SSVNetworkViews implementation deployed to: ${ssvNetworkViewsImplAddress}`); + } return { ssvNetworkViewsProxyAddress, ssvNetworkViewsImplAddress }; });