From fe1542e25e24174fe2c40e6b49b51f0522fb1cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1n=20Jakub=20Nani=C5=A1ta?= Date: Wed, 10 Jan 2024 17:06:12 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=9A=20Add=20ability=20to=20list=20all?= =?UTF-8?q?=20hardhat=20networks=20(#189)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/rude-mugs-protect.md | 5 ++ packages/devtools-evm-hardhat/src/runtime.ts | 89 +++++++++++++++---- .../test/__snapshots__/runtime.test.ts.snap | 18 ++++ .../devtools-evm-hardhat/test/runtime.test.ts | 64 ++++++++++++- 4 files changed, 157 insertions(+), 19 deletions(-) create mode 100644 .changeset/rude-mugs-protect.md create mode 100644 packages/devtools-evm-hardhat/test/__snapshots__/runtime.test.ts.snap diff --git a/.changeset/rude-mugs-protect.md b/.changeset/rude-mugs-protect.md new file mode 100644 index 000000000..1e3056f32 --- /dev/null +++ b/.changeset/rude-mugs-protect.md @@ -0,0 +1,5 @@ +--- +"@layerzerolabs/devtools-evm-hardhat": patch +--- + +Add getEidsByNetworkName utility diff --git a/packages/devtools-evm-hardhat/src/runtime.ts b/packages/devtools-evm-hardhat/src/runtime.ts index c6d16fe47..4c74768fb 100644 --- a/packages/devtools-evm-hardhat/src/runtime.ts +++ b/packages/devtools-evm-hardhat/src/runtime.ts @@ -9,6 +9,7 @@ import { EndpointId } from '@layerzerolabs/lz-definitions' import { EndpointBasedFactory, Factory, formatEid } from '@layerzerolabs/devtools' import { EthersProviderWrapper } from '@nomiclabs/hardhat-ethers/internal/ethers-provider-wrapper' import assert from 'assert' +import memoize from 'micro-memoize' /** * Helper type for when we need to grab something asynchronously by the network name @@ -167,25 +168,79 @@ export const getNetworkNameForEid = ( eid: EndpointId, hre: HardhatRuntimeEnvironment = getDefaultRuntimeEnvironment() ): string => { - const networkNames: string[] = [] + // We are using getEidsByNetworkName to get the nice validation of network config + const eidsByNetworkName = getEidsByNetworkName(hre) - for (const [networkName, networkConfig] of Object.entries(hre.config.networks)) { - // This is basically just an extra condition that ensures that even if the user - // passes undefined / null despite the TypeScript telling them not to, they won't get a messed up return value - if (networkConfig.eid == null) continue - - if (eid === networkConfig.eid) networkNames.push(networkName) + for (const [networkName, networkEid] of Object.entries(eidsByNetworkName)) { + if (networkEid === eid) return networkName } - // Here we error out of the user by accident specified the same eid for multiple networks - assert( - networkNames.length < 2, - `Multiple networks found with 'eid' set to ${eid} (${formatEid(eid)}): ${networkNames.join(', ')}` - ) - // Here we error out if there are no networks with this eid - const networkName = networkNames.at(0) - assert(networkName, `Could not find a network for eid ${eid} (${formatEid(eid)})`) - - return networkName + assert(false, `Could not find a network for eid ${eid} (${formatEid(eid)})`) } + +/** + * Gets a record containing the mapping between network names and endpoint IDs. + * Will also return the network names for which the `eid` has not been defined + * + * Throws if there are multiple networks defined with the same `eid` + * + * @param {HardhatRuntimeEnvironment | undefined} [hre] + * @returns {Record} + */ +export const getEidsByNetworkName = memoize( + (hre: HardhatRuntimeEnvironment = getDefaultRuntimeEnvironment()): Record => { + // First we get the network name -> network config pairs + const networkEntries = Object.entries(hre.config.networks) + // And map the network config to an endpoint ID + const eidEntries = networkEntries.map( + ([networkName, networkConfig]) => [networkName, networkConfig.eid] as const + ) + // Now we turn the entries back into a record + const eidsByNetworkName = Object.fromEntries(eidEntries) + + // Now we check that the user has not configured the endpoint ID mapping incorrectly + // (i.e. there are more networks configured with the same endpoint ID) + // + // For this we'll drop all the networks whose endpoint IDs are not defined + const eidEntriesWithDefinedEid = eidEntries.filter(([_, eid]) => eid != null) + const definedEidsByNetworkName = Object.fromEntries(eidEntriesWithDefinedEid) + + // Now we grab the sets of unique network names and endpoint IDs + const allDefinedEids = new Set(Object.values(definedEidsByNetworkName)) + const allNetworkNames = new Set(Object.keys(definedEidsByNetworkName)) + + // If the number of unique networks matches the number of unique endpoint IDs, there are no duplicates + if (allDefinedEids.size === allNetworkNames.size) return eidsByNetworkName + + // At this point the number of defined endpoint IDs can only be lower than + // the number of defined network names (since network names are taken from the keys + // of an object and endpoint IDs from its values) + // + // To let the user know whihc networks to fix, we need to grab all the ones that + // have been duplicated + // + // We are not claiming any efficiency of this algorithm as we don't expect any large numbers of networks + const duplicatedNetworkNames = Array.from(allDefinedEids) + // First we grab all the network names with this endpoint ID + .map((eid) => + eidEntriesWithDefinedEid.flatMap(([networkName, definedEid]) => + eid === definedEid ? [networkName] : [] + ) + ) + // Then we find all the network names listed more than once + .filter((networkNames) => networkNames.length > 1) + + // Now we let the user know which network names have identical endpoint IDs + const messages = duplicatedNetworkNames + .map( + (networkNames) => + `- ${networkNames.join(', ')} have eid set to ${formatEid(eidsByNetworkName[networkNames[0]!]!)}` + ) + .join('\n') + + throw new Error( + `Found multiple networks configured with the same 'eid':\n\n${messages}\n\nPlease fix this in your hardhat config.` + ) + } +) diff --git a/packages/devtools-evm-hardhat/test/__snapshots__/runtime.test.ts.snap b/packages/devtools-evm-hardhat/test/__snapshots__/runtime.test.ts.snap new file mode 100644 index 000000000..3398ea1f2 --- /dev/null +++ b/packages/devtools-evm-hardhat/test/__snapshots__/runtime.test.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`runtime getEidsByNetworkName should throw if there are more networks defined with the same eid 1`] = ` +"Found multiple networks configured with the same 'eid': + +- one, two, three have eid set to APTOS_MAINNET +- four, five have eid set to BASE_TESTNET + +Please fix this in your hardhat config." +`; + +exports[`runtime getNetworkNameForEid should throw if there are more networks defined with the same eid 1`] = ` +"Found multiple networks configured with the same 'eid': + +- one, two, three have eid set to APTOS_MAINNET + +Please fix this in your hardhat config." +`; diff --git a/packages/devtools-evm-hardhat/test/runtime.test.ts b/packages/devtools-evm-hardhat/test/runtime.test.ts index 8245f12d5..515e24fc0 100644 --- a/packages/devtools-evm-hardhat/test/runtime.test.ts +++ b/packages/devtools-evm-hardhat/test/runtime.test.ts @@ -1,6 +1,12 @@ import hre from 'hardhat' import { DeploymentsManager } from 'hardhat-deploy/dist/src/DeploymentsManager' -import { createGetHreByEid, getEidForNetworkName, getNetworkNameForEid, getHreByNetworkName } from '@/runtime' +import { + createGetHreByEid, + getEidForNetworkName, + getNetworkNameForEid, + getHreByNetworkName, + getEidsByNetworkName, +} from '@/runtime' import type { DeploymentSubmission } from 'hardhat-deploy/dist/types' import { EndpointId } from '@layerzerolabs/lz-definitions' import { HardhatRuntimeEnvironment } from 'hardhat/types' @@ -115,7 +121,61 @@ describe('runtime', () => { expect(() => getNetworkNameForEid(EndpointId.APTOS_MAINNET, hre as unknown as HardhatRuntimeEnvironment) - ).toThrow(`Multiple networks found with 'eid' set to 108 (APTOS_MAINNET): one, two, three`) + ).toThrowErrorMatchingSnapshot() + }) + }) + + describe('getEidsByNetworkName', () => { + it('should return undefined for networks that do not have eid defined', async () => { + const hre = { + config: { + networks: { + one: { + eid: EndpointId.APTOS_MAINNET, + }, + two: { + eid: EndpointId.BASE_TESTNET, + }, + three: {}, + four: {}, + }, + }, + } + + expect(getEidsByNetworkName(hre as unknown as HardhatRuntimeEnvironment)).toEqual({ + one: EndpointId.APTOS_MAINNET, + two: EndpointId.BASE_TESTNET, + three: undefined, + four: undefined, + }) + }) + + it('should throw if there are more networks defined with the same eid', () => { + const hre = { + config: { + networks: { + one: { + eid: EndpointId.APTOS_MAINNET, + }, + two: { + eid: EndpointId.APTOS_MAINNET, + }, + three: { + eid: EndpointId.APTOS_MAINNET, + }, + four: { + eid: EndpointId.BASE_TESTNET, + }, + five: { + eid: EndpointId.BASE_TESTNET, + }, + }, + }, + } + + expect(() => + getEidsByNetworkName(hre as unknown as HardhatRuntimeEnvironment) + ).toThrowErrorMatchingSnapshot() }) }) })