From 47baae6c96aa59d7c99e32688f2dc190227dfb92 Mon Sep 17 00:00:00 2001 From: Anna Carroll Date: Wed, 26 Jan 2022 15:00:05 +0100 Subject: [PATCH] feat: governance utils --- .../nomad-deploy/src/governance/index.ts | 62 ++++++++ .../src/{incremental => governance}/utils.ts | 150 ++++++++++-------- typescript/nomad-deploy/src/index.ts | 1 + .../src/verification/readDeployOutput.ts | 16 +- typescript/nomad-deploy/tsconfig.json | 1 + 5 files changed, 163 insertions(+), 67 deletions(-) create mode 100644 typescript/nomad-deploy/src/governance/index.ts rename typescript/nomad-deploy/src/{incremental => governance}/utils.ts (66%) diff --git a/typescript/nomad-deploy/src/governance/index.ts b/typescript/nomad-deploy/src/governance/index.ts new file mode 100644 index 000000000..ac535a54b --- /dev/null +++ b/typescript/nomad-deploy/src/governance/index.ts @@ -0,0 +1,62 @@ +import { NomadContext } from '@nomad-xyz/sdk'; +import { CallBatch } from '@nomad-xyz/sdk/nomad'; +import { getBatch, writeBatch } from './utils'; +import { DeployEnvironment } from '../chain'; + +// execute a batch on the Governor Chain +export async function executeBatch( + batch: CallBatch, + environment: DeployEnvironment, + reason: string, +): Promise { + // persist the call batch to a local file + await writeBatch(batch, environment, reason); + // send the batch transaction, + // either directly on-chain or to gnosis safe + if (environment === 'dev') { + // in dev, execute the batch directly + console.log('Sending governance transaction...'); + const txResponse = await batch.execute(); + const receipt = await txResponse.wait(); + console.log('Governance tx mined!! ', receipt.transactionHash); + } else { + // TODO: send to gnosis safe directly + } +} + +// execute the remote Call Batches on each non-Governor chain +export async function executeRemoteBatches( + sdk: NomadContext, + environment: string, + reason: string, +) { + const batch = await getBatch(sdk, environment, reason); + // ensure that all batch hashes have landed on each remote domain + await batch.waitAll(); + console.log('All Batch Hashes Ready.'); + // for each domain, execute the batch calls + for (const domain of batch.domains) { + const domainName = sdk.resolveDomainName(domain); + console.log(`Executing Batch on ${domainName}...`); + const tx = await batch.executeDomain(domain); + const receipt = await tx.wait(); + console.log(`Executed Batch on ${domainName}:`, receipt.transactionHash); + } +} + +// log the progress of batches on all domains +export async function waitBatches( + sdk: NomadContext, + environment: string, + reason: string, +): Promise { + const batch = await getBatch(sdk, environment, reason); + return Promise.all( + batch.domains.map(async (domain: number) => { + const domainName = sdk.resolveDomainName(domain); + console.log(`Waiting for batch to be received on ${domainName}...`); + await batch.waitDomain(domain); + console.log(`Batch received on ${domainName}!`); + }), + ); +} diff --git a/typescript/nomad-deploy/src/incremental/utils.ts b/typescript/nomad-deploy/src/governance/utils.ts similarity index 66% rename from typescript/nomad-deploy/src/incremental/utils.ts rename to typescript/nomad-deploy/src/governance/utils.ts index d43c90a3a..3ff552b8b 100644 --- a/typescript/nomad-deploy/src/incremental/utils.ts +++ b/typescript/nomad-deploy/src/governance/utils.ts @@ -1,11 +1,93 @@ -import { NomadDomain } from '@nomad-xyz/sdk/nomad'; +import { CallBatch, RemoteContents, NomadContext, NomadDomain } from '@nomad-xyz/sdk/nomad'; import { BridgeDeploy } from '../bridge/BridgeDeploy'; import { CoreDeploy } from '../core/CoreDeploy'; import { isValidEnvironment, + parseFile, VALID_ENVIRONMENTS, } from '../verification/readDeployOutput'; import fs from 'fs'; +import { DeployEnvironment } from "../chain"; + +// instantiate a stored call batch +export async function getBatch(sdk: NomadContext, environment: string, reason: string): Promise { + const dir = getPathToBatchOutput(environment, reason); + const calls = parseFile(dir, "unbuilt.json"); + return CallBatch.fromJSON(sdk, calls); +} + +// write the batch to a local file +export async function writeBatch(batch: CallBatch, environment: DeployEnvironment, reason: string): Promise { + console.log("Writing governance transaction to file."); + // construct unbuilt transaction contents + const remote: RemoteContents = {}; + const domains = batch.domains; + domains.map((domain: number) => { + remote[domain.toString()] = batch.remote.get(domain)!; + }) + const unbuiltStr = JSON.stringify( + {local: batch.local, remote}, + null, + 2, + ); + // construct built transaction + const built = await batch.build(); + const builtStr = JSON.stringify(built, null, 2); + // get the directory where the governance action will be written + const dir = getPathToBatchOutput(environment, reason); + // write to file to persist governance actions for posterity + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(`${dir}/built.json`, builtStr); + fs.writeFileSync(`${dir}/unbuilt.json`, unbuiltStr); + console.log("Done!"); +} + +// get the path for a call batch to be output +export function getPathToBatchOutput(environment: string, reason: string) { + if (!isValidEnvironment) { + throw new Error( + `${environment} is not a valid environment. Please choose from ${JSON.stringify( + VALID_ENVIRONMENTS, + null, + 2, + )}`, + ); + } + let folder; + if (environment == 'staging') { + folder = 'staging'; + } else if (environment == 'prod') { + folder = 'mainnet'; + } else { + folder = 'development'; + } + return `../../governance/${folder}/${reason}`; +} + +// TODO: everything below this is in question -- consider deleting or moving into local utils +export function deploysToSDK( + core: CoreDeploy, + bridge: BridgeDeploy, + safeService?: string, +): NomadDomain { + return { + id: core.chain.domain, + name: core.chain.name, + bridgeRouter: bridge.contracts.bridgeRouter!.proxy.address, + tokenRegistry: bridge.contracts.tokenRegistry!.proxy.address, + ethHelper: bridge.contracts.ethHelper?.address, + home: core.contracts.home!.proxy.address, + replicas: Object.entries(core.contracts.replicas).map( + ([domain, replica]) => ({ + domain: parseInt(domain), + address: replica.proxy.address, + }), + ), + governanceRouter: core.contracts.governance!.proxy.address, + xAppConnectionManager: core.contracts.xAppConnectionManager!.address, + safeService, + }; +} /** * Structure that allows to poll an async block @@ -36,9 +118,9 @@ export class Waiter { * where `T` is callback's return and second item is a `success` boolean */ constructor( - callback: () => Promise, - timeoutMs: number, - retryMs: number, + callback: () => Promise, + timeoutMs: number, + retryMs: number, ) { this.resolve = undefined; this.reject = undefined; @@ -90,63 +172,3 @@ export class Waiter { return await this.promise; } } - -// TODO: bring it into good shape and move somewhere else. Also not sure if we should pass safe service as argument -export function deploysToSDK( - core: CoreDeploy, - bridge: BridgeDeploy, - safeService?: string, -): NomadDomain { - return { - id: core.chain.domain, - name: core.chain.name, - bridgeRouter: bridge.contracts.bridgeRouter!.proxy.address, - tokenRegistry: bridge.contracts.tokenRegistry!.proxy.address, - ethHelper: bridge.contracts.ethHelper?.address, - home: core.contracts.home!.proxy.address, - replicas: Object.entries(core.contracts.replicas).map( - ([domain, replica]) => ({ - domain: parseInt(domain), - address: replica.proxy.address, - }), - ), - governanceRouter: core.contracts.governance!.proxy.address, - xAppConnectionManager: core.contracts.xAppConnectionManager!.address, - safeService, - }; -} - -export function writeBatchOutput( - builtStr: string, - unbuiltStr: string, - environment: string, -) { - let dir = getPathToBatchOutput(environment); - - fs.mkdirSync(dir, { recursive: true }); - - fs.writeFileSync(`${dir}/built.json`, builtStr); - - fs.writeFileSync(`${dir}/unbuilt.json`, unbuiltStr); -} - -export function getPathToBatchOutput(environment: string) { - if (!isValidEnvironment) { - throw new Error( - `${environment} is not a valid environment. Please choose from ${JSON.stringify( - VALID_ENVIRONMENTS, - null, - 2, - )}`, - ); - } - let folder; - if (environment == 'staging') { - folder = 'staging'; - } else if (environment == 'prod') { - folder = 'mainnet'; - } else { - folder = 'development'; - } - return `../../governance/${folder}`; -} diff --git a/typescript/nomad-deploy/src/index.ts b/typescript/nomad-deploy/src/index.ts index 937b2e343..91191332d 100644 --- a/typescript/nomad-deploy/src/index.ts +++ b/typescript/nomad-deploy/src/index.ts @@ -2,6 +2,7 @@ export * as core from './core'; export * as bridge from './bridge'; export * as incremental from './incremental'; +export * as governance from './governance'; export * as deploy from './deploy'; export * as chain from './chain'; export * as contracts from './contracts'; diff --git a/typescript/nomad-deploy/src/verification/readDeployOutput.ts b/typescript/nomad-deploy/src/verification/readDeployOutput.ts index ec46251e4..8ddb482aa 100644 --- a/typescript/nomad-deploy/src/verification/readDeployOutput.ts +++ b/typescript/nomad-deploy/src/verification/readDeployOutput.ts @@ -106,6 +106,7 @@ export function getVerificationInputFromDeploy( * @param fileSuffix target file suffix to parse ("config", "contracts", "verification") * */ export function parseFileFromDeploy( + path: string, network: string, fileSuffix: string, @@ -121,10 +122,19 @@ export function parseFileFromDeploy( `No ${fileSuffix} files found for ${network} at ${path}/${targetFileName}`, ); } + return parseFile(path, targetFileName); +} +/* + * @notice Return the JSON-parsed file + * from the given file name + * and directory path + * @param path relative path to the file + * @param fileName the file name + * */ +export function parseFile(path: string, fileName: string) { const fileString: string = fs - .readFileSync(`${path}/${targetFileName}`) - .toString(); - + .readFileSync(`${path}/${fileName}`) + .toString(); return JSON.parse(fileString); } diff --git a/typescript/nomad-deploy/tsconfig.json b/typescript/nomad-deploy/tsconfig.json index 5114a6837..b33937acd 100644 --- a/typescript/nomad-deploy/tsconfig.json +++ b/typescript/nomad-deploy/tsconfig.json @@ -16,5 +16,6 @@ "./src/bridge/*.ts", "./src/verification/*.ts", "./src/incremental/*.ts", + "./src/governance/**/*.ts" ] }