Skip to content

Commit

Permalink
Merge pull request NomicFoundation#807 from nomiclabs/buidler-ethersc…
Browse files Browse the repository at this point in the history
…an/multi-solc

Update buidler-etherscan to support multi-solc pipeline.
  • Loading branch information
alcuadrado authored Sep 25, 2020
2 parents 08910c9 + 090f8cb commit 49086c6
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 166 deletions.
3 changes: 2 additions & 1 deletion packages/buidler-etherscan/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@
"@ethersproject/abi": "^5.0.2",
"@ethersproject/address": "^5.0.2",
"cbor": "^5.0.2",
"ethereumjs-abi": "^0.6.8",
"fs-extra": "^7.0.1",
"node-fetch": "^2.6.0",
"semver": "^6.3.0"
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.0",
"@types/cbor": "^5.0.1",
"@types/chai": "^4.2.0",
"@types/fs-extra": "^5.1.0",
"@types/nock": "^9.3.1",
"@types/node-fetch": "^2.3.7",
"@types/semver": "^6.0.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/buidler-etherscan/src/ABIEncoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { pluginName } from "./pluginContext";

export async function encodeArguments(
abi: any,
contractFilename: string,
sourceName: string,
contractName: string,
constructorArguments: any[]
) {
Expand All @@ -24,7 +24,7 @@ export async function encodeArguments(
} = await import("./ABITypes");
if (isABIArgumentLengthError(error)) {
// TODO: add a list of types and constructor arguments to the error message?
const message = `The constructor for ${contractFilename}:${contractName} has ${error.count.types} parameters
const message = `The constructor for ${sourceName}:${contractName} has ${error.count.types} parameters
but ${error.count.values} arguments were provided instead.`;
throw new NomicLabsHardhatPluginError(pluginName, message, error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function toVerifyRequest(params: {
apiKey: string;
contractAddress: string;
sourceCode: string;
contractFilename: string;
sourceName: string;
contractName: string;
compilerVersion: string;
constructorArguments: string;
Expand All @@ -37,7 +37,7 @@ export function toVerifyRequest(params: {
contractaddress: params.contractAddress,
sourceCode: params.sourceCode,
codeformat: "solidity-standard-json-input",
contractname: `${params.contractFilename}:${params.contractName}`,
contractname: `${params.sourceName}:${params.contractName}`,
compilerversion: params.compilerVersion,
constructorArguements: params.constructorArguments,
};
Expand Down
91 changes: 57 additions & 34 deletions packages/buidler-etherscan/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,16 @@ See https://etherscan.io/apis`
// TODO: perhaps querying and scraping this list would be a better approach?
// This list should be validated - it links to https://github.com/ethereum/solc-bin/blob/gh-pages/bin/list.txt
// which has many old compilers included in the list too.
// TODO-HH: handle multiple compilers
const configuredVersions = config.solidity.compilers.map((c) => c.version);
if (config.solidity.overrides !== undefined) {
for (const { version } of Object.values(config.solidity.overrides)) {
configuredVersions.push(version);
}
}
if (
!semver.satisfies(
config.solidity.compilers[0].version,
supportedSolcVersionRange
)
configuredVersions.some((version) => {
return !semver.satisfies(version, supportedSolcVersionRange);
})
) {
throw new NomicLabsHardhatPluginError(
pluginName,
Expand Down Expand Up @@ -143,38 +147,45 @@ The selected network is ${network.name}.`
const bytecodeBuffer = Buffer.from(deployedContractBytecode, "hex");
const inferredSolcVersion = await inferSolcVersion(bytecodeBuffer);

// TODO-HH: handle multiple compilers
if (
!semver.satisfies(
config.solidity.compilers[0].version,
inferredSolcVersion.range
)
) {
let detailedContext;
const matchingVersions = configuredVersions.filter((version) => {
return semver.satisfies(version, inferredSolcVersion.range);
});
if (matchingVersions.length === 0) {
const detailedContext = [];
if (inferredSolcVersion.inferralType === InferralType.EXACT) {
detailedContext = `The expected version is ${inferredSolcVersion.range}.`;
detailedContext.push(
`The expected version is ${inferredSolcVersion.range}.`
);
} else {
detailedContext = `The expected version range is ${inferredSolcVersion.range}.`;
detailedContext.push(
`The expected version range is ${inferredSolcVersion.range}.`
);
}
// TODO-HH: handle multiple compilers
const message = `The bytecode retrieved could not have been generated by the selected compiler.
The selected compiler version is v${config.solidity.compilers[0].version}.
${detailedContext}
// There is always at least one configured version.
if (configuredVersions.length > 1) {
detailedContext.push(
`The selected compiler versions are: ${configuredVersions.join(", ")}`
);
} else {
detailedContext.push(
`The selected compiler version is: ${configuredVersions[0]}`
);
}
const message = `The bytecode retrieved could not have been generated by any of the selected compilers.
${detailedContext.join("\n")}
Possible causes are:
- Wrong compiler version in hardhat config.
- Wrong compiler version selected in hardhat config.
- The given address is wrong.
- The selected network (${network.name}) is wrong.`;
throw new NomicLabsHardhatPluginError(pluginName, message);
}

const { lookupMatchingBytecode, compile } = await import("./solc/bytecode");
// TODO: this gives us the input for all contracts.
// This could be restricted to relevant contracts in a future iteration of the compiler tasks.
const { compilerInput, compilerOutput } = await compile(run);
const builds = await compile(run, matchingVersions, config.paths.artifacts);

const contractMatches = await lookupMatchingBytecode(
compilerOutput.contracts,
builds,
deployedContractBytecode,
inferredSolcVersion.inferralType
);
Expand All @@ -192,7 +203,7 @@ Possible causes are:
if (contractMatches.length > 1) {
const nameList = contractMatches
.map((contract) => {
return `${contract.contractFilename}:${contract.contractName}`;
return `${contract.sourceName}:${contract.contractName}`;
})
.join(", ");
const message = `More than one contract was found to match the deployed bytecode.
Expand All @@ -202,22 +213,34 @@ ${nameList}`;
}
const [contractInformation] = contractMatches;

const libraryLinks = contractInformation.libraryLinks;
const deployLibraryReferences =
contractInformation.contract.evm.bytecode.linkReferences;
if (
Object.keys(libraryLinks).length <
Object.keys(deployLibraryReferences).length
) {
throw new NomicLabsHardhatPluginError(
pluginName,
`The contract ${contractInformation.sourceName}:${contractInformation.contractName} has one or more library references that cannot be detected from deployed bytecode.
This can occur if the library is only called in the contract constructor.`
);
}

const { encodeArguments } = await import("./ABIEncoder");
const deployArgumentsEncoded = await encodeArguments(
contractInformation.contract.abi,
contractInformation.contractFilename,
contractInformation.sourceName,
contractInformation.contractName,
constructorArguments
);

// Ensure the linking information is present in the compiler input;
compilerInput.settings.libraries = contractInformation.libraryLinks;
const compilerInputJSON = JSON.stringify(compilerInput);
contractInformation.compilerInput.settings.libraries =
contractInformation.libraryLinks;
const compilerInputJSON = JSON.stringify(contractInformation.compilerInput);

// TODO-HH: handle multiple compilers
const solcFullVersion = await getLongVersion(
config.solidity.compilers[0].version
);
const solcFullVersion = await getLongVersion(contractInformation.solcVersion);

const { toVerifyRequest, toCheckStatusRequest } = await import(
"./etherscan/EtherscanVerifyContractRequest"
Expand All @@ -226,7 +249,7 @@ ${nameList}`;
apiKey: etherscan.apiKey,
contractAddress: address,
sourceCode: compilerInputJSON,
contractFilename: contractInformation.contractFilename,
sourceName: contractInformation.sourceName,
contractName: contractInformation.contractName,
compilerVersion: solcFullVersion,
constructorArguments: deployArgumentsEncoded,
Expand All @@ -239,7 +262,7 @@ ${nameList}`;

console.log(
`Successfully submitted source code for contract
${contractInformation.contractFilename}:${contractInformation.contractName} at ${address}
${contractInformation.sourceName}:${contractInformation.contractName} at ${address}
for verification on etherscan. Waiting for verification result...`
);

Expand Down
Loading

0 comments on commit 49086c6

Please sign in to comment.