@solarity/hardhat-migrate
Version:
The simplest way to deploy smart contracts
226 lines • 12.4 kB
JavaScript
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
import { Interface, isAddress, resolveAddress } from "ethers";
import { MinimalContract } from "./MinimalContract.js";
import { MigrateError } from "../utils/index.js";
import { CatchClassError } from "../utils/index.js";
import { Reporter } from "../tools/reporters/Reporter.js";
import { ArtifactProcessor } from "../tools/storage/ArtifactProcessor.js";
import { TransactionProcessor } from "../tools/storage/TransactionProcessor.js";
let LinkerHelper = (() => {
let _classDecorators = [CatchClassError];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
var LinkerHelper = class {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
LinkerHelper = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
__runInitializers(_classThis, _classExtraInitializers);
}
constructor() { }
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 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 MigrateError(`The libraries needed are:\n${libraryFQNames}`);
}
else {
throw new 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 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 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 (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 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?.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(artifact.bytecode, new Interface(artifact.abi), libraryName);
Reporter.notifyDeploymentOfMissingLibrary(libraryName);
return core.deploy();
}
}
_mustGetContractArtifact(contractName, contractBytecode) {
const artifact = this._getContractFromStorage(contractName, contractBytecode);
if (!artifact) {
throw new MigrateError(`Contract artifact of ${contractName} not found. Linking cannot be performed.`);
}
return artifact;
}
_mustGetLibraryArtifact(libraryName) {
const artifact = this._getContractFromStorage(libraryName);
if (!artifact) {
throw new MigrateError(`Library artifact of ${libraryName} not found. Linking cannot be performed.`);
}
return artifact;
}
_getContractFromStorage(contractName, contractBytecode) {
try {
return ArtifactProcessor.tryGetArtifactByName(contractName);
}
catch {
/* ignore */
}
try {
const txData = TransactionProcessor?.tryRestoreSavedDeployedTxByContractName(contractName);
return ArtifactProcessor.tryGetArtifactByName(txData.metadata.fullyQualifiedContractName);
}
catch {
/* ignore */
}
if (contractBytecode) {
try {
return ArtifactProcessor.tryGetArtifactByBytecode(contractBytecode);
}
catch {
/* ignore */
}
}
return null;
}
};
return LinkerHelper = _classThis;
})();
export let Linker = new LinkerHelper();
//# sourceMappingURL=Linker.js.map