From 69d82b44b5d5361aaf95badc55afc0a6af2a309e Mon Sep 17 00:00:00 2001 From: Jan Nanista Date: Tue, 28 Nov 2023 15:37:46 -0800 Subject: [PATCH] chore: Add collectDeployments utility to utils-evm-hardhat --- packages/utils-evm-hardhat/hardhat.config.ts | 4 + packages/utils-evm-hardhat/src/index.ts | 1 + .../src/internal/assertions.ts | 0 .../src/omnigraph/coordinates.ts | 39 ++++++ .../utils-evm-hardhat/src/omnigraph/index.ts | 1 + packages/utils-evm-hardhat/src/runtime.ts | 33 +++-- .../test/omnigraph/coordinates.test.ts | 118 ++++++++++++++++++ 7 files changed, 187 insertions(+), 9 deletions(-) rename packages/{ua-utils-evm-hardhat => utils-evm-hardhat}/src/internal/assertions.ts (100%) create mode 100644 packages/utils-evm-hardhat/src/omnigraph/coordinates.ts create mode 100644 packages/utils-evm-hardhat/src/omnigraph/index.ts create mode 100644 packages/utils-evm-hardhat/test/omnigraph/coordinates.test.ts diff --git a/packages/utils-evm-hardhat/hardhat.config.ts b/packages/utils-evm-hardhat/hardhat.config.ts index a5e7f8156..fdf7f2136 100644 --- a/packages/utils-evm-hardhat/hardhat.config.ts +++ b/packages/utils-evm-hardhat/hardhat.config.ts @@ -13,6 +13,10 @@ const config: HardhatUserConfig = { saveDeployments: true, endpointId: EndpointId.ETHEREUM_MAINNET, }, + 'ethereum-testnet': { + url: 'https://eth-goerli.public.blastapi.io', + endpointId: EndpointId.ETHEREUM_TESTNET, + }, 'bsc-testnet': { url: 'https://bsc-testnet.publicnode.com', accounts: { diff --git a/packages/utils-evm-hardhat/src/index.ts b/packages/utils-evm-hardhat/src/index.ts index f2f21cb31..a377be569 100644 --- a/packages/utils-evm-hardhat/src/index.ts +++ b/packages/utils-evm-hardhat/src/index.ts @@ -4,6 +4,7 @@ import './type-extensions' // Regular exports export * from './config' export * from './logger' +export * from './omnigraph' export * from './provider' export * from './runtime' export * from './types' diff --git a/packages/ua-utils-evm-hardhat/src/internal/assertions.ts b/packages/utils-evm-hardhat/src/internal/assertions.ts similarity index 100% rename from packages/ua-utils-evm-hardhat/src/internal/assertions.ts rename to packages/utils-evm-hardhat/src/internal/assertions.ts diff --git a/packages/utils-evm-hardhat/src/omnigraph/coordinates.ts b/packages/utils-evm-hardhat/src/omnigraph/coordinates.ts new file mode 100644 index 000000000..e9f3447fe --- /dev/null +++ b/packages/utils-evm-hardhat/src/omnigraph/coordinates.ts @@ -0,0 +1,39 @@ +import { EndpointId } from '@layerzerolabs/lz-definitions' +import { Deployment } from 'hardhat-deploy/dist/types' +import { HardhatRuntimeEnvironment } from 'hardhat/types' +import { assertHardhatDeploy } from '../internal/assertions' +import { getNetworkNamesByEid, getNetworkRuntimeEnvironment } from '../runtime' + +export interface OmniDeployment { + eid: EndpointId + deployment: Deployment +} + +/** + * Collects all deployment of a certain contract along with their endpoint IDs. + * + * Network s which don't have `endpointId` configured in their hardhat network config will be ignored + * + * @param hre `HardhatRuntimeEnvironment` + * @param contractName `string` + * @returns `OmniDeployment[]` + */ +export const collectDeployments = async ( + hre: HardhatRuntimeEnvironment, + contractName: string +): Promise => { + assertHardhatDeploy(hre) + + const deployments: OmniDeployment[] = [] + const networkNamesByEid = getNetworkNamesByEid(hre) + + for (const [eid, networkName] of networkNamesByEid) { + const env = await getNetworkRuntimeEnvironment(networkName) + const deployment = await env.deployments.getOrNull(contractName) + if (deployment == null) continue + + deployments.push({ eid, deployment }) + } + + return deployments +} diff --git a/packages/utils-evm-hardhat/src/omnigraph/index.ts b/packages/utils-evm-hardhat/src/omnigraph/index.ts new file mode 100644 index 000000000..663b1e846 --- /dev/null +++ b/packages/utils-evm-hardhat/src/omnigraph/index.ts @@ -0,0 +1 @@ +export * from './coordinates' diff --git a/packages/utils-evm-hardhat/src/runtime.ts b/packages/utils-evm-hardhat/src/runtime.ts index 531177bd8..dda1fbb66 100644 --- a/packages/utils-evm-hardhat/src/runtime.ts +++ b/packages/utils-evm-hardhat/src/runtime.ts @@ -1,4 +1,4 @@ -import type { HardhatRuntimeEnvironment, EIP1193Provider } from 'hardhat/types' +import type { HardhatRuntimeEnvironment, EIP1193Provider, Network } from 'hardhat/types' import pMemoize from 'p-memoize' import { Web3Provider } from '@ethersproject/providers' @@ -100,14 +100,7 @@ export const wrapEIP1193Provider = (provider: EIP1193Provider): Web3Provider => export const createNetworkEnvironmentFactory = ( hre: HardhatRuntimeEnvironment ): EndpointBasedFactory => { - const networks = Object.entries(hre.config.networks) - const networkNamesByEndpointId: Map = new Map( - networks.flatMap(([networkName, { endpointId }]) => { - if (endpointId == null) return [] - - return [[endpointId, networkName]] - }) - ) + const networkNamesByEndpointId = getNetworkNamesByEid(hre) return async (eid) => { const networkName = networkNamesByEndpointId.get(eid) @@ -116,3 +109,25 @@ export const createNetworkEnvironmentFactory = ( return getNetworkRuntimeEnvironment(networkName) } } + +/** + * Creates a mapping between EndpointId and network name + * based on the hardhat project configuration. + * + * It will silently ignore networks that don't have `endpointId` + * specified in their network configuration. + * + * @param hre `HardhatRuntimeEnvironment` + * @returns `Map` + */ +export const getNetworkNamesByEid = (hre: HardhatRuntimeEnvironment): Map => { + const networks = Object.entries(hre.config.networks) + + return new Map( + networks.flatMap(([networkName, networkConfig]) => { + if (networkConfig.endpointId == null) return [] + + return [[networkConfig.endpointId, networkName]] + }) + ) +} diff --git a/packages/utils-evm-hardhat/test/omnigraph/coordinates.test.ts b/packages/utils-evm-hardhat/test/omnigraph/coordinates.test.ts new file mode 100644 index 000000000..5c6a7c4ef --- /dev/null +++ b/packages/utils-evm-hardhat/test/omnigraph/coordinates.test.ts @@ -0,0 +1,118 @@ +import { collectDeployments } from '../../src/omnigraph' +import { expect } from 'chai' +import { describe } from 'mocha' +import hre from 'hardhat' +import { DeploymentSubmission } from 'hardhat-deploy/dist/types' +import { getNetworkRuntimeEnvironment } from '../../src/runtime' + +describe('omnigraph/coordinates', () => { + beforeEach(async () => { + const bscTestnetRuntime = await getNetworkRuntimeEnvironment('bsc-testnet') + const ethMainnetRuntime = await getNetworkRuntimeEnvironment('ethereum-mainnet') + const ethTestnetRuntime = await getNetworkRuntimeEnvironment('ethereum-testnet') + + await bscTestnetRuntime.deployments.delete('MyWeirdContract') + await ethMainnetRuntime.deployments.delete('MyWeirdContract') + await ethTestnetRuntime.deployments.delete('MyWeirdContract') + }) + + describe('collectDeployments', () => { + it('should return an empty array if there are no deployments', async () => { + const deployments = await collectDeployments(hre, 'NonExistentContract') + + expect(deployments).to.eql([]) + }) + + it('should skip a network if endpointId is missing', async () => { + // First we make sure that the network we want to deploy to has no endpointId + const bscRuntime = await getNetworkRuntimeEnvironment('bsc-testnet') + expect(bscRuntime.network.config.endpointId).to.be.undefined + + // Now we create a mock deployment + const now = Date.now() + const deploymentSubmission = { + args: [now], + } as DeploymentSubmission + + // Save it + await bscRuntime.deployments.save('MyWeirdContract', deploymentSubmission) + + // And check that it will not be picked up + const deployments = await collectDeployments(hre, 'MyWeirdContract') + + expect(deployments).to.eql([]) + }) + + it('should skip a network if deployment is missing', async () => { + // First we make sure that the network we want to deploy to has an endpointId + const ethRuntime = await getNetworkRuntimeEnvironment('ethereum-mainnet') + expect(ethRuntime.network.config.endpointId).not.to.be.undefined + + // Now we create a mock deployment + const now = Date.now() + const deploymentSubmission = { + args: [now], + } as DeploymentSubmission + + // Save it + await ethRuntime.deployments.save('MyWeirdContract', deploymentSubmission) + + // And check that it will not be picked up + const deployments = await collectDeployments(hre, 'MyWeirdContract') + + // Now check that bsc-testnet and ethereum-testnet did not get included + expect(deployments).to.eql([ + { + eid: ethRuntime.network.config.endpointId, + deployment: { + ...deploymentSubmission, + numDeployments: 1, + }, + }, + ]) + }) + + it('should include all deployments that have endpointId configured', async () => { + // First we make sure that the networks we want to deploy to have endpointId + const ethMainnetRuntime = await getNetworkRuntimeEnvironment('ethereum-mainnet') + const ethTestnetRuntime = await getNetworkRuntimeEnvironment('ethereum-testnet') + expect(ethMainnetRuntime.network.config.endpointId).not.to.be.undefined + expect(ethTestnetRuntime.network.config.endpointId).not.to.be.undefined + + // Now we create mock deployments + const now = Date.now() + const deploymentSubmissionMainnet = { + args: ['mainnet', now], + } as DeploymentSubmission + + const deploymentSubmissionTestnet = { + args: ['testnet', now], + } as DeploymentSubmission + + // Save it + await ethMainnetRuntime.deployments.save('MyWeirdContract', deploymentSubmissionMainnet) + await ethTestnetRuntime.deployments.save('MyWeirdContract', deploymentSubmissionTestnet) + + // And check that it will not be picked up + const deployments = await collectDeployments(hre, 'MyWeirdContract') + + // Now check that bsc-testnet and ethereum-testnet did not get included + expect(deployments).to.eql([ + { + eid: ethMainnetRuntime.network.config.endpointId, + deployment: { + ...deploymentSubmissionMainnet, + numDeployments: 1, + }, + }, + { + eid: ethTestnetRuntime.network.config.endpointId, + deployment: { + ...deploymentSubmissionTestnet, + numDeployments: 1, + }, + }, + ]) + }) + }) +})