diff --git a/examples/oft/package.json b/examples/oft/package.json index 314b74847..6cd872b06 100644 --- a/examples/oft/package.json +++ b/examples/oft/package.json @@ -20,7 +20,7 @@ "@nomiclabs/hardhat-ethers": "^2.2.3", "@rushstack/eslint-patch": "^1.5.1", "ethers": "^5.7.0", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "hardhat-contract-sizer": "^2.10.0", "hardhat-deploy": "^0.11.43", "hardhat-deploy-ethers": "^0.4.1", diff --git a/packages/protocol-utils-evm/src/uln302/schema.ts b/packages/protocol-utils-evm/src/uln302/schema.ts index e77f21fde..2df5b2f1d 100644 --- a/packages/protocol-utils-evm/src/uln302/schema.ts +++ b/packages/protocol-utils-evm/src/uln302/schema.ts @@ -1,4 +1,5 @@ import { AddressSchema } from '@layerzerolabs/utils' +import { BigNumberishNumberSchema } from '@layerzerolabs/utils-evm' import { BigNumberishBigintSchema } from '@layerzerolabs/utils-evm' import { z } from 'zod' @@ -12,6 +13,14 @@ export const Uln302UlnConfigSchema = z.object({ optionalDVNThreshold: z.coerce.number().int().nonnegative(), }) +/** + * Schema for parsing an ethers-specific ExecutorConfig into a common format + */ +export const Uln302ExecutorConfigSchema = z.object({ + maxMessageSize: BigNumberishNumberSchema, + executor: AddressSchema, +}) + /** * Schema for parsing a common UlnConfig into a ethers-specific format */ diff --git a/packages/protocol-utils-evm/src/uln302/sdk.ts b/packages/protocol-utils-evm/src/uln302/sdk.ts index cc77763e0..4117a9ee1 100644 --- a/packages/protocol-utils-evm/src/uln302/sdk.ts +++ b/packages/protocol-utils-evm/src/uln302/sdk.ts @@ -1,14 +1,14 @@ import type { EndpointId } from '@layerzerolabs/lz-definitions' import type { IUln302, Uln302ExecutorConfig, Uln302UlnConfig } from '@layerzerolabs/protocol-utils' -import { formatEid, type OmniTransaction } from '@layerzerolabs/utils' -import { omniContractToPoint, type OmniContract } from '@layerzerolabs/utils-evm' -import { Uln302UlnConfigInputSchema, Uln302UlnConfigSchema } from './schema' +import { Address, formatEid, type OmniTransaction } from '@layerzerolabs/utils' +import { omniContractToPoint, type OmniContract, makeZero } from '@layerzerolabs/utils-evm' +import { Uln302ExecutorConfigSchema, Uln302UlnConfigInputSchema, Uln302UlnConfigSchema } from './schema' export class Uln302 implements IUln302 { constructor(public readonly contract: OmniContract) {} - async getUlnConfig(eid: EndpointId, address: string): Promise { - const config = await this.contract.contract.getUlnConfig(address, eid) + async getUlnConfig(eid: EndpointId, address?: Address | null | undefined): Promise { + const config = await this.contract.contract.getUlnConfig(makeZero(address), eid) // Now we convert the ethers-specific object into the common structure // @@ -17,6 +17,16 @@ export class Uln302 implements IUln302 { return Uln302UlnConfigSchema.parse({ ...config }) } + async getExecutorConfig(eid: EndpointId, address?: Address | null | undefined): Promise { + const config = await this.contract.contract.getExecutorConfig(makeZero(address), eid) + + // Now we convert the ethers-specific object into the common structure + // + // Here we need to spread the config into an object because what ethers gives us + // is actually an array with extra properties + return Uln302ExecutorConfigSchema.parse({ ...config }) + } + async setDefaultExecutorConfig(eid: EndpointId, config: Uln302ExecutorConfig): Promise { const data = this.contract.contract.interface.encodeFunctionData('setDefaultExecutorConfigs', [ [{ eid, config }], diff --git a/packages/protocol-utils/src/uln302/types.ts b/packages/protocol-utils/src/uln302/types.ts index 9cbe79577..51d89b05f 100644 --- a/packages/protocol-utils/src/uln302/types.ts +++ b/packages/protocol-utils/src/uln302/types.ts @@ -2,7 +2,8 @@ import type { Address, OmniGraph, OmniPointBasedFactory, OmniTransaction } from import type { EndpointId } from '@layerzerolabs/lz-definitions' export interface IUln302 { - getUlnConfig(eid: EndpointId, address: Address): Promise + getUlnConfig(eid: EndpointId, address?: Address | null | undefined): Promise + getExecutorConfig(eid: EndpointId, address?: Address | null | undefined): Promise setDefaultExecutorConfig(eid: EndpointId, config: Uln302ExecutorConfig): Promise setDefaultUlnConfig(eid: EndpointId, config: Uln302UlnConfig): Promise } diff --git a/packages/test-evm-node/package.json b/packages/test-evm-node/package.json index a7c4b2145..33dc7b4a7 100644 --- a/packages/test-evm-node/package.json +++ b/packages/test-evm-node/package.json @@ -14,7 +14,7 @@ "start": "npx hardhat node --hostname 0.0.0.0" }, "devDependencies": { - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "ts-node": "^10.9.1", "typescript": "^5.2.2" } diff --git a/packages/ua-utils-evm-hardhat-test/package.json b/packages/ua-utils-evm-hardhat-test/package.json index 2d76e2e4c..aaecef14a 100644 --- a/packages/ua-utils-evm-hardhat-test/package.json +++ b/packages/ua-utils-evm-hardhat-test/package.json @@ -39,7 +39,7 @@ "chai": "^4.3.10", "chai-as-promised": "^7.1.1", "ethers": "^5.7.0", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "hardhat-deploy": "^0.11.22", "hardhat-deploy-ethers": "^0.3.0-beta.12", "sinon": "^17.0.1", diff --git a/packages/ua-utils-evm-hardhat/package.json b/packages/ua-utils-evm-hardhat/package.json index 6322d7db3..404796e7d 100644 --- a/packages/ua-utils-evm-hardhat/package.json +++ b/packages/ua-utils-evm-hardhat/package.json @@ -51,7 +51,7 @@ "dotenv": "^16.0.3", "ethers": "^5.7.0", "fast-check": "^3.14.0", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "hardhat-deploy": "^0.11.22", "jest": "^29.7.0", "ts-jest": "^29.1.1", @@ -72,7 +72,7 @@ "@layerzerolabs/utils-evm-hardhat": "~0.0.2", "@nomiclabs/hardhat-ethers": "^2.2.3", "ethers": "^5.5.2", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "hardhat-deploy": "^0.11.22" } } \ No newline at end of file diff --git a/packages/utils-evm-hardhat/README.md b/packages/utils-evm-hardhat/README.md index 95794df11..3546518ee 100644 --- a/packages/utils-evm-hardhat/README.md +++ b/packages/utils-evm-hardhat/README.md @@ -25,3 +25,68 @@ pnpm add @layerzerolabs/utils-evm-hardhat npm install @layerzerolabs/utils-evm-hardhat ``` + +## API Documentation + +### Omnigraph types + +#### OmniContract + +Interface that represents an ethers.js contract connected to a particular endpoint + +```typescript +import { EndpointId } from "@layerzerolabs/lz-definitions"; +import { OmniContract } from "@layerzerolabs/utils-evm"; + +const omniContract: OmniContract = { + eid: EndpointId.ETHEREUM_MAINNET, + contract: new Contract(address, abi), +}; +``` + +#### OmniContractFactory + +Type that represents a function that can return an `OmniContract` based on an `OmniPoint` + +```typescript +import { EndpointId } from "@layerzerolabs/lz-definitions"; +import { OmniPoint } from "@layerzerolabs/utils"; +import { OmniContractFactory } from "@layerzerolabs/utils-evm"; + +declare const omniContractFactory: OmniContractFactory; + +const omniPoint: OmniPoint = { + eid: EndpointId.ETHEREUM_MAINNET, + address: "0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8", +}; + +const omniContract = await omniContractFactory(omniPoint); +``` + +### Address utilities + +#### ignoreZero(address: Address | null | undefined) + +Turns EVM zero addresses to `undefined` + +```typescript +import { ignoreZero } from "@layerzerolabs/utils-evm"; + +ignoreZero("0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8"); // Returns '0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8' +ignoreZero("0x0000000000000000000000000000000000000000"); // Returns undefined +ignoreZero(undefined); // Returns undefined +ignoreZero(null); // Returns undefined +``` + +#### makeZero(address) + +Turns `null` and `undefined` into EVM zero address + +```typescript +import { makeZero } from "@layerzerolabs/utils-evm"; + +makeZero("0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8"); // Returns '0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8' +makeZero("0x0000000000000000000000000000000000000000"); // Returns '0x0000000000000000000000000000000000000000' +makeZero(undefined); // Returns '0x0000000000000000000000000000000000000000' +makeZero(null); // Returns '0x0000000000000000000000000000000000000000' +``` diff --git a/packages/utils-evm-hardhat/package.json b/packages/utils-evm-hardhat/package.json index 6a056f1e5..6fa656201 100644 --- a/packages/utils-evm-hardhat/package.json +++ b/packages/utils-evm-hardhat/package.json @@ -52,7 +52,7 @@ "@layerzerolabs/utils-evm": "~0.0.1", "@types/jest": "^29.5.10", "fast-check": "^3.14.0", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "hardhat-deploy": "^0.11.22", "jest": "^29.7.0", "p-memoize": "~4.0.1", @@ -69,7 +69,7 @@ "@ethersproject/providers": "^5.7.0", "@layerzerolabs/lz-definitions": "~1.5.68", "@layerzerolabs/utils-evm": "~0.0.1", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "hardhat-deploy": "^0.9.19" } } \ No newline at end of file diff --git a/packages/utils-evm-hardhat/src/runtime.ts b/packages/utils-evm-hardhat/src/runtime.ts index 88835ed6e..454126032 100644 --- a/packages/utils-evm-hardhat/src/runtime.ts +++ b/packages/utils-evm-hardhat/src/runtime.ts @@ -6,13 +6,22 @@ import { ConfigurationError } from './errors' import { HardhatContext } from 'hardhat/internal/context' import { Environment as HardhatRuntimeEnvironmentImplementation } from 'hardhat/internal/core/runtime-environment' import { EndpointId } from '@layerzerolabs/lz-definitions' -import { EndpointBasedFactory } from '@layerzerolabs/utils' +import { EndpointBasedFactory, formatEid } from '@layerzerolabs/utils' +import assert from 'assert' /** * Helper type for when we need to grab something asynchronously by the network name */ export type GetByNetwork = (networkName: string) => Promise +/** + * Returns the default hardhat context for the project, i.e. + * the context that the project has been setup with. + * + * Throws if there is no context. + * + * @returns {HardhatContext} + */ export const getDefaultContext = (): HardhatContext => { // Context is registered globally as a singleton and can be accessed // using the static methods of the HardhatContext class @@ -26,6 +35,13 @@ export const getDefaultContext = (): HardhatContext => { } } +/** + * Returns the default `HardhatRuntimeEnvironment` (`hre`) for the project. + * + * Throws if there is no `HardhatRuntimeEnvironment`. + * + * @returns {HardhatRuntimeEnvironment} + */ export const getDefaultRuntimeEnvironment = (): HardhatRuntimeEnvironment => { // The first step is to get the hardhat context const context = getDefaultContext() @@ -50,6 +66,8 @@ export const getDefaultRuntimeEnvironment = (): HardhatRuntimeEnvironment => { * // All the ususal properties are present * env.deployments.get("MyContract") * ``` + * + * @returns {Promise} */ export const getNetworkRuntimeEnvironment: GetByNetwork = pMemoize(async (networkName) => { const context = getDefaultContext() @@ -83,8 +101,8 @@ export const getNetworkRuntimeEnvironment: GetByNetwork new Web3Provider(provider) @@ -99,42 +117,70 @@ export const wrapEIP1193Provider = (provider: EIP1193Provider): Web3Provider => * const env = factory(EndpointId.FANTOM_MAINNET) * ``` * - * @param hre `HardhatRuntimeEnvironment` - * @returns `(eid: EndpointId) => Promise` + * @param {HardhatRuntimeEnvironment | undefined} [hre] + * @returns {(eid: EndpointId) => Promise} */ export const createNetworkEnvironmentFactory = ( hre: HardhatRuntimeEnvironment = getDefaultRuntimeEnvironment() ): EndpointBasedFactory => { - const networkNamesByEndpointId = getNetworkNamesByEid(hre) + return async (eid) => getNetworkRuntimeEnvironment(getNetworkNameForEid(eid, hre)) +} - return async (eid) => { - const networkName = networkNamesByEndpointId.get(eid) - if (networkName == null) throw new Error(`No network defined for eid ${eid}`) +/** + * Gets an EndpointId defined in the hardhat config + * for a particular network name (as an `eid` property). + * + * Throws if the network or the eid are not defined + * + * @param {string} networkName + * @param {HardhatRuntimeEnvironment | undefined} [hre] + * @returns {EndpointId} + */ +export const getEidForNetworkName = ( + networkName: string, + hre: HardhatRuntimeEnvironment = getDefaultRuntimeEnvironment() +): EndpointId => { + const networkConfig = hre.config.networks[networkName] + assert(networkConfig, `Network '${networkName}' is not defined in hardhat config`) + assert(networkConfig.eid != null, `Network '${networkName}' does not have 'eid' property defined in its config`) - return getNetworkRuntimeEnvironment(networkName) - } + return networkConfig.eid } /** - * Creates a mapping between EndpointId and network name - * based on the hardhat project configuration. + * Gets a network name with its `eid` property matching + * a particular `eid` * - * It will silently ignore networks that don't have `eid` - * specified in their network configuration. + * Throws if there is no such network or if there are multiple + * networks defined with the same `eid` * - * @param hre `HardhatRuntimeEnvironment` - * @returns `Map` + * @param {EndpointId} eid + * @param {HardhatRuntimeEnvironment | undefined} [hre] + * @returns {string} */ -export const getNetworkNamesByEid = ( +export const getNetworkNameForEid = ( + eid: EndpointId, hre: HardhatRuntimeEnvironment = getDefaultRuntimeEnvironment() -): Map => { - const networks = Object.entries(hre.config.networks) +): string => { + const networkNames: string[] = [] + + 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 - return new Map( - networks.flatMap(([networkName, networkConfig]) => { - if (networkConfig.eid == null) return [] + if (eid === networkConfig.eid) networkNames.push(networkName) + } - return [[networkConfig.eid, 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 } diff --git a/packages/utils-evm-hardhat/test/runtime.test.ts b/packages/utils-evm-hardhat/test/runtime.test.ts index 61cb77eec..f01d3dadf 100644 --- a/packages/utils-evm-hardhat/test/runtime.test.ts +++ b/packages/utils-evm-hardhat/test/runtime.test.ts @@ -1,8 +1,14 @@ import hre from 'hardhat' import { DeploymentsManager } from 'hardhat-deploy/dist/src/DeploymentsManager' -import { createNetworkEnvironmentFactory, getNetworkRuntimeEnvironment } from '@/runtime' +import { + createNetworkEnvironmentFactory, + getEidForNetworkName, + getNetworkNameForEid, + getNetworkRuntimeEnvironment, +} from '@/runtime' import type { DeploymentSubmission } from 'hardhat-deploy/dist/types' import { EndpointId } from '@layerzerolabs/lz-definitions' +import { HardhatRuntimeEnvironment } from 'hardhat/types' jest.spyOn(DeploymentsManager.prototype, 'getChainId').mockResolvedValue('1') @@ -54,7 +60,9 @@ describe('runtime', () => { describe('createNetworkEnvironmentFactory', () => { it('should reject with an endpoint that is not in the hardhat config', async () => { - await expect(createNetworkEnvironmentFactory(hre)(EndpointId.CATHAY_TESTNET)).rejects.toBeTruthy() + await expect(createNetworkEnvironmentFactory(hre)(EndpointId.CATHAY_TESTNET)).rejects.toThrow( + 'Could not find a network for eid 10171 (CATHAY_TESTNET)' + ) }) it('should return a HardhatRuntimeEnvironment with correct network', async () => { @@ -66,4 +74,53 @@ describe('runtime', () => { }) }) }) + + describe('getEidForNetworkName', () => { + it('should throw if there is no such network defined', () => { + expect(() => getEidForNetworkName('nonsense')).toThrow( + `Network 'nonsense' is not defined in hardhat config` + ) + }) + + it('should throw if the network does not have eid defined', () => { + expect(() => getEidForNetworkName('bsc-testnet')).toThrow( + `Network 'bsc-testnet' does not have 'eid' property defined in its config` + ) + }) + + it('should return eid if defined', () => { + expect(getEidForNetworkName('ethereum-testnet')).toBe(EndpointId.ETHEREUM_TESTNET) + expect(getEidForNetworkName('ethereum-mainnet')).toBe(EndpointId.ETHEREUM_MAINNET) + }) + }) + + describe('getNetworkNameForEid', () => { + it('should throw if there is no such eid defined', () => { + expect(() => getNetworkNameForEid(EndpointId.CATHAY_TESTNET)).toThrow( + 'Could not find a network for eid 10171 (CATHAY_TESTNET)' + ) + }) + + 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, + }, + }, + }, + } + + expect(() => + getNetworkNameForEid(EndpointId.APTOS_MAINNET, hre as unknown as HardhatRuntimeEnvironment) + ).toThrow(`Multiple networks found with 'eid' set to 108 (APTOS_MAINNET): one, two, three`) + }) + }) }) diff --git a/packages/utils-evm-test/package.json b/packages/utils-evm-test/package.json index e4ed335f6..084f44c14 100644 --- a/packages/utils-evm-test/package.json +++ b/packages/utils-evm-test/package.json @@ -28,7 +28,7 @@ "chai": "^4.3.10", "ethers": "^5.7.0", "fast-check": "^3.14.0", - "hardhat": "^2.19.0", + "hardhat": "^2.19.2", "ts-node": "^10.9.1", "typescript": "^5.2.2" } diff --git a/packages/utils-evm/src/schema.ts b/packages/utils-evm/src/schema.ts index a4082c1ee..8b0bf5e5d 100644 --- a/packages/utils-evm/src/schema.ts +++ b/packages/utils-evm/src/schema.ts @@ -4,3 +4,5 @@ import { BigNumber, BigNumberish, isBigNumberish } from '@ethersproject/bignumbe export const BigNumberishSchema = z.custom((value: unknown) => isBigNumberish(value)) export const BigNumberishBigintSchema = BigNumberishSchema.transform(BigNumber.from).transform((bn) => bn.toBigInt()) + +export const BigNumberishNumberSchema = BigNumberishSchema.transform(BigNumber.from).transform((bn) => bn.toNumber()) diff --git a/packages/utils-evm/test/schema.test.ts b/packages/utils-evm/test/schema.test.ts index e485a0ff0..2a34c869e 100644 --- a/packages/utils-evm/test/schema.test.ts +++ b/packages/utils-evm/test/schema.test.ts @@ -1,9 +1,6 @@ import fc from 'fast-check' -import { AddressZero } from '@ethersproject/constants' -import { evmAddressArbitrary } from '@layerzerolabs/test-utils' -import { ignoreZero, makeZero } from '@/address' import { BigNumber, BigNumberish } from '@ethersproject/bignumber' -import { BigNumberishBigintSchema, BigNumberishSchema } from '@/schema' +import { BigNumberishBigintSchema, BigNumberishNumberSchema, BigNumberishSchema } from '@/schema' describe('schema', () => { const bigIntArbitrary = fc.bigInt() @@ -41,4 +38,29 @@ describe('schema', () => { ) }) }) + + describe('BigNumberishNumberSchema', () => { + it('should parse BigNumberish into a number if within bounds', () => { + fc.assert( + fc.property(bigNumberishArbitrary, (bigNumberish) => { + fc.pre(BigNumber.from(bigNumberish).abs().lte(BigInt(Number.MAX_SAFE_INTEGER))) + + const parsed = BigNumberishNumberSchema.parse(bigNumberish) + + expect(typeof parsed).toBe('number') + expect(BigNumber.from(parsed)).toEqual(BigNumber.from(bigNumberish)) + }) + ) + }) + + it('should throw an error if there is an overflow', () => { + fc.assert( + fc.property(bigNumberishArbitrary, (bigNumberish) => { + fc.pre(BigNumber.from(bigNumberish).abs().gt(BigInt(Number.MAX_SAFE_INTEGER))) + + expect(() => BigNumberishNumberSchema.parse(bigNumberish)).toThrow('overflow') + }) + ) + }) + }) }) diff --git a/packages/utils/README.md b/packages/utils/README.md index 819fd23e9..3a405ea8f 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -18,6 +18,49 @@ ## Installation -```sh -$ npm install @layerzerolabs/utils +```bash +yarn add @layerzerolabs/utils + +pnpm add @layerzerolabs/utils + +npm install @layerzerolabs/utils +``` + +## API Documentation + +### Omnigraph types + +#### OmniPoint + +Type that uniquely identifies a contract (a point) in an omnichain universe. It consists of `eid` (`EndpointId`) to which the contract is connected to and the address of the contract. + +```typescript +import { EndpointId } from "@layerzerolabs/lz-definitions"; +import { OmniPoint } from "@layerzerolabs/utils"; + +const omniPoint: OmniPoint = { + eid: EndpointId.ETHEREUM_MAINNET, + address: "0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8", +}; +``` + +#### OmniVector + +Type that uniquely identifies a connection between two `OmniPoint`s, two contracts in an omnichain universe. It consists of two `OmniPoint` instances - `from` and `to`. + +```typescript +import { EndpointId } from "@layerzerolabs/lz-definitions"; +import { OmniVector } from "@layerzerolabs/utils"; + +const from: OmniPoint = { + eid: EndpointId.ETHEREUM_MAINNET, + address: "0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8", +}; + +const to: OmniPoint = { + eid: EndpointId.AVALANCHE_MAINNET, + address: "0xEe6cF2E1Bc7645F8439d241ce37820305F2BB3F8", +}; + +const omniVector: OmniVector = { from, to }; ``` diff --git a/yarn.lock b/yarn.lock index b18e2c8c5..dfeb99c51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6844,10 +6844,10 @@ hardhat-deploy@^0.11.22, hardhat-deploy@^0.11.43: qs "^6.9.4" zksync-web3 "^0.14.3" -hardhat@^2.19.0: - version "2.19.1" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.1.tgz#5e09e8070ecfc6109ba9d3a4a117ec2b0643032a" - integrity sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw== +hardhat@^2.19.2: + version "2.19.2" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.2.tgz#815819e4efd234941d495decb718b358d572e2c8" + integrity sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0"