diff --git a/js/verifiableBinaries.js b/js/verifiableBinaries.js index 5bc39c0..ba75815 100644 --- a/js/verifiableBinaries.js +++ b/js/verifiableBinaries.js @@ -32,8 +32,8 @@ function loadPackageArtifacts(packageConfig, artifactsPath, artifacts) { ...(packageConfig.artifactsConfig || {}), }; - let buildInfoPath = path.join(artifactsPath, "build-info.json"); - if (!fs.existsSync(buildInfoPath)) { + let buildInfoPath = path.parse(packageConfig.buildInfoPath || path.join(artifactsPath, "build-info.json")); + if (!fs.existsSync(path.format(buildInfoPath))) { buildInfoPath = null; } const finder = new glob.GlobSync(path.join(artifactsPath, "**/*.json")); @@ -41,7 +41,7 @@ function loadPackageArtifacts(packageConfig, artifactsPath, artifacts) { const parsedFile = path.parse(fileName); const contractName = parsedFile.name; // Skip build-info and debug files - if (contractName === "build-info" || contractName.endsWith(".dbg")) continue; + if (contractName === (buildInfoPath?.name || "build-info") || contractName.endsWith(".dbg")) continue; // If there's a whitelist, skip those that aren't in the whitelist if (artifactsConfig.whitelist.length !== 0 && !artifactsConfig.whitelist.includes(contractName)) continue; // If there's a blacklist, skip those that are in the blacklist @@ -52,7 +52,7 @@ function loadPackageArtifacts(packageConfig, artifactsPath, artifacts) { package: packageConfig.package, version: packageConfig.version, fileName, - buildInfoPath, + buildInfoPath: buildInfoPath ? path.format(buildInfoPath) : null, onlyFQ: artifactsConfig.onlyFQ, // Indicates this contract should match only when specified with full // name, like "@openzeppelin/contracts/AccessManager" }); @@ -144,8 +144,10 @@ function verifiableContractFactory(contractFactory, binaryArtifact) { get(target, prop, receiver) { if (prop === "deploy") { return async (...args) => { - const contract = await target.deploy(...args); - contract.binaryArtifact = binaryArtifact; + const contract = target.deploy(...args); + contract.then((c) => { + c.binaryArtifact = binaryArtifact; + }); return contract; }; } @@ -160,7 +162,6 @@ function wrapGetContractFactory(hre, defaultGetContractFactory) { if (typeof contractClass === "string") { const binaryArtifact = await findBinaryArtifact(hre, contractClass); if (binaryArtifact !== undefined) { - // return hre.ethers.getContractFactoryFromArtifact(artifact, ...args); const artifact = JSON.parse(fs.readFileSync(binaryArtifact.fileName)); return verifiableContractFactory( await hre.ethers.getContractFactoryFromArtifact(artifact, ...args), @@ -176,9 +177,12 @@ function wrapGetContractAt(hre, defaultGetContractAt) { return async function (contractClass, ...args) { if (typeof contractClass === "string") { const binaryArtifact = await findBinaryArtifact(hre, contractClass); + if (binaryArtifact !== undefined) { const artifact = JSON.parse(fs.readFileSync(binaryArtifact.fileName)); - return hre.ethers.getContractAtFromArtifact(artifact, ...args); + const contract = await hre.ethers.getContractAtFromArtifact(artifact, ...args); + contract.binaryArtifact = binaryArtifact; + return contract; } } return defaultGetContractAt(contractClass, ...args); @@ -209,19 +213,30 @@ function absolutePath(importedFile, sourceFileName) { return sourcePath.join("/"); } -function addRequiredSources(requiredSources, sources, sourcePath) { +function addRequiredSources(requiredSources, sources, startSourcePath) { const importRe = new RegExp("^import .*[\"'](?.+)[\"'];$", "gmu"); - if (sources[sourcePath] === undefined) { - throw new Error(`Source ${sourcePath} not found. Keys: ${Object.keys(sources)}`); - } - const sourceCode = sources[sourcePath].content; - sourceCode.replace(importRe, (_, importedFile) => { - importedFile = absolutePath(importedFile, sourcePath); - if (!requiredSources.includes(importedFile)) { - requiredSources.push(importedFile); + const queue = [startSourcePath]; + const visited = new Set(); + + while (queue.length > 0) { + const sourcePath = queue.shift(); + + if (visited.has(sourcePath)) continue; + visited.add(sourcePath); + + if (sources[sourcePath] === undefined) { + throw new Error(`Source ${sourcePath} not found. Keys: ${Object.keys(sources)}`); } - addRequiredSources(requiredSources, sources, importedFile); - }); + + const sourceCode = sources[sourcePath].content; + sourceCode.replace(importRe, (_, importedFile) => { + importedFile = absolutePath(importedFile, sourcePath); + if (!requiredSources.includes(importedFile)) { + requiredSources.push(importedFile); + queue.push(importedFile); + } + }); + } } function buildInfoForSource(fullBuildInfo, sourceName) { @@ -349,6 +364,31 @@ function addTasks() { execSync(`scripts/fetch-binary-package.sh ${basePath} ${packageConfig.package} ${packageConfig.version} npm`); } }); + task("vb:verify", "Verifies a binary contract on Etherscan") + .addParam("contractType", "The contract type to verify") + .addParam("address", "The contract address to verify") + .addOptionalVariadicPositionalParam("constructorArguments", "The constructor arguments", []) + .setAction(async function ({ contractType, constructorArguments, address }, hre) { + const contract = await hre.ethers.getContractAt(contractType, address); + + await verifyBinaryContract(hre, contract, false, constructorArguments); + }); + task("vb:findArtifact", "Finds the artifact for a contract") + .addPositionalParam("contract", "The contract type to find") + .setAction(async function ({ contract }, hre) { + const artifact = await findBinaryArtifact(hre, contract); + if (artifact === undefined) { + console.log(`Binary artifact not found for ${contract}`); + const legacyArtifact = await hre.artifacts.readArtifact(contract); + if (legacyArtifact !== undefined) + console.log("Legacy artifact found:", { + contractName: legacyArtifact.contractName, + sourceName: legacyArtifact.sourceName, + }); + } else { + console.log(artifact); + } + }); } module.exports = {