diff --git a/packages/contracts/deploy/00_info/01_info.ts b/packages/contracts/deploy/00_info/01_info.ts index d6f6acaa..135db1f7 100644 --- a/packages/contracts/deploy/00_info/01_info.ts +++ b/packages/contracts/deploy/00_info/01_info.ts @@ -4,6 +4,7 @@ import { getProductionNetworkName, isLocal, } from '../../utils/helpers'; +import {forkNetwork} from '../helpers'; import {getNetworkByNameOrAlias} from '@aragon/osx-commons-configs'; import {UnsupportedNetworkError} from '@aragon/osx-commons-sdk'; import {DeployFunction} from 'hardhat-deploy/types'; @@ -20,7 +21,9 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { console.log(`\n✨ ${path.basename(__filename)}:`); const [deployer] = await hre.ethers.getSigners(); - if (isLocal(hre)) { + if (process.env.FORKING_RPC_URL) { + await forkNetwork(hre, process.env.FORKING_RPC_URL); + } else if (isLocal(hre)) { const productionNetworkName: string = getProductionNetworkName(hre); console.log( @@ -32,16 +35,11 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { if (networkConfig === null) { throw new UnsupportedNetworkError(productionNetworkName); } - await hre.network.provider.request({ - method: 'hardhat_reset', - params: [ - { - forking: { - jsonRpcUrl: networkConfig.url, - }, - }, - ], - }); + if (!networkConfig.url) { + throw new Error('RPC Url on network not defined'); + } + + await forkNetwork(hre, networkConfig.url); } else { console.log(`Production deployment on network '${hre.network.name}'.`); } diff --git a/packages/contracts/deploy/10_create_repo/11_create_repo.ts b/packages/contracts/deploy/10_create_repo/11_create_repo.ts index caf42237..928d804b 100644 --- a/packages/contracts/deploy/10_create_repo/11_create_repo.ts +++ b/packages/contracts/deploy/10_create_repo/11_create_repo.ts @@ -1,9 +1,14 @@ -import {PLUGIN_REPO_ENS_SUBDOMAIN_NAME} from '../../plugin-settings'; +import { + PLUGIN_REPO_ENS_SUBDOMAIN_NAME, + PLUGIN_CONTRACT_NAME, +} from '../../plugin-settings'; import { findPluginRepo, getProductionNetworkName, pluginEnsDomain, + isValidAddress, } from '../../utils/helpers'; +import {saveToDeployedJson} from '../helpers'; import { getLatestNetworkDeployment, getNetworkNameByAlias, @@ -18,6 +23,7 @@ import { PluginRepo__factory, PluginRepoFactory__factory, } from '@aragon/osx-ethers'; +import {ethers} from 'hardhat'; import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; import path from 'path'; @@ -27,6 +33,7 @@ import path from 'path'; * @param {HardhatRuntimeEnvironment} hre */ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + // todo change this log console.log( `Creating the '${pluginEnsDomain( hre @@ -35,24 +42,57 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const [deployer] = await hre.ethers.getSigners(); - // Get the Aragon `PluginRepoFactory` from the `osx-commons-configs` - const productionNetworkName = getProductionNetworkName(hre); - const network = getNetworkNameByAlias(productionNetworkName); - if (network === null) { - throw new UnsupportedNetworkError(productionNetworkName); - } - const networkDeployments = getLatestNetworkDeployment(network); - if (networkDeployments === null) { - throw `Deployments are not available on network ${network}.`; + let pluginRepoFactoryAddress = process.env.PLUGIN_REPO_FACTORY_ADDRESS; + let subdomainRegistrar; + + if (pluginRepoFactoryAddress) { + if (!isValidAddress(pluginRepoFactoryAddress)) { + throw new Error('Plugin Repo Factory in .env is not of type Address'); + } + // use this factory + const pluginRepoFactory = PluginRepoFactory__factory.connect( + pluginRepoFactoryAddress, + deployer + ); + + const pluginRepoRegistry = PluginRepoRegistry__factory.connect( + await pluginRepoFactory.pluginRepoRegistry(), + deployer + ); + subdomainRegistrar = await pluginRepoRegistry.subdomainRegistrar(); + } else { + // get the factory from osx-commons-configs deployments + + // Get the Aragon `PluginRepoFactory` from the `osx-commons-configs` + const productionNetworkName = getProductionNetworkName(hre); + const network = getNetworkNameByAlias(productionNetworkName); + if (network === null) { + throw new UnsupportedNetworkError(productionNetworkName); + } + const networkDeployments = getLatestNetworkDeployment(network); + if (networkDeployments === null) { + throw `Deployments are not available on network ${network}.`; + } + + pluginRepoFactoryAddress = networkDeployments.PluginRepoFactory.address; + + subdomainRegistrar = + networkDeployments.PluginENSSubdomainRegistrarProxy.address; } + // subdomain will depend on if the framework has the ens or not + const subdomain = + subdomainRegistrar !== ethers.constants.AddressZero + ? PLUGIN_REPO_ENS_SUBDOMAIN_NAME + : ''; + const pluginRepoFactory = PluginRepoFactory__factory.connect( - networkDeployments.PluginRepoFactory.address, + pluginRepoFactoryAddress, deployer ); // Create the `PluginRepo` through the Aragon `PluginRepoFactory` const tx = await pluginRepoFactory.createPluginRepo( - PLUGIN_REPO_ENS_SUBDOMAIN_NAME, + subdomain, deployer.address ); @@ -70,13 +110,35 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { ); console.log( - `PluginRepo '${pluginEnsDomain(hre)}' deployed at '${pluginRepo.address}'.` + `PluginRepo ${ + subdomainRegistrar !== ethers.constants.AddressZero + ? 'with ens:' + pluginEnsDomain(hre) + : 'without ens' + } deployed at '${pluginRepo.address}'.` ); hre.aragonToVerifyContracts.push({ address: pluginRepo.address, args: [], }); + + saveToDeployedJson( + [ + { + name: PLUGIN_CONTRACT_NAME + 'RepoProxy', + address: pluginRepo.address, + blockNumber: tx.blockNumber, + txHash: tx.hash, + }, + { + name: PLUGIN_CONTRACT_NAME + 'RepoImplementation', + address: await pluginRepoFactory.pluginRepoBase(), + blockNumber: null, + txHash: null, + }, + ], + true + ); }; export default func; @@ -89,24 +151,34 @@ func.tags = ['CreateRepo']; func.skip = async (hre: HardhatRuntimeEnvironment) => { console.log(`\n🏗️ ${path.basename(__filename)}:`); - // Check if the ens record exists already - const {pluginRepo, ensDomain} = await findPluginRepo(hre); + // try getting the plugin repo. + const res = await findPluginRepo(hre); + const pluginRepoAddress = res.pluginRepo?.address; + const ensDomain = res.ensDomain; - if (pluginRepo !== null) { - console.log( - `ENS name '${ensDomain}' was claimed already at '${ - pluginRepo.address - }' on network '${getProductionNetworkName(hre)}'. Skipping deployment...` - ); + if (pluginRepoAddress) { + if (ensDomain != '') { + console.log( + `ENS name '${ensDomain}' was claimed already at '${pluginRepoAddress}' on network '${getProductionNetworkName( + hre + )}'. Skipping deployment...` + ); + } else { + console.log( + `Plugin Repo already deployed at '${pluginRepoAddress}' on network '${getProductionNetworkName( + hre + )}'. Skipping deployment...` + ); + } hre.aragonToVerifyContracts.push({ - address: pluginRepo.address, + address: pluginRepoAddress, args: [], }); return true; } else { - console.log(`ENS name '${ensDomain}' is unclaimed. Deploying...`); + console.log('Deploying Plugin Repo'); return false; } diff --git a/packages/contracts/deploy/20_new_version/21_setup.ts b/packages/contracts/deploy/20_new_version/21_setup.ts index 424e2fc8..b84589d1 100644 --- a/packages/contracts/deploy/20_new_version/21_setup.ts +++ b/packages/contracts/deploy/20_new_version/21_setup.ts @@ -10,6 +10,7 @@ import { } from '../../plugin-settings'; import {ZK_SYNC_NETWORKS} from '../../utils/zkSync'; import {pluginSetupContractName} from '../helpers'; +import {saveToDeployedJson} from '../helpers'; import hre from 'hardhat'; import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; @@ -70,6 +71,21 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { log: true, }); + saveToDeployedJson([ + { + name: GOVERNANCE_ERC20_CONTRACT_NAME, + address: governanceERC20DeployResult.address, + blockNumber: governanceERC20DeployResult.receipt?.blockNumber, + txHash: governanceERC20DeployResult.transactionHash, + }, + { + name: GOVERNANCE_WRAPPED_ERC20_CONTRACT_NAME, + address: governanceWrappedERC20DeployResult.address, + blockNumber: governanceWrappedERC20DeployResult.receipt?.blockNumber, + txHash: governanceWrappedERC20DeployResult.transactionHash, + }, + ]); + console.log( `Deployed '${PLUGIN_SETUP_CONTRACT_NAME}' contract at '${res.address}'` ); diff --git a/packages/contracts/deploy/20_new_version/23_publish.ts b/packages/contracts/deploy/20_new_version/23_publish.ts index bc909cd6..e2186c05 100644 --- a/packages/contracts/deploy/20_new_version/23_publish.ts +++ b/packages/contracts/deploy/20_new_version/23_publish.ts @@ -10,9 +10,13 @@ import { impersonatedManagementDaoSigner, isLocal, pluginEnsDomain, + isValidAddress, } from '../../utils/helpers'; import {pluginSetupContractName} from '../helpers'; +import {getLatestContractAddress} from '../helpers'; import {PLUGIN_REPO_PERMISSIONS, uploadToPinata} from '@aragon/osx-commons-sdk'; +import {PluginRepo} from '@aragon/osx-ethers'; +import {SignerWithAddress} from '@nomiclabs/hardhat-ethers/signers'; import {writeFile} from 'fs/promises'; import {ethers} from 'hardhat'; import hre from 'hardhat'; @@ -20,6 +24,25 @@ import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; import path from 'path'; +async function createVersion( + pluginRepo: PluginRepo, + release: number, + setup: string, + releaseMetadataURI: string, + buildMetadataURI: string, + signer: SignerWithAddress +) { + const tx = await pluginRepo + .connect(signer) + .createVersion( + release, + setup, + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(buildMetadataURI)), + ethers.utils.hexlify(ethers.utils.toUtf8Bytes(releaseMetadataURI)) + ); + await tx.wait(); +} + const PLUGIN_SETUP_CONTRACT_NAME = pluginSetupContractName(hre); /** @@ -57,7 +80,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // Get PluginRepo const {pluginRepo, ensDomain} = await findPluginRepo(hre); if (pluginRepo === null) { - throw `PluginRepo '${ensDomain}' does not exist yet.`; + throw `Can't find Plugin Repo...`; } // Check release number @@ -73,18 +96,24 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // Check build number const latestBuild = (await pluginRepo.buildCount(VERSION.release)).toNumber(); - if (VERSION.build < latestBuild) { - throw Error( - `Publishing with build number ${VERSION.build} is not possible. The latest build is ${latestBuild}. Aborting publication...` - ); - } - if (VERSION.build > latestBuild + 1) { - throw Error( - `Publishing with build number ${VERSION.build} is not possible. + if (latestBuild == 0 && VERSION.build > 1) { + // it means there's no build yet on the repo on the specific VERSION.release + // and build version in the plugin settings is > 1, meaning that + // it must push placeholder contracts and as the last one, push the actual plugin setup. + } else { + if (VERSION.build < latestBuild) { + throw Error( + `Publishing with build number ${VERSION.build} is not possible. The latest build is ${latestBuild}. Aborting publication...` + ); + } + if (VERSION.build > latestBuild + 1) { + throw Error( + `Publishing with build number ${VERSION.build} is not possible. The latest build is ${latestBuild} and the next release you can publish is release number ${ - latestBuild + 1 - }. Aborting publication...` - ); + latestBuild + 1 + }. Aborting publication...` + ); + } } if (setup == undefined || setup?.receipt == undefined) { @@ -114,17 +143,41 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { [] ) ) { - // Create the new version - const tx = await pluginRepo - .connect(signer) - .createVersion( - VERSION.release, - setup.address, - ethers.utils.hexlify(ethers.utils.toUtf8Bytes(buildMetadataURI)), - ethers.utils.hexlify(ethers.utils.toUtf8Bytes(releaseMetadataURI)) - ); + if (latestBuild == 0 && VERSION.build > 1) { + // We are publishing the first version as build > 1. + // So we need to publish placeholders first.. + const placeholderSetup = + process.env.PLACEHOLDER_SETUP ?? + getLatestContractAddress('PlaceholderSetup', hre); + + if (!isValidAddress(placeholderSetup)) { + throw new Error( + 'Aborting. Placeholder setup not present in this network or in .env or is not a valid address (is not an address or is address zero)' + ); + } - await tx.wait(); + for (let i = 0; i < VERSION.build - 1; i++) { + console.log('Publishing placeholder', i + 1); + await createVersion( + pluginRepo, + VERSION.release, + placeholderSetup, + `{}`, + 'placeholder-setup-build', + signer + ); + } + } + + // Create the new version + await createVersion( + pluginRepo, + VERSION.release, + setup.address, + buildMetadataURI, + releaseMetadataURI, + signer + ); const version = await pluginRepo['getLatestVersion(uint8)']( VERSION.release diff --git a/packages/contracts/deploy/30_upgrade_repo/_common.ts b/packages/contracts/deploy/30_upgrade_repo/_common.ts index 1070dcec..1631463d 100644 --- a/packages/contracts/deploy/30_upgrade_repo/_common.ts +++ b/packages/contracts/deploy/30_upgrade_repo/_common.ts @@ -1,10 +1,18 @@ -import {findPluginRepo, getProductionNetworkName} from '../../utils/helpers'; +import { + findPluginRepo, + getProductionNetworkName, + isValidAddress, +} from '../../utils/helpers'; import { getLatestNetworkDeployment, getNetworkNameByAlias, } from '@aragon/osx-commons-configs'; import {UnsupportedNetworkError} from '@aragon/osx-commons-sdk'; -import {PluginRepo, PluginRepo__factory} from '@aragon/osx-ethers'; +import { + PluginRepo, + PluginRepo__factory, + PluginRepoFactory__factory, +} from '@aragon/osx-ethers'; import {SignerWithAddress} from '@nomiclabs/hardhat-ethers/signers'; import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; @@ -30,10 +38,10 @@ export async function fetchData( if (network === null) { throw new UnsupportedNetworkError(productionNetworkName); } - const networkDeployments = getLatestNetworkDeployment(network); - if (networkDeployments === null) { - throw `Deployments are not available on network ${network}.`; - } + // const networkDeployments = getLatestNetworkDeployment(network); + // if (networkDeployments === null) { + // throw `Deployments are not available on network ${network}.`; + // } // Get PluginRepo const {pluginRepo, ensDomain} = await findPluginRepo(hre); @@ -45,11 +53,34 @@ export async function fetchData( `Upgrading plugin repo '${ensDomain}' (${pluginRepo.address})...` ); - // Get the latest `PluginRepo` implementation as the upgrade target - const latestPluginRepoImplementation = PluginRepo__factory.connect( - networkDeployments.PluginRepoBase.address, - deployer - ); + let latestPluginRepoImplementation; + const pluginRepoFactoryAddress = process.env.PLUGIN_REPO_FACTORY_ADDRESS; + + if (pluginRepoFactoryAddress) { + if (!isValidAddress(pluginRepoFactoryAddress)) { + throw new Error( + 'Plugin Repo Factory in .env is not a valid address (is not an address or is address zero)' + ); + } + + const pluginRepoFactory = PluginRepoFactory__factory.connect( + pluginRepoFactoryAddress, + deployer + ); + latestPluginRepoImplementation = PluginRepo__factory.connect( + await pluginRepoFactory.pluginRepoBase(), + deployer + ); + } else { + const networkDeployments = getLatestNetworkDeployment(network); + if (networkDeployments === null) { + throw `Deployments are not available on network ${network}.`; + } + latestPluginRepoImplementation = PluginRepo__factory.connect( + networkDeployments.PluginRepoBase.address, + deployer + ); + } // Get the current OSX protocol version from the current plugin repo implementation let current: SemVer; diff --git a/packages/contracts/deploy/helpers.ts b/packages/contracts/deploy/helpers.ts index c9a863c5..590664a1 100644 --- a/packages/contracts/deploy/helpers.ts +++ b/packages/contracts/deploy/helpers.ts @@ -2,6 +2,7 @@ import { PLUGIN_SETUP_CONTRACT_NAME, PLUGIN_SETUP_CONTRACT_NAME_ZKSYNC, } from '../plugin-settings'; +import {DEPLOYMENT_JSON_PATH} from '../plugin-settings'; import {VersionTag} from '../test/test-utils/psp/types'; import { ENSRegistry__factory, @@ -110,10 +111,6 @@ export function getLatestContractAddress( ): string { let networkName = hre.network.name; - if (hre.testingFork.network) { - networkName = hre.testingFork.network; - } - const osxNetworkName = getNetworkNameByAlias(networkName); if (!osxNetworkName) { if (isLocal(hre.network)) { @@ -131,6 +128,22 @@ export function getLatestContractAddress( return ''; } +export async function forkNetwork( + hre: HardhatRuntimeEnvironment, + fork_url: string +) { + await hre.network.provider.request({ + method: 'hardhat_reset', + params: [ + { + forking: { + jsonRpcUrl: fork_url, + }, + }, + ], + }); +} + export async function detemineDeployerNextAddress( index: number, deployer: SignerWithAddress @@ -579,5 +592,40 @@ export function getManagementDAOMultisigAddress( return address; } +export type AddressInfo = { + name: string; + address: string; + blockNumber: number | undefined | null; + txHash: string | undefined | null; +}; + +export function saveToDeployedJson( + addressesInfo: AddressInfo[], + newDeployment: boolean = false +) { + // Write plugin repo address to file + const fs = require('fs'); + const outputPath = DEPLOYMENT_JSON_PATH; + + // Read existing JSON file + let existingData: {[key: string]: AddressInfo} = {}; + + if (fs.existsSync(outputPath) && !newDeployment) { + const fileContent = fs.readFileSync(outputPath, 'utf8'); + existingData = JSON.parse(fileContent); + } + + for (const addressInfo of addressesInfo) { + existingData[addressInfo.name] = { + address: addressInfo.address, + blockNumber: addressInfo.blockNumber, + txHash: addressInfo.txHash, + }; + } + + fs.writeFileSync(outputPath, JSON.stringify(existingData, null, 2)); + console.log(`Plugin repo addresses saved to ${outputPath}`); +} + // hh-deploy cannot process files without default exports export default async () => {}; diff --git a/packages/contracts/plugin-settings.ts b/packages/contracts/plugin-settings.ts index 1529d8fd..a9f6e1b1 100644 --- a/packages/contracts/plugin-settings.ts +++ b/packages/contracts/plugin-settings.ts @@ -3,6 +3,12 @@ import releaseMetadata from './src/release-metadata.json'; import {GovernanceERC20} from './test/test-utils/typechain-versions'; import {VersionTag} from '@aragon/osx-commons-sdk'; import {ethers} from 'hardhat'; +import path from 'path'; + +export const DEPLOYMENT_JSON_PATH = path.join( + __dirname, + './deployed-contracts.json' +); export const PLUGIN_CONTRACT_NAME = 'TokenVoting'; // This must match the filename `packages/contracts/src/MyPlugin.sol` and the contract name `MyPlugin` within. export const PLUGIN_SETUP_CONTRACT_NAME = 'TokenVotingSetup'; // This must match the filename `packages/contracts/src/MyPluginSetup.sol` and the contract name `MyPluginSetup` within. diff --git a/packages/contracts/utils/helpers.ts b/packages/contracts/utils/helpers.ts index bace7383..a3d15335 100644 --- a/packages/contracts/utils/helpers.ts +++ b/packages/contracts/utils/helpers.ts @@ -15,10 +15,14 @@ import { PluginRepo, PluginRepoEvents, PluginRepo__factory, + PluginRepoFactory__factory, + PluginRepoRegistry__factory, } from '@aragon/osx-ethers'; import {setBalance} from '@nomicfoundation/hardhat-network-helpers'; import {SignerWithAddress} from '@nomiclabs/hardhat-ethers/signers'; -import {BigNumber, ContractTransaction, ethers} from 'ethers'; +import {BigNumber, ContractTransaction} from 'ethers'; +import {LogDescription} from 'ethers/lib/utils'; +import {ethers} from 'hardhat'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; export function isLocal(hre: HardhatRuntimeEnvironment): boolean { @@ -68,18 +72,68 @@ export async function findPluginRepo( hre: HardhatRuntimeEnvironment ): Promise<{pluginRepo: PluginRepo | null; ensDomain: string}> { const [deployer] = await hre.ethers.getSigners(); - const productionNetworkName: string = getProductionNetworkName(hre); - const network = getNetworkNameByAlias(productionNetworkName); - if (network === null) { - throw new UnsupportedNetworkError(productionNetworkName); + + if (process.env.PLUGIN_REPO_ADDRESS) { + if (!isValidAddress(process.env.PLUGIN_REPO_ADDRESS)) { + throw new Error( + 'Plugin Repo in .env is not a valid address (is not an address or is address zero)' + ); + } + + return { + pluginRepo: PluginRepo__factory.connect( + process.env.PLUGIN_REPO_ADDRESS, + deployer + ), + ensDomain: '', + }; } - const networkDeployments = getLatestNetworkDeployment(network); - if (networkDeployments === null) { - throw `Deployments are not available on network ${network}.`; + + let subdomainRegistrarAddress; + const pluginRepoFactoryAddress = process.env.PLUGIN_REPO_FACTORY_ADDRESS; + + if (pluginRepoFactoryAddress) { + if (!isValidAddress(pluginRepoFactoryAddress)) { + throw new Error( + 'Plugin Repo Factory in .env is not valid address (is not an address or is address zero)' + ); + } + // get ENS registrar from the plugin factory provided + const pluginRepoFactory = PluginRepoFactory__factory.connect( + pluginRepoFactoryAddress, + deployer + ); + + const pluginRepoRegistry = PluginRepoRegistry__factory.connect( + await pluginRepoFactory.pluginRepoRegistry(), + deployer + ); + + subdomainRegistrarAddress = await pluginRepoRegistry.subdomainRegistrar(); + } else { + // get ENS registrar from the commons configs deployments + const productionNetworkName: string = getProductionNetworkName(hre); + + const network = getNetworkNameByAlias(productionNetworkName); + if (network === null) { + throw new UnsupportedNetworkError(productionNetworkName); + } + const networkDeployments = getLatestNetworkDeployment(network); + if (networkDeployments === null) { + throw `Deployments are not available on network ${network}.`; + } + + subdomainRegistrarAddress = + networkDeployments.PluginENSSubdomainRegistrarProxy.address; + } + + if (subdomainRegistrarAddress === ethers.constants.AddressZero) { + // the network does not support ENS + return {pluginRepo: null, ensDomain: ''}; } const registrar = ENSSubdomainRegistrar__factory.connect( - networkDeployments.PluginENSSubdomainRegistrarProxy.address, + subdomainRegistrarAddress, deployer ); @@ -109,7 +163,7 @@ export async function findPluginRepo( } export type EventWithBlockNumber = { - event: ethers.utils.LogDescription; + event: LogDescription; blockNumber: number; }; @@ -187,6 +241,13 @@ export async function createVersion( return tx; } +export function isValidAddress(address: string): boolean { + // check if the address is valid and not zero address + return ( + ethers.utils.isAddress(address) && address !== ethers.constants.AddressZero + ); +} + export const AragonOSxAsciiArt = " ____ _____ \n /\\ / __ \\ / ____| \n / \\ _ __ __ _ __ _ ___ _ __ | | | | (_____ __ \n / /\\ \\ | '__/ _` |/ _` |/ _ \\| '_ \\ | | | |\\___ \\ \\/ / \n / ____ \\| | | (_| | (_| | (_) | | | | | |__| |____) > < \n /_/ \\_\\_| \\__,_|\\__, |\\___/|_| |_| \\____/|_____/_/\\_\\ \n __/ | \n |___/ \n"; @@ -194,6 +255,20 @@ export async function getManagementDao( hre: HardhatRuntimeEnvironment ): Promise { const [deployer] = await hre.ethers.getSigners(); + + const managementDaoAddress = process.env.MANAGEMENT_DAO_ADDRESS; + + if (managementDaoAddress) { + // getting the management DAO from the env var + if (!isValidAddress(managementDaoAddress)) { + throw new Error( + 'Management DAO address in .env is not valid address (is not an address or is address zero)' + ); + } + + return DAO__factory.connect(managementDaoAddress, deployer); + } + const productionNetworkName = getProductionNetworkName(hre); const network = getNetworkNameByAlias(productionNetworkName); if (network === null) {