Skip to content

Commit f5f8b85

Browse files
authored
Merge pull request #6995 from NomicFoundation/ignition/verify-v3
Re-enable verification for Hardhat Ignition
2 parents 617312f + 9c8421c commit f5f8b85

File tree

19 files changed

+120
-807
lines changed

19 files changed

+120
-807
lines changed

.changeset/breezy-jars-dress.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@nomicfoundation/ignition-core": patch
3+
"@nomicfoundation/hardhat-ignition": patch
4+
"@nomicfoundation/hardhat-verify": patch
5+
---
6+
7+
Add `--verify` option for Ignition deploy task to verify all contracts of a deployment on Etherscan ([#6817](https://github.com/NomicFoundation/hardhat/issues/6817))

pnpm-lock.yaml

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

v-next/hardhat-ignition-core/src/types/verify.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { SolidityParameterType } from "./module.js";
2+
13
/**
24
* The configuration info needed to verify a contract on Etherscan on a given chain.
35
*
@@ -32,19 +34,17 @@ export interface SourceToLibraryToAddress {
3234
*/
3335
export interface VerifyInfo {
3436
address: string;
35-
compilerVersion: string;
36-
sourceCode: string;
37-
name: string;
38-
args: string;
37+
constructorArgs: SolidityParameterType[];
38+
libraries: Record<string, string>;
39+
contract: string;
3940
}
4041

4142
/**
4243
* The result of requesting the verification info for a deployment.
43-
* It returns the chainConfig followed by an array of VerifyInfo objects, one for each contract to be verified.
44-
* Alternatively, it returns null and the contract name if the contract used external artifacts that could not be resolved for verification.
44+
* It returns a VerifyInfo object for each contract to be verified.
45+
* Alternatively, it returns the contract name if the contract used
46+
* external artifacts that could not be resolved for verification.
4547
*
4648
* @beta
4749
*/
48-
export type VerifyResult =
49-
| [ChainConfig, VerifyInfo]
50-
| [_null: null, name: string];
50+
export type VerifyResult = VerifyInfo | string;
Lines changed: 10 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
1-
import type { DeploymentState } from "./internal/execution/types/deployment-state.js";
21
import type { DeploymentExecutionState } from "./internal/execution/types/execution-state.js";
3-
import type { Artifact, BuildInfo, CompilerInput } from "./types/artifact.js";
4-
import type {
5-
ChainConfig,
6-
SourceToLibraryToAddress,
7-
VerifyInfo,
8-
VerifyResult,
9-
} from "./types/verify.js";
10-
11-
import path from "node:path";
2+
import type { Artifact } from "./types/artifact.js";
3+
import type { VerifyInfo, VerifyResult } from "./types/verify.js";
124

135
import { HardhatError } from "@nomicfoundation/hardhat-errors";
146
import { FileNotFoundError } from "@nomicfoundation/hardhat-utils/fs";
15-
import { analyze } from "@nomicfoundation/solidity-analyzer";
167

17-
import { builtinChains } from "./internal/chain-config.js";
188
import { FileDeploymentLoader } from "./internal/deployment-loader/file-deployment-loader.js";
19-
import { encodeDeploymentArguments } from "./internal/execution/abi.js";
209
import { loadDeploymentState } from "./internal/execution/deployment-state-helpers.js";
2110
import { ExecutionResultType } from "./internal/execution/types/execution-result.js";
2211
import {
@@ -30,14 +19,11 @@ import { findExecutionStatesByType } from "./internal/views/find-execution-state
3019
* Retrieve the information required to verify all contracts from a deployment on Etherscan.
3120
*
3221
* @param deploymentDir - the file directory of the deployment
33-
* @param customChains - an array of custom chain configurations
3422
*
3523
* @beta
3624
*/
3725
export async function* getVerificationInformation(
3826
deploymentDir: string,
39-
customChains: ChainConfig[] = [],
40-
includeUnrelatedContracts = false,
4127
): AsyncGenerator<VerifyResult> {
4228
const deploymentLoader = new FileDeploymentLoader(deploymentDir);
4329

@@ -52,8 +38,6 @@ export async function* getVerificationInformation(
5238
);
5339
}
5440

55-
const chainConfig = resolveChainConfig(deploymentState, customChains);
56-
5741
const deploymentExStates = findExecutionStatesByType(
5842
ExecutionSateType.DEPLOYMENT_EXECUTION_STATE,
5943
deploymentState,
@@ -72,85 +56,25 @@ export async function* getVerificationInformation(
7256
const verifyInfo = await convertExStateToVerifyInfo(
7357
exState,
7458
deploymentLoader,
75-
includeUnrelatedContracts,
7659
);
7760

7861
if (typeof verifyInfo === "string") {
79-
yield [null, verifyInfo];
62+
yield verifyInfo;
8063
continue;
8164
}
8265

83-
const verifyResult: VerifyResult = [chainConfig, verifyInfo];
84-
85-
yield verifyResult;
86-
}
87-
}
88-
89-
function resolveChainConfig(
90-
deploymentState: DeploymentState,
91-
customChains: ChainConfig[],
92-
) {
93-
// implementation note:
94-
// if a user has set a custom chain with the same chainId as a builtin chain,
95-
// the custom chain will be used instead of the builtin chain
96-
const chainConfig = [...customChains, ...builtinChains].find(
97-
(c) => c.chainId === deploymentState.chainId,
98-
);
99-
100-
if (chainConfig === undefined) {
101-
throw new HardhatError(
102-
HardhatError.ERRORS.IGNITION.VERIFY.UNSUPPORTED_CHAIN,
103-
{
104-
chainId: deploymentState.chainId,
105-
},
106-
);
107-
}
108-
109-
return chainConfig;
110-
}
111-
112-
export function getImportSourceNames(
113-
sourceName: string,
114-
buildInfo: BuildInfo,
115-
visited: Record<string, boolean> = {},
116-
): string[] {
117-
if (visited[sourceName]) {
118-
return [];
66+
yield verifyInfo;
11967
}
120-
121-
visited[sourceName] = true;
122-
123-
const contractSource = buildInfo.input.sources[sourceName].content;
124-
const { imports } = analyze(contractSource);
125-
126-
const importSources = imports.map((i) => {
127-
if (/^\.\.?[\/|\\]/.test(i)) {
128-
return path.join(path.dirname(sourceName), i).replaceAll("\\", "/");
129-
}
130-
131-
return i;
132-
});
133-
134-
return [
135-
...importSources,
136-
...importSources.flatMap((i) =>
137-
getImportSourceNames(i, buildInfo, visited),
138-
),
139-
];
14068
}
14169

14270
async function convertExStateToVerifyInfo(
14371
exState: DeploymentExecutionState,
14472
deploymentLoader: FileDeploymentLoader,
145-
includeUnrelatedContracts: boolean = false,
14673
): Promise<VerifyInfo | string> {
147-
let result: [BuildInfo, Artifact];
74+
let artifact: Artifact;
14875

14976
try {
150-
result = await Promise.all([
151-
deploymentLoader.readBuildInfo(exState.artifactId),
152-
deploymentLoader.loadArtifact(exState.artifactId),
153-
]);
77+
artifact = await deploymentLoader.loadArtifact(exState.artifactId);
15478
} catch (e) {
15579
assertIgnitionInvariant(
15680
e instanceof FileNotFoundError,
@@ -164,8 +88,6 @@ async function convertExStateToVerifyInfo(
16488
return exState.artifactId;
16589
}
16690

167-
const [buildInfo, artifact] = result;
168-
16991
const { contractName, constructorArgs, libraries } = exState;
17092

17193
assertIgnitionInvariant(
@@ -174,78 +96,12 @@ async function convertExStateToVerifyInfo(
17496
`Deployment execution state ${exState.id} should have a successful result to retrieve address`,
17597
);
17698

177-
const sourceCode = prepareInputBasedOn(buildInfo, artifact, libraries);
178-
179-
if (!includeUnrelatedContracts) {
180-
const sourceNames = [
181-
artifact.sourceName,
182-
...getImportSourceNames(artifact.sourceName, buildInfo),
183-
];
184-
185-
for (const source of Object.keys(sourceCode.sources)) {
186-
if (!sourceNames.includes(source)) {
187-
delete sourceCode.sources[source];
188-
}
189-
}
190-
}
191-
192-
const verifyInfo = {
99+
const verifyInfo: VerifyInfo = {
100+
constructorArgs,
101+
libraries,
193102
address: exState.result.address,
194-
compilerVersion: buildInfo.solcLongVersion.startsWith("v")
195-
? buildInfo.solcLongVersion
196-
: `v${buildInfo.solcLongVersion}`,
197-
sourceCode: JSON.stringify(sourceCode),
198-
name: `${artifact.sourceName}:${contractName}`,
199-
args: encodeDeploymentArguments(artifact, constructorArgs),
103+
contract: `${artifact.sourceName}:${contractName}`,
200104
};
201105

202106
return verifyInfo;
203107
}
204-
205-
function prepareInputBasedOn(
206-
buildInfo: BuildInfo,
207-
artifact: Artifact,
208-
libraries: Record<string, string>,
209-
): CompilerInput {
210-
const sourceToLibraryAddresses = resolveLibraryInfoForArtifact(
211-
artifact,
212-
libraries,
213-
);
214-
215-
if (sourceToLibraryAddresses === null) {
216-
return buildInfo.input;
217-
}
218-
219-
const { input } = buildInfo;
220-
input.settings.libraries = sourceToLibraryAddresses;
221-
222-
return input;
223-
}
224-
225-
function resolveLibraryInfoForArtifact(
226-
artifact: Artifact,
227-
libraries: Record<string, string>,
228-
): SourceToLibraryToAddress | null {
229-
const sourceToLibraryToAddress: SourceToLibraryToAddress = {};
230-
231-
for (const [sourceName, refObj] of Object.entries(artifact.linkReferences)) {
232-
for (const [libName] of Object.entries(refObj)) {
233-
sourceToLibraryToAddress[sourceName] ??= {};
234-
235-
const libraryAddress = libraries[libName];
236-
237-
assertIgnitionInvariant(
238-
libraryAddress !== undefined,
239-
`Could not find address for library ${libName}`,
240-
);
241-
242-
sourceToLibraryToAddress[sourceName][libName] = libraryAddress;
243-
}
244-
}
245-
246-
if (Object.entries(sourceToLibraryToAddress).length === 0) {
247-
return null;
248-
}
249-
250-
return sourceToLibraryToAddress;
251-
}

v-next/hardhat-ignition-core/test/mocks/verify/external-artifacts/artifacts/LockModule#Basic.json

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)