@nomicfoundation/hardhat-verify
Version:
Hardhat plugin for verifying contracts
116 lines (99 loc) • 3.96 kB
text/typescript
import type { ArtifactManager, BuildInfo } from "hardhat/types/artifacts";
import type {
CompilerInput,
SolidityBuildInfoOutput,
SolidityBuildSystem,
} from "hardhat/types/solidity";
import path from "node:path";
import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors";
import { readJsonFile } from "@nomicfoundation/hardhat-utils/fs";
export interface BuildInfoAndOutput {
buildInfo: BuildInfo;
buildInfoOutput: SolidityBuildInfoOutput;
}
// TODO: reading build info from disk on each call is inefficient.
// We should consider caching the results in memory.
// TODO2: getBuildInfoId and readJsonFile can throw errors, we should
// wrap them in a try-catch block and return undefined if an error occurs
// while also logging the error.
// TODO3: the information contained in the output can be read from the
// artifact:
// artifact field | output field
// abi | contracts.[sourceName][contractName].abi
// linkReferences | contracts.[sourceName][contractName].evm.bytecode.linkReferences
// deployedLinkReferences | contracts.[sourceName][contractName].evm.deployedBytecode.linkReferences
// deployedBytecode | contracts.[sourceName][contractName].evm.deployedBytecode.object
// immutableReferences | contracts.[sourceName][contractName].evm.deployedBytecode.immutableReferences
// TODO4: consider returning only the fields needed from the build info: solcVersion, solcLongVersion, input
/**
* Retrieves the saved build information and output for a given contract.
*
* @param artifacts The artifact manager instance to use for retrieving build info.
* @param contract The fully qualified contract name (e.g., "contracts/Token.sol:Token").
* @returns The `BuildInfo` object if it exists on disk, or `undefined` if no
* build info is found.
*/
export async function getBuildInfoAndOutput(
artifacts: ArtifactManager,
contract: string,
): Promise<BuildInfoAndOutput | undefined> {
const buildInfoId = await artifacts.getBuildInfoId(contract);
if (buildInfoId === undefined) {
// TODO: maybe we should throw an error here indicating that the
// contract wasn't compiled with Hardhat 3's build system
return undefined;
}
const buildInfoPath = await artifacts.getBuildInfoPath(buildInfoId);
if (buildInfoPath === undefined) {
return undefined;
}
const buildInfoOutputPath =
await artifacts.getBuildInfoOutputPath(buildInfoId);
if (buildInfoOutputPath === undefined) {
return undefined;
}
const buildInfo: BuildInfo = await readJsonFile(buildInfoPath);
const buildInfoOutput: SolidityBuildInfoOutput =
await readJsonFile(buildInfoOutputPath);
return {
buildInfo,
buildInfoOutput,
};
}
// TODO: consider moving this to the solidity build system as a helper function
export async function getCompilerInput(
solidity: SolidityBuildSystem,
root: string,
sourceName: string,
isNpmModule: boolean,
buildProfileName: string,
): Promise<CompilerInput> {
const rootFilePath = isNpmModule
? `npm:${sourceName}`
: path.join(root, sourceName);
const getCompilationJobsResult = await solidity.getCompilationJobs(
[rootFilePath],
{
buildProfile: buildProfileName,
quiet: true,
force: true,
},
);
assertHardhatInvariant(
getCompilationJobsResult.success,
"getCompilationJobs should not error",
);
const compilationJob = getCompilationJobsResult.compilationJobsPerFile;
// TODO: should this be an error instead?
assertHardhatInvariant(
compilationJob instanceof Map && compilationJob.size === 1,
"The compilation job for the contract source was not found.",
);
const compilerInput = await compilationJob.get(rootFilePath)?.getSolcInput();
// TODO: should this be an error instead?
assertHardhatInvariant(
compilerInput !== undefined,
"The compiler input for the contract source was not found.",
);
return compilerInput;
}