Skip to content

Commit

Permalink
🪚 Add ability to list all hardhat networks (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista authored Jan 11, 2024
1 parent 9e3dc4d commit fe1542e
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-mugs-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@layerzerolabs/devtools-evm-hardhat": patch
---

Add getEidsByNetworkName utility
89 changes: 72 additions & 17 deletions packages/devtools-evm-hardhat/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<string, EndpointId | undefined>}
*/
export const getEidsByNetworkName = memoize(
(hre: HardhatRuntimeEnvironment = getDefaultRuntimeEnvironment()): Record<string, EndpointId | undefined> => {
// 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.`
)
}
)
Original file line number Diff line number Diff line change
@@ -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."
`;
64 changes: 62 additions & 2 deletions packages/devtools-evm-hardhat/test/runtime.test.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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()
})
})
})

0 comments on commit fe1542e

Please sign in to comment.