Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 59 additions & 19 deletions js/verifiableBinaries.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ 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"));
for (const fileName of finder.found) {
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
Expand All @@ -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"
});
Expand Down Expand Up @@ -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;
};
}
Expand All @@ -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),
Expand All @@ -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);
Expand Down Expand Up @@ -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 .*[\"'](?<importedFile>.+)[\"'];$", "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) {
Expand Down Expand Up @@ -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 = {
Expand Down
Loading