@nomiclabs/hardhat-etherscan
Version:
Hardhat plugin for verifying contracts on etherscan
193 lines (189 loc) • 10.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLibraryLinks = void 0;
const constants_1 = require("../constants");
const errors_1 = require("../errors");
async function getLibraryLinks(contractInformation, libraries) {
const allLibraries = getLibraryNames(contractInformation.contract.evm.bytecode.linkReferences);
const detectableLibraries = getLibraryNames(contractInformation.contract.evm.deployedBytecode.linkReferences);
const undetectableLibraries = allLibraries.filter((lib) => !detectableLibraries.some((detectableLib) => {
return (detectableLib.sourceName === lib.sourceName &&
detectableLib.libName === lib.libName);
}));
// Resolve and normalize library links given by user
const normalizedLibraries = await normalizeLibraries(allLibraries, detectableLibraries, undetectableLibraries, libraries, contractInformation.contractName);
// Merge library links
const mergedLibraryLinks = mergeLibraries(normalizedLibraries, contractInformation.libraryLinks);
const mergedLibraries = getLibraryNames(mergedLibraryLinks);
if (mergedLibraries.length < allLibraries.length) {
// TODO: update message to help solve this problem
const missingLibraries = allLibraries.filter((lib) => !mergedLibraries.some((mergedLib) => {
return (lib.sourceName === mergedLib.sourceName &&
lib.libName === mergedLib.libName);
}));
const missingLibraryNames = missingLibraries
.map(({ sourceName, libName }) => `${sourceName}:${libName}`)
.map((x) => ` * ${x}`)
.join("\n");
let message = `The contract ${contractInformation.sourceName}:${contractInformation.contractName} has one or more library addresses that cannot be detected from deployed bytecode.
This can occur if the library is only called in the contract constructor. The missing libraries are:
${missingLibraryNames}`;
// We want to distinguish the case when no undetectable libraries were provided to give a more helpful message.
if (missingLibraries.length === undetectableLibraries.length) {
message += `
Visit https://hardhat.org/hardhat-runner/plugins/nomiclabs-hardhat-etherscan#libraries-with-undetectable-addresses to learn how to solve this.`;
}
else {
message += `
To solve this, you can add them to your --libraries dictionary with their corresponding addresses.`;
}
throw new errors_1.HardhatEtherscanPluginError(constants_1.pluginName, message);
}
return { libraryLinks: mergedLibraryLinks, undetectableLibraries };
}
exports.getLibraryLinks = getLibraryLinks;
function mergeLibraries(normalizedLibraries, detectedLibraries) {
const conflicts = [];
for (const [sourceName, libraries] of Object.entries(normalizedLibraries)) {
for (const [libName, libAddress] of Object.entries(libraries)) {
if (sourceName in detectedLibraries &&
libName in detectedLibraries[sourceName]) {
const detectedAddress = detectedLibraries[sourceName][libName];
// Our detection logic encodes bytes into lowercase hex.
if (libAddress.toLowerCase() !== detectedAddress) {
conflicts.push({
library: `${sourceName}:${libName}`,
detectedAddress,
inputAddress: libAddress,
});
}
}
}
}
if (conflicts.length > 0) {
const conflictDescriptions = conflicts
.map((conflict) => ` * ${conflict.library}
given address: ${conflict.inputAddress}
detected address: ${conflict.detectedAddress}`)
.join("\n");
throw new errors_1.HardhatEtherscanPluginError(constants_1.pluginName, `The following detected library addresses are different from the ones provided:
${conflictDescriptions}
You can either fix these addresses in your libraries dictionary or simply remove them to let the plugin autodetect them.`);
}
const mergedLibraries = {};
addLibraries(mergedLibraries, normalizedLibraries);
addLibraries(mergedLibraries, detectedLibraries);
return mergedLibraries;
}
function addLibraries(targetLibraries, newLibraries) {
for (const [sourceName, libraries] of Object.entries(newLibraries)) {
if (targetLibraries[sourceName] === undefined) {
targetLibraries[sourceName] = {};
}
for (const [libName, libAddress] of Object.entries(libraries)) {
targetLibraries[sourceName][libName] = libAddress;
}
}
}
async function normalizeLibraries(allLibraries, detectableLibraries, undetectableLibraries, libraries, contractName) {
const { isAddress } = await Promise.resolve().then(() => __importStar(require("@ethersproject/address")));
const libraryFQNs = new Set();
const normalizedLibraries = {};
for (const [linkedLibraryName, linkedLibraryAddress] of Object.entries(libraries)) {
if (!isAddress(linkedLibraryAddress)) {
throw new errors_1.HardhatEtherscanPluginError(constants_1.pluginName, `You gave a link for the contract ${contractName} with the library ${linkedLibraryName}, but provided this invalid address: ${linkedLibraryAddress}`);
}
const neededLibrary = lookupLibrary(allLibraries, detectableLibraries, undetectableLibraries, linkedLibraryName, contractName);
const neededLibraryFQN = `${neededLibrary.sourceName}:${neededLibrary.libName}`;
// The only way for this library to be already mapped is
// for it to be given twice in the libraries user input:
// once as a library name and another as a fully qualified library name.
if (libraryFQNs.has(neededLibraryFQN)) {
throw new errors_1.HardhatEtherscanPluginError(constants_1.pluginName, `The library names ${neededLibrary.libName} and ${neededLibraryFQN} refer to the same library and were given as two entries in the libraries dictionary.
Remove one of them and review your libraries dictionary before proceeding.`);
}
libraryFQNs.add(neededLibraryFQN);
if (normalizedLibraries[neededLibrary.sourceName] === undefined) {
normalizedLibraries[neededLibrary.sourceName] = {};
}
normalizedLibraries[neededLibrary.sourceName][neededLibrary.libName] =
linkedLibraryAddress;
}
return normalizedLibraries;
}
function lookupLibrary(allLibraries, detectableLibraries, undetectableLibraries, linkedLibraryName, contractName) {
const matchingLibraries = allLibraries.filter((lib) => {
return (lib.libName === linkedLibraryName ||
`${lib.sourceName}:${lib.libName}` === linkedLibraryName);
});
if (matchingLibraries.length === 0) {
let detailedMessage = "";
if (allLibraries.length > 0) {
const undetectableLibraryFQNames = undetectableLibraries
.map((lib) => `${lib.sourceName}:${lib.libName}`)
.map((x) => ` * ${x}`)
.join("\n");
const detectableLibraryFQNames = detectableLibraries
.map((lib) => `${lib.sourceName}:${lib.libName}`)
.map((x) => ` * ${x} (optional)`)
.join("\n");
detailedMessage += `This contract uses the following external libraries:
${undetectableLibraryFQNames}
${detectableLibraryFQNames}`;
if (detectableLibraries.length > 0) {
detailedMessage += `
Libraries marked as optional don't need to be specified since their addresses are autodetected by the plugin.`;
}
}
else {
detailedMessage += "This contract doesn't use any external libraries.";
}
throw new errors_1.HardhatEtherscanPluginError(constants_1.pluginName, `You gave an address for the library ${linkedLibraryName} in the libraries dictionary, which is not one of the libraries of contract ${contractName}.
${detailedMessage}`);
}
if (matchingLibraries.length > 1) {
const matchingLibrariesFQNs = matchingLibraries
.map(({ sourceName, libName }) => `${sourceName}:${libName}`)
.map((x) => ` * ${x}`)
.join("\n");
throw new errors_1.HardhatEtherscanPluginError(constants_1.pluginName, `The library name ${linkedLibraryName} is ambiguous for the contract ${contractName}.
It may resolve to one of the following libraries:
${matchingLibrariesFQNs}
To fix this, choose one of these fully qualified library names and replace it in your libraries dictionary.`);
}
const [neededLibrary] = matchingLibraries;
return neededLibrary;
}
function getLibraryNames(libraries) {
const libraryNames = [];
for (const [sourceName, sourceLibraries] of Object.entries(libraries)) {
for (const libName of Object.keys(sourceLibraries)) {
libraryNames.push({ sourceName, libName });
}
}
return libraryNames;
}
//# sourceMappingURL=libraries.js.map