Skip to content

Commit 269b2f2

Browse files
authored
Merge pull request #7037 from NomicFoundation/verify-all
Support verifying contracts on all enabled providers from the main verify task
2 parents cd43f21 + 2377027 commit 269b2f2

File tree

15 files changed

+471
-151
lines changed

15 files changed

+471
-151
lines changed

.changeset/old-wolves-rhyme.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@nomicfoundation/hardhat-errors": patch
3+
"@nomicfoundation/hardhat-verify": patch
4+
"hardhat": patch
5+
---
6+
7+
Support verifying contracts on all enabled providers from the main verify task ([#7007](https://github.com/NomicFoundation/hardhat/issues/7007))

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

v-next/hardhat-errors/src/descriptors.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,16 +2582,8 @@ Reason: "{reason}".
25822582
25832583
If your contract uses libraries whose addresses cannot be detected automatically, make sure you are providing the correct address for each undetectable library.`,
25842584
},
2585-
VERIFICATION_DISABLED_IN_CONFIG: {
2586-
number: 80027,
2587-
messageTemplate:
2588-
"{verificationProvider} verification is disabled in your config. Please add the verification provider configuration to your Hardhat config.",
2589-
websiteTitle: "Provider verification disabled",
2590-
websiteDescription:
2591-
"No provider verification configuration set in Hardhat config",
2592-
},
25932585
BLOCK_EXPLORER_NOT_CONFIGURED: {
2594-
number: 80028,
2586+
number: 80027,
25952587
messageTemplate:
25962588
"No {verificationProvider} block explorer is configured for the {chainId} chain in the chain descriptors.",
25972589
websiteTitle: "Block explorer not configured",
@@ -2614,14 +2606,14 @@ chainDescriptors: {
26142606
`,
26152607
},
26162608
ADDRESS_NOT_A_CONTRACT: {
2617-
number: 80029,
2609+
number: 80028,
26182610
messageTemplate: `{verificationProvider} responded that the address "{address}" does not contain a contract. This usually means the address is incorrect, the contract was not deployed on the selected network, or there is a temporary issue with the block explorer not updating its index.`,
26192611
websiteTitle: "Address is not a contract",
26202612
websiteDescription: `The block explorer responded that the address does not contain a contract. This usually means the address is incorrect, the contract was not deployed on the selected network, or there is a temporary issue with the block explorer not updating its index.
26212613
Please verify the address and network, and try again later if necessary.`,
26222614
},
26232615
EXPLORER_API_KEY_EMPTY: {
2624-
number: 80030,
2616+
number: 80029,
26252617
messageTemplate: `The {verificationProvider} API key is empty.`,
26262618
websiteTitle: "Block explorer API key is empty",
26272619
websiteDescription: `The provided API key for the block explorer is empty. This can happen in the following cases:

v-next/hardhat-verify/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"@nomicfoundation/hardhat-utils": "workspace:^3.0.0-next.23",
6767
"@nomicfoundation/hardhat-zod-utils": "workspace:^3.0.0-next.23",
6868
"cbor2": "^1.9.0",
69+
"chalk": "^5.3.0",
6970
"debug": "^4.3.2",
7071
"semver": "^7.6.3",
7172
"zod": "^3.23.8"

v-next/hardhat-verify/src/internal/hook-handlers/config.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import type {
1212
HardhatUserConfigValidationError,
1313
} from "hardhat/types/hooks";
1414

15+
import { isObject } from "@nomicfoundation/hardhat-utils/lang";
1516
import {
17+
conditionalUnionType,
1618
sensitiveStringSchema,
1719
validateUserConfigZodType,
1820
} from "@nomicfoundation/hardhat-zod-utils";
@@ -31,12 +33,27 @@ const userConfigType = z.object({
3133
enabled: z.boolean().optional(),
3234
})
3335
.optional(),
34-
etherscan: z
35-
.object({
36-
apiKey: sensitiveStringSchema,
37-
enabled: z.boolean().optional(),
38-
})
39-
.optional(),
36+
etherscan: conditionalUnionType(
37+
[
38+
[
39+
(data) => isObject(data) && data.enabled === false,
40+
z.object({
41+
apiKey: sensitiveStringSchema.optional(),
42+
enabled: z.literal(false),
43+
}),
44+
],
45+
[
46+
(data) =>
47+
isObject(data) &&
48+
(data.enabled === undefined || data.enabled === true),
49+
z.object({
50+
apiKey: sensitiveStringSchema,
51+
enabled: z.literal(true).optional(),
52+
}),
53+
],
54+
],
55+
"Expected an object with an 'apiKey' property and an optional 'enabled' boolean property",
56+
).optional(),
4057
})
4158
.optional(),
4259
});
@@ -88,7 +105,7 @@ function resolveEtherscanConfig(
88105
resolveConfigurationVariable: ConfigurationVariableResolver,
89106
): EtherscanConfig {
90107
return {
91-
apiKey: resolveConfigurationVariable(etherscanConfig.apiKey),
108+
apiKey: resolveConfigurationVariable(etherscanConfig.apiKey ?? ""),
92109
enabled: etherscanConfig.enabled ?? true,
93110
};
94111
}

v-next/hardhat-verify/src/internal/tasks/verify/blockscout/task-action.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import type { VerifyActionArgs } from "../types.js";
22
import type { NewTaskActionFunction } from "hardhat/types/tasks";
33

4-
import { HardhatError } from "@nomicfoundation/hardhat-errors";
5-
import { capitalize } from "@nomicfoundation/hardhat-utils/string";
6-
74
import { BLOCKSCOUT_PROVIDER_NAME } from "../../../blockscout.js";
85
import { verifyContract } from "../../../verification.js";
96
import {
@@ -15,17 +12,6 @@ const verifyBlockscoutAction: NewTaskActionFunction<VerifyActionArgs> = async (
1512
{ constructorArgs, constructorArgsPath, librariesPath, ...verifyActionArgs },
1613
hre,
1714
) => {
18-
// Note: this check is done at the beginning of the task to throw
19-
// early if the user has disabled the Blockscout verification.
20-
if (hre.config.verify.blockscout.enabled === false) {
21-
throw new HardhatError(
22-
HardhatError.ERRORS.HARDHAT_VERIFY.GENERAL.VERIFICATION_DISABLED_IN_CONFIG,
23-
{
24-
verificationProvider: capitalize(BLOCKSCOUT_PROVIDER_NAME),
25-
},
26-
);
27-
}
28-
2915
const resolvedConstructorArgs = await resolveConstructorArgs(
3016
constructorArgs,
3117
constructorArgsPath,

v-next/hardhat-verify/src/internal/tasks/verify/etherscan/task-action.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import type { VerifyActionArgs } from "../types.js";
22
import type { NewTaskActionFunction } from "hardhat/types/tasks";
33

4-
import { HardhatError } from "@nomicfoundation/hardhat-errors";
5-
import { capitalize } from "@nomicfoundation/hardhat-utils/string";
6-
74
import { ETHERSCAN_PROVIDER_NAME } from "../../../etherscan.js";
85
import { verifyContract } from "../../../verification.js";
96
import {
@@ -15,17 +12,6 @@ const verifyEtherscanAction: NewTaskActionFunction<VerifyActionArgs> = async (
1512
{ constructorArgs, constructorArgsPath, librariesPath, ...verifyActionArgs },
1613
hre,
1714
) => {
18-
// Note: this check is done at the beginning of the task to throw
19-
// early if the user has disabled the Etherscan verification.
20-
if (hre.config.verify.etherscan.enabled === false) {
21-
throw new HardhatError(
22-
HardhatError.ERRORS.HARDHAT_VERIFY.GENERAL.VERIFICATION_DISABLED_IN_CONFIG,
23-
{
24-
verificationProvider: capitalize(ETHERSCAN_PROVIDER_NAME),
25-
},
26-
);
27-
}
28-
2915
const resolvedConstructorArgs = await resolveConstructorArgs(
3016
constructorArgs,
3117
constructorArgsPath,

v-next/hardhat-verify/src/internal/tasks/verify/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { extendWithVerificationArgs } from "./utils.js";
77
const verifyTask: NewTaskDefinition = extendWithVerificationArgs(
88
task("verify", "Verify a contract on all supported explorers"),
99
)
10-
.setAction(import.meta.resolve("./etherscan/task-action.js"))
10+
.setAction(import.meta.resolve("./task-action.js"))
1111
.build();
1212

1313
export default verifyTask;
Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,78 @@
11
import type { VerifyActionArgs } from "./types.js";
2+
import type { VerificationProvidersConfig } from "hardhat/types/config";
3+
import type { HardhatRuntimeEnvironment } from "hardhat/types/hre";
24
import type { NewTaskActionFunction } from "hardhat/types/tasks";
35

6+
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
7+
import { capitalize } from "@nomicfoundation/hardhat-utils/string";
8+
import chalk from "chalk";
9+
10+
import { verifyContract } from "../../verification.js";
11+
import { resolveConstructorArgs, resolveLibraries } from "../arg-resolution.js";
12+
413
const verifyAction: NewTaskActionFunction<VerifyActionArgs> = async (
5-
taskArgs,
14+
verifyActionArgs,
615
hre,
716
) => {
8-
await hre.tasks.getTask(["verify", "etherscan"]).run(taskArgs);
17+
await internalVerifyAction(verifyActionArgs, hre, verifyContract);
918
};
1019

20+
export async function internalVerifyAction(
21+
{
22+
constructorArgs,
23+
constructorArgsPath,
24+
librariesPath,
25+
...verifyActionArgs
26+
}: VerifyActionArgs,
27+
hre: HardhatRuntimeEnvironment,
28+
verifyContractFn: typeof verifyContract,
29+
): Promise<void> {
30+
const allProviders: Array<keyof VerificationProvidersConfig> = [
31+
"etherscan",
32+
"blockscout",
33+
];
34+
35+
const enabledProviders = allProviders.filter(
36+
(provider) => hre.config.verify[provider].enabled,
37+
);
38+
39+
if (enabledProviders.length === 0) {
40+
console.warn(chalk.yellow("\n⚠️ No verification providers are enabled."));
41+
process.exitCode = 0;
42+
return;
43+
}
44+
45+
const resolvedConstructorArgs = await resolveConstructorArgs(
46+
constructorArgs,
47+
constructorArgsPath,
48+
);
49+
50+
const resolvedLibraries = await resolveLibraries(librariesPath);
51+
52+
let errorOccurred = false;
53+
for (const provider of enabledProviders) {
54+
try {
55+
console.log(chalk.cyan.bold(`\n=== ${capitalize(provider)} ===`));
56+
await verifyContractFn(
57+
{
58+
...verifyActionArgs,
59+
constructorArgs: resolvedConstructorArgs,
60+
libraries: resolvedLibraries,
61+
provider,
62+
},
63+
hre,
64+
);
65+
} catch (error) {
66+
ensureError(error);
67+
// It would be nice to use printErrorMessages
68+
// from v-next/hardhat/src/internal/cli/error-handler.ts
69+
// for consistent error formatting
70+
console.error(chalk.red(error.message));
71+
errorOccurred = true;
72+
}
73+
}
74+
75+
process.exitCode = errorOccurred ? 1 : 0;
76+
}
77+
1178
export default verifyAction;

v-next/hardhat-verify/src/internal/verification.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,6 @@ export async function verifyContract(
8585
verifyContractArgs;
8686
validateVerificationProviderName(verificationProviderName);
8787

88-
if (config.verify[verificationProviderName].enabled === false) {
89-
throw new HardhatError(
90-
HardhatError.ERRORS.HARDHAT_VERIFY.GENERAL.VERIFICATION_DISABLED_IN_CONFIG,
91-
{
92-
verificationProvider: capitalize(verificationProviderName),
93-
},
94-
);
95-
}
96-
9788
validateArgs(verifyContractArgs);
9889

9990
const {
@@ -148,8 +139,7 @@ The contract at ${address} has already been verified on ${instance.name}.
148139
149140
If you need to verify a partially verified contract, please use the --force flag.
150141
151-
Explorer: ${instance.getContractUrl(address)}
152-
`);
142+
Explorer: ${instance.getContractUrl(address)}`);
153143
return true;
154144
}
155145

@@ -234,11 +224,10 @@ Explorer: ${instance.getContractUrl(address)}
234224

235225
if (minimalInputVerificationSuccess) {
236226
consoleLog(`
237-
🎉 Contract verified successfully on ${instance.name}!
227+
Contract verified successfully on ${instance.name}!
238228
239229
${contractInformation.userFqn}
240-
Explorer: ${instance.getContractUrl(address)}
241-
`);
230+
Explorer: ${instance.getContractUrl(address)}`);
242231
return true;
243232
}
244233

@@ -275,11 +264,10 @@ Unrelated contracts may be displayed on ${instance.name} as a result.
275264

276265
if (fullCompilerInputVerificationSuccess) {
277266
consoleLog(`
278-
🎉 Contract verified successfully on ${instance.name}!
267+
Contract verified successfully on ${instance.name}!
279268
280269
${contractInformation.userFqn}
281-
Explorer: ${instance.getContractUrl(address)}
282-
`);
270+
Explorer: ${instance.getContractUrl(address)}`);
283271
return true;
284272
}
285273

@@ -417,7 +405,7 @@ async function attemptVerification(
417405
);
418406

419407
consoleLog(`
420-
Submitted source code for verification on ${verificationProvider.name}:
408+
📤 Submitted source code for verification on ${verificationProvider.name}:
421409
422410
${contractInformation.userFqn}
423411
Address: ${address}

0 commit comments

Comments
 (0)