UNPKG

@solarity/hardhat-migrate

Version:
199 lines 9.91 kB
"use strict"; 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