@solarity/hardhat-migrate
Version:
The simplest way to deploy smart contracts
199 lines • 9.91 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Linker = void 0;
exports.createLinker = createLinker;
const ethers_1 = require("ethers");
const MinimalContract_1 = require("./MinimalContract");
const errors_1 = require("../errors");
const utils_1 = require("../utils");
const Reporter_1 = require("../tools/reporters/Reporter");
const ArtifactProcessor_1 = require("../tools/storage/ArtifactProcessor");
const TransactionProcessor_1 = require("../tools/storage/TransactionProcessor");
let LinkerHelper = class LinkerHelper {
_hre;
constructor(_hre) {
this._hre = _hre;
}
isBytecodeNeedsLinking(bytecode) {
return bytecode.indexOf("__") === -1;
}
async tryLinkBytecode(contractName, bytecode, libraries) {
const artifact = this._mustGetContractArtifact(contractName, bytecode);
const neededLibraries = this._cleanNeededLibraries(bytecode, artifact, artifact.neededLibraries);
let linksToApply = new Map();
for (const [linkedLibraryName, linkedLibraryAddress] of Object.entries(libraries)) {
const neededLibrary = this._mustGetNeededLibrary(neededLibraries, linkedLibraryName, linksToApply);
const neededLibraryFQN = `${neededLibrary.sourceName}:${neededLibrary.libName}`;
linksToApply.set(neededLibraryFQN, {
sourceName: neededLibrary.sourceName,
libraryName: neededLibrary.libName,
address: await (0, ethers_1.resolveAddress)(linkedLibraryAddress),
});
}
if (linksToApply.size < neededLibraries.length) {
const separatelyDeployedLibraries = await this._findMissingLibraries(neededLibraries.filter((lib) => !linksToApply.has(`${lib.sourceName}:${lib.libName}`)));
linksToApply = new Map([...linksToApply.entries(), ...separatelyDeployedLibraries.entries()]);
this._validateLibrariesToLink(linksToApply, neededLibraries);
}
return this._linkBytecode(bytecode, artifact, [...linksToApply.values()]);
}
_mustGetNeededLibrary(neededLibraries, libraryName, linksToApply) {
const matchingNeededLibraries = neededLibraries.filter((lib) => lib.libName === libraryName || `${lib.sourceName}:${lib.libName}` === libraryName);
return this._validateMatchedNeededLibraries(neededLibraries, matchingNeededLibraries, linksToApply);
}
_validateMatchedNeededLibraries(neededLibraries, matchingNeededLibraries, linksToApply) {
if (matchingNeededLibraries.length === 0) {
if (neededLibraries.length > 0) {
const libraryFQNames = neededLibraries
.map((lib) => `${lib.sourceName}:${lib.libName}`)
.map((x) => `* ${x}`)
.join("\n");
throw new errors_1.MigrateError(`The libraries needed are:\n${libraryFQNames}`);
}
else {
throw new errors_1.MigrateError("This contract doesn't need linking any libraries.");
}
}
if (matchingNeededLibraries.length > 1) {
const matchingNeededLibrariesFQNs = matchingNeededLibraries
.map(({ sourceName, libName }) => `${sourceName}:${libName}`)
.map((x) => `* ${x}`)
.join("\n");
throw new errors_1.MigrateError(`The library name is ambiguous.\nIt may resolve to one of the following libraries:\n${matchingNeededLibrariesFQNs}\n\nTo fix this, choose one of these fully qualified library names and replace where appropriate.`);
}
const neededLibrary = matchingNeededLibraries[0];
const neededLibraryFQN = `${neededLibrary.sourceName}:${neededLibrary.libName}`;
if (linksToApply.has(neededLibraryFQN)) {
throw new errors_1.MigrateError(`The library names ${neededLibrary.libName} and ${neededLibraryFQN} refer to the same library and were given as two separate library links.\nRemove one of them and review your library links before proceeding.`);
}
return neededLibrary;
}
async _findMissingLibraries(missingLibraries) {
const missingLibrariesMap = new Map();
for (const missingLibrary of missingLibraries) {
const lib = `${missingLibrary.sourceName}:${missingLibrary.libName}`;
const address = await this._getOrDeployLibrary(lib);
if ((0, ethers_1.isAddress)(address)) {
missingLibrariesMap.set(lib, {
sourceName: missingLibrary.sourceName,
libraryName: missingLibrary.libName,
address: address,
});
}
}
return missingLibrariesMap;
}
_validateLibrariesToLink(linksToApply, neededLibraries) {
if (linksToApply.size < neededLibraries.length) {
const missingLibraries = neededLibraries
.map((lib) => `${lib.sourceName}:${lib.libName}`)
.filter((libFQName) => !linksToApply.has(libFQName))
.map((x) => `* ${x}`)
.join("\n");
throw new errors_1.MigrateError(`The contract is missing links for the following libraries:\n${missingLibraries}`);
}
}
_cleanNeededLibraries(bytecode, artifact, libraries) {
const actuallyNeededLibs = new Map();
for (const { sourceName, libName } of libraries) {
const linkReferences = artifact.linkReferences[sourceName][libName];
for (const { start, length } of linkReferences) {
if (!this._isLinkedLibrary(bytecode, start, length)) {
actuallyNeededLibs.set(`${sourceName}:${libName}`, { sourceName, libName });
}
}
}
return [...actuallyNeededLibs.values()];
}
/**
* The address of the linked library can be extracted like this: bytecode.slice(prefixLength + 2, suffixStart + 2)
*/
_isLinkedLibrary(bytecode, start, length) {
const prefixLength = start * 2;
const prefix = bytecode.slice(prefixLength + 2, prefixLength + 5);
const suffixStart = (start + length) * 2;
const suffix = bytecode.slice(suffixStart - 1, suffixStart + 2);
return `${prefix}${suffix}` !== "__$$__";
}
_linkBytecode(bytecode, artifact, libraries) {
for (const { sourceName, libraryName, address } of libraries) {
const linkReferences = artifact.linkReferences[sourceName][libraryName];
for (const { start, length } of linkReferences) {
const prefixLength = 2 + start * 2;
const prefix = bytecode.slice(0, prefixLength);
const suffixStart = 2 + (start + length) * 2;
const suffix = bytecode.slice(suffixStart);
bytecode = prefix + address.slice(2) + suffix;
}
}
return bytecode;
}
async _getOrDeployLibrary(libraryName) {
try {
return (await TransactionProcessor_1.TransactionProcessor?.tryRestoreContractAddressByName(libraryName));
}
catch {
const artifact = this._mustGetLibraryArtifact(libraryName);
// https://github.com/ethers-io/ethers.js/issues/2431
// https://github.com/ethers-io/ethers.js/issues/1126
const core = new MinimalContract_1.MinimalContract(this._hre, artifact.bytecode, new ethers_1.Interface(artifact.abi), libraryName);
Reporter_1.Reporter.notifyDeploymentOfMissingLibrary(libraryName);
return core.deploy();
}
}
_mustGetContractArtifact(contractName, contractBytecode) {
const artifact = this._getContractFromStorage(contractName, contractBytecode);
if (!artifact) {
throw new errors_1.MigrateError(`Contract artifact of ${contractName} not found. Linking cannot be performed.`);
}
return artifact;
}
_mustGetLibraryArtifact(libraryName) {
const artifact = this._getContractFromStorage(libraryName);
if (!artifact) {
throw new errors_1.MigrateError(`Library artifact of ${libraryName} not found. Linking cannot be performed.`);
}
return artifact;
}
_getContractFromStorage(contractName, contractBytecode) {
try {
return ArtifactProcessor_1.ArtifactProcessor.tryGetArtifactByName(contractName);
}
catch {
/* ignore */
}
try {
const txData = TransactionProcessor_1.TransactionProcessor?.tryRestoreSavedDeployedTxByContractName(contractName);
return ArtifactProcessor_1.ArtifactProcessor.tryGetArtifactByName(txData.metadata.fullyQualifiedContractName);
}
catch {
/* ignore */
}
if (contractBytecode) {
try {
return ArtifactProcessor_1.ArtifactProcessor.tryGetArtifactByBytecode(contractBytecode);
}
catch {
/* ignore */
}
}
return null;
}
};
LinkerHelper = __decorate([
utils_1.catchError
], LinkerHelper);
exports.Linker = null;
function createLinker(hre) {
if (exports.Linker) {
return;
}
exports.Linker = new LinkerHelper(hre);
}
//# sourceMappingURL=Linker.js.map