diff --git a/packages/hardhat-utils/src/config.ts b/packages/hardhat-utils/src/config.ts index 934d89aa6..c1c3ac468 100644 --- a/packages/hardhat-utils/src/config.ts +++ b/packages/hardhat-utils/src/config.ts @@ -5,6 +5,19 @@ import { HardhatUserConfig } from "hardhat/types" import { join, dirname } from "path" import { createNetworkLogger } from "./logger" +const resolvePackageDirectory = (packageName: string): string => { + // The tricky bit here is the fact that if we resolve packages by their package name, + // we might be pointed to a file in some dist directory - node will just pick up the `main` + // entry in package.json and point us there + // + // So in order to get a stable path we choose package.json, pretty solid choice + const packageJsonName = join(packageName, "package.json") + // We now resolve the path to package.json + const packageJsonPath = require.resolve(packageJsonName) + // And return its directory + return dirname(packageJsonPath) +} + /** * Helper utility that adds external deployment paths for all LayzerZero enabled networks. * This will make LayerZero contracts available in your deploy scripts and tasks. @@ -32,19 +45,10 @@ import { createNetworkLogger } from "./logger" * @returns `(config: THardhatUserConfig): THardhatUserConfig` Hardhat config decorator */ export const withLayerZeroDeployments = (...packageNames: string[]) => { - // The first thing we do is we resolve the paths to LayerZero packages const resolvedDeploymentsDirectories = packageNames - // The tricky bit here is the fact that if we resolve packages by their package name, - // we might be pointed to a file in some dist directory - node will just pick up the `main` - // entry in package.json and point us there - // - // So in order to get a stable path we choose package.json, pretty solid choice - .map((packageName) => join(packageName, "package.json")) - // We now resolve the path to package.json - .map((packageJsonPath) => require.resolve(packageJsonPath)) - // Take its directory - .map(dirname) - // And navigate to the deployments folder + // The first thing we do is we resolve the paths to LayerZero packages + .map(resolvePackageDirectory) + // Then navigate to the deployments folder .map((resolvedPackagePath) => join(resolvedPackagePath, "deployments")) // We return a function that will enrich hardhat config with the external deployments configuration @@ -103,3 +107,62 @@ export const withLayerZeroDeployments = (...packageNames: string[]) => { }, }) } + +/** + * Helper utility that appends external artifacts directories + * to existing hadrhat config + * + * ``` + * // hardhat.config.ts + * import { EndpointId } from "@layerzerolabs/lz-definitions" + * + * const config: HardhatUserConfig = { + * networks: { + * arbitrum: { + * endpointId: EndpointId.ARBITRUM_MAINNET + * }, + * fuji: { + * endpointId: EndpointId.AVALANCHE_TESTNET + * } + * } + * } + * + * export default withLayerZeroArtifacts("@layerzerolabs/lz-evm-sdk-v1") + * ``` + * + * @param packageNames `string[]` + * + * @returns `(config: THardhatUserConfig): THardhatUserConfig` Hardhat config decorator + */ +export const withLayerZeroArtifacts = (...packageNames: string[]) => { + const resolvedArtifactsDirectories = packageNames + // The first thing we do is we resolve the paths to LayerZero packages + .map(resolvePackageDirectory) + // Then navigate to the artifacts folder + .map((resolvedPackagePath) => join(resolvedPackagePath, "artifacts")) + + // We return a function that will enrich hardhat config with the external artifacts configuration + // + // This is a pretty standard way of enriching configuration files that leads to quite nice consumer code + return (config: THardhatUserConfig): THardhatUserConfig => { + // We'll first grab all the external artifacts already defined + const existingArtifacts = new Set(config.external?.contracts?.flatMap(({ artifacts }) => artifacts) ?? []) + + // And only append stuff if we have something new to say + const newArtifacts = new Set(resolvedArtifactsDirectories.filter((artifact) => !existingArtifacts.has(artifact))) + if (newArtifacts.size === 0) return config + + return { + ...config, + external: { + ...config.external, + contracts: [ + ...(config.external?.contracts ?? []), + { + artifacts: Array.from(newArtifacts), + }, + ], + }, + } + } +} diff --git a/packages/hardhat-utils/test/config.test.ts b/packages/hardhat-utils/test/config.test.ts index 84c75ab22..06a5f6239 100644 --- a/packages/hardhat-utils/test/config.test.ts +++ b/packages/hardhat-utils/test/config.test.ts @@ -1,11 +1,11 @@ import { EndpointId } from "@layerzerolabs/lz-definitions" import { expect } from "chai" import { describe } from "mocha" -import { withLayerZeroDeployments } from "../src/config" +import { withLayerZeroArtifacts, withLayerZeroDeployments } from "../src/config" import { dirname, join } from "path" describe("config", () => { - describe("withExternalDeployments()", () => { + describe("withLayerZeroDeployments()", () => { const resolvedLzEvmSdkPackageJson = dirname(require.resolve(join("@layerzerolabs/lz-evm-sdk-v1", "package.json"))) it("should add no external deployments if no networks have been specified", () => { @@ -105,4 +105,61 @@ describe("config", () => { }) }) }) + + describe("withLayerZeroArtifacts()", () => { + const resolvedLzEvmSdkPackageJson = dirname(require.resolve(join("@layerzerolabs/lz-evm-sdk-v1", "package.json"))) + + it("should append external artifacts", () => { + const config = { + networks: {}, + } + + expect(withLayerZeroArtifacts("@layerzerolabs/lz-evm-sdk-v1")(config)).to.eql({ + networks: {}, + external: { + contracts: [ + { + artifacts: [`${resolvedLzEvmSdkPackageJson}/artifacts`], + }, + ], + }, + }) + }) + + it("should not append duplicate external artifacts", () => { + const config = { + external: { + contracts: [ + { + artifacts: "./my/external/artifact", + }, + { + artifacts: ["./my/other/external/artifact"], + }, + ], + }, + networks: {}, + } + + const configWithSomePath = withLayerZeroArtifacts("@layerzerolabs/lz-evm-sdk-v1", "@layerzerolabs/lz-evm-sdk-v1")(config) + const configWithSomePathAgain = withLayerZeroArtifacts("@layerzerolabs/lz-evm-sdk-v1")(configWithSomePath) + + expect(configWithSomePathAgain).to.eql({ + networks: {}, + external: { + contracts: [ + { + artifacts: "./my/external/artifact", + }, + { + artifacts: ["./my/other/external/artifact"], + }, + { + artifacts: [`${resolvedLzEvmSdkPackageJson}/artifacts`], + }, + ], + }, + }) + }) + }) })