From 14e4883222dfa4d65a8a9b457c1cdcabf3be606b Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Fri, 10 Apr 2026 16:12:37 -0300 Subject: [PATCH 1/5] Optimize ArtifactsManager#artifactExists --- .../artifacts/artifact-manager.ts | 22 +++++++------------ packages/hardhat/src/types/artifacts.ts | 2 -- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/packages/hardhat/src/internal/builtin-plugins/artifacts/artifact-manager.ts b/packages/hardhat/src/internal/builtin-plugins/artifacts/artifact-manager.ts index c13f5d46475..4000802ac0a 100644 --- a/packages/hardhat/src/internal/builtin-plugins/artifacts/artifact-manager.ts +++ b/packages/hardhat/src/internal/builtin-plugins/artifacts/artifact-manager.ts @@ -81,22 +81,16 @@ export class ArtifactManagerImplementation implements ArtifactManager { public async artifactExists( contractNameOrFullyQualifiedName: string, ): Promise { - try { - // This throw if the artifact doesn't exist - await this.getArtifactPath(contractNameOrFullyQualifiedName); - - return true; - } catch (error) { - if (HardhatError.isHardhatError(error)) { - if ( - error.number === HardhatError.ERRORS.CORE.ARTIFACTS.NOT_FOUND.number - ) { - return false; - } - } + const { bareNameToFullyQualifiedNameMap, allFullyQualifiedNames } = + await this.#getFsData(); - throw error; + if (this.#isFullyQualifiedName(contractNameOrFullyQualifiedName)) { + return allFullyQualifiedNames.has(contractNameOrFullyQualifiedName); } + + return bareNameToFullyQualifiedNameMap.has( + contractNameOrFullyQualifiedName, + ); } public async getBuildInfoId( diff --git a/packages/hardhat/src/types/artifacts.ts b/packages/hardhat/src/types/artifacts.ts index a569bfa96b7..4491a661d4c 100644 --- a/packages/hardhat/src/types/artifacts.ts +++ b/packages/hardhat/src/types/artifacts.ts @@ -92,8 +92,6 @@ export interface ArtifactManager { * This function doesn't throw if the name is not unique. * * @param contractNameOrFullyQualifiedName Contract or fully qualified name. - * @throws Throws an error if a non-unique contract name is used, - * indicating which fully qualified names can be used instead. */ artifactExists(contractNameOrFullyQualifiedName: string): Promise; From edfa548e48076d7f127523354c173b48ed9494a7 Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Fri, 10 Apr 2026 17:04:23 -0300 Subject: [PATCH 2/5] Add changeset --- .changeset/twelve-rabbits-draw.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twelve-rabbits-draw.md diff --git a/.changeset/twelve-rabbits-draw.md b/.changeset/twelve-rabbits-draw.md new file mode 100644 index 00000000000..bfa33f39000 --- /dev/null +++ b/.changeset/twelve-rabbits-draw.md @@ -0,0 +1,5 @@ +--- +"hardhat": patch +--- + +Optimize `hre.artifacts.artifactExists()` From bd991f536829ad68fde500a61d85d05b6c0d7149 Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Fri, 10 Apr 2026 17:09:23 -0300 Subject: [PATCH 3/5] Add missing tests --- .../duplicate-names-project/.gitignore | 2 + .../contracts/a/Foo.sol | 6 ++ .../contracts/b/Foo.sol | 6 ++ .../duplicate-names-project/hardhat.config.ts | 1 + .../duplicate-names-project/package.json | 1 + .../artifacts/artifact-manager.ts | 65 +++++++++++++++++++ 6 files changed, 81 insertions(+) create mode 100644 packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/.gitignore create mode 100644 packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/a/Foo.sol create mode 100644 packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/b/Foo.sol create mode 100644 packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/hardhat.config.ts create mode 100644 packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/package.json diff --git a/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/.gitignore b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/.gitignore new file mode 100644 index 00000000000..e7f801166c5 --- /dev/null +++ b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/.gitignore @@ -0,0 +1,2 @@ +artifacts/ +cache/ diff --git a/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/a/Foo.sol b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/a/Foo.sol new file mode 100644 index 00000000000..d4a18f04b6e --- /dev/null +++ b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/a/Foo.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Foo { + uint public x; +} diff --git a/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/b/Foo.sol b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/b/Foo.sol new file mode 100644 index 00000000000..d89bca11bbc --- /dev/null +++ b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/contracts/b/Foo.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Foo { + uint public y; +} diff --git a/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/hardhat.config.ts b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/hardhat.config.ts new file mode 100644 index 00000000000..ff8b4c56321 --- /dev/null +++ b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/hardhat.config.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/package.json b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/package.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/packages/hardhat/test/fixture-projects/artifacts/duplicate-names-project/package.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/hardhat/test/internal/builtin-plugins/artifacts/artifact-manager.ts b/packages/hardhat/test/internal/builtin-plugins/artifacts/artifact-manager.ts index ea865630c94..aac877dbaf5 100644 --- a/packages/hardhat/test/internal/builtin-plugins/artifacts/artifact-manager.ts +++ b/packages/hardhat/test/internal/builtin-plugins/artifacts/artifact-manager.ts @@ -105,4 +105,69 @@ describe("ArtifactManagerImplementation", () => { ); }); }); + + describe("artifactExists", () => { + it("should return true for an existing bare name", async () => { + const result = await artifactManager.artifactExists("Counter"); + assert.equal(result, true); + }); + + it("should return true for an existing fully-qualified name", async () => { + const result = await artifactManager.artifactExists( + "contracts/Counter.sol:Counter", + ); + assert.equal(result, true); + }); + + it("should return false for a missing bare name", async () => { + const result = await artifactManager.artifactExists("NonExistent"); + assert.equal(result, false); + }); + + it("should return false for a missing fully-qualified name", async () => { + const result = await artifactManager.artifactExists( + "contracts/NonExistent.sol:NonExistent", + ); + assert.equal(result, false); + + const result2 = await artifactManager.artifactExists( + "contracts/Counter.sol:NonExistent", + ); + assert.equal(result2, false); + }); + }); +}); + +describe("ArtifactManagerImplementation - duplicate bare names", () => { + useFixtureProject("artifacts/duplicate-names-project"); + + let hre: HardhatRuntimeEnvironment; + let artifactManager: ArtifactManagerImplementation; + before(async () => { + hre = await createHardhatRuntimeEnvironment({}); + + await hre.tasks.getTask(["build"]).run({}); + + artifactManager = new ArtifactManagerImplementation( + hre.config.paths.artifacts, + ); + }); + + describe("artifactExists", () => { + it("should return true and not throw when multiple artifacts share the same bare name", async () => { + const result = await artifactManager.artifactExists("Foo"); + assert.equal(result, true); + }); + + it("should return true for each fully-qualified name individually", async () => { + const resultA = await artifactManager.artifactExists( + "contracts/a/Foo.sol:Foo", + ); + const resultB = await artifactManager.artifactExists( + "contracts/b/Foo.sol:Foo", + ); + assert.equal(resultA, true); + assert.equal(resultB, true); + }); + }); }); From b1e4fcbd7201b8b975755190f2309b196f50428c Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Mon, 13 Apr 2026 07:23:56 -0300 Subject: [PATCH 4/5] Add comment explaining an edge-case --- packages/hardhat/src/types/artifacts.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/hardhat/src/types/artifacts.ts b/packages/hardhat/src/types/artifacts.ts index 4491a661d4c..b3f5a356f4c 100644 --- a/packages/hardhat/src/types/artifacts.ts +++ b/packages/hardhat/src/types/artifacts.ts @@ -89,7 +89,9 @@ export interface ArtifactManager { /** * Returns true if an artifact exists. * - * This function doesn't throw if the name is not unique. + * This function doesn't throw if the name is not unique, this means that + * this method may return `true`, and a succesive call to `readArtifact` can + * throw because of a duplicated name. * * @param contractNameOrFullyQualifiedName Contract or fully qualified name. */ From b432591d939d21f8782a95ae1b0faf627e146732 Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Mon, 13 Apr 2026 07:31:17 -0300 Subject: [PATCH 5/5] Fix typo and formatting --- packages/hardhat/src/types/artifacts.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/hardhat/src/types/artifacts.ts b/packages/hardhat/src/types/artifacts.ts index b3f5a356f4c..88c966d3ba5 100644 --- a/packages/hardhat/src/types/artifacts.ts +++ b/packages/hardhat/src/types/artifacts.ts @@ -90,7 +90,7 @@ export interface ArtifactManager { * Returns true if an artifact exists. * * This function doesn't throw if the name is not unique, this means that - * this method may return `true`, and a succesive call to `readArtifact` can + * this method may return `true`, and a successive call to `readArtifact` can * throw because of a duplicated name. * * @param contractNameOrFullyQualifiedName Contract or fully qualified name. @@ -120,7 +120,8 @@ export interface ArtifactManager { * If it does return an id, it's not guaranteed that the build info is * present. * - * @param contractNameOrFullyQualifiedName Contract or fully qualified name, whose artifact must exist. + * @param contractNameOrFullyQualifiedName Contract or fully qualified name, + * whose artifact must exist. * @throws Throws an error if a non-unique contract name is used, * indicating which fully qualified names can be used instead. * @throws Throws an error if the artifact doesn't exist.