UNPKG

@solarity/hardhat-migrate

Version:
424 lines 20.8 kB
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; }; /* eslint-disable no-console */ import ora from "ora"; import { Network, id } from "ethers"; import { ReporterStorage } from "./ReporterStorage.js"; import { networkManager } from "../network/NetworkManager.js"; import { castAmount, CatchClassError, underline } from "../../utils/index.js"; import { predefinedChains } from "../../../types/verifier.js"; /** * Global error handling for network-related issues is conducted within the NetworkManager class */ let BaseReporter = (() => { let _classDecorators = [CatchClassError]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var BaseReporter = 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); BaseReporter = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); } _hre = {}; _config = {}; _network = {}; _spinner = null; _spinnerMessage = null; _spinnerInterval = null; _spinnerState = []; _nativeSymbol = ""; _explorerUrl = ""; _txExplorerUrl = ""; _warningsToPrint = new Map(); _storage = null; async init(hre) { this._hre = hre; this._config = hre.config.migrate; this._network = await this._getNetwork(); this._nativeSymbol = await this._getNativeSymbol(); this._explorerUrl = await this.getExplorerUrl(); try { this._txExplorerUrl = this._explorerUrl !== "" ? new URL("tx/", this._explorerUrl).toString() : ""; } catch { this._txExplorerUrl = ""; } this._storage = new ReporterStorage(hre); } async completeReport() { await this._storage.finishReport(); } notifyStorageAboutContracts(contracts) { this._storage.storeReportedContracts(contracts); } reportMigrationBegin(files) { this._reportMigrationFiles(files); this._reportChainInfo(); console.log("\nStarting migration...\n"); this._storage.storeMigrationBegin(); } reportMigrationFileBegin(file) { console.log(`\n${underline(`Running ${file}...`)}`); this._storage.storeMigrationFileBegin(file); } reportTransactionResponseHeader(tx, instanceName) { console.log("\n" + underline(this._parseTransactionTitle(tx, instanceName))); const txLink = this._getExplorerLink(tx.hash); console.log(`> explorer: ${txLink}`); this._storage.storeTransactionResponseHeader(tx, instanceName, txLink); } async startTxReporting(tx) { if (this._hre.config.migrate.execution.withoutCLIReporting) { return; } const timeStart = Date.now(); const blockStart = await networkManager.provider.provider.getBlockNumber(); const formatPendingTimeTask = async () => this._formatPendingTime(tx, timeStart, blockStart); return this.startSpinner("tx-report", formatPendingTimeTask); } async startSpinner(id, getSpinnerText) { if (this._spinnerState.includes(id)) return; if (this._spinnerState.length === 0) { this._spinner = ora(await getSpinnerText()).start(); this._spinnerInterval = setInterval(async () => { if (!this._spinner) { clearInterval(this._spinnerInterval); return; } this._spinner.text = await getSpinnerText(); }, this._config.execution.transactionStatusCheckInterval); } this._spinnerState.push(id); } stopSpinner() { if (!this._spinner) return; this._spinnerMessage = null; this._spinnerState.pop(); if (this._spinnerState.length === 0) { clearInterval(this._spinnerInterval); this._spinner.stop(); this._spinner = null; } } async reportTransactionReceipt(receipt) { let output = ""; if (receipt.contractAddress) { output += `> contractAddress: ${receipt.contractAddress}\n`; } const nativeSymbol = this._nativeSymbol; output += `> blockNumber: ${receipt.blockNumber}\n`; output += `> account: ${receipt.from}\n`; const value = (await receipt.getTransaction()).value; output += `> value: ${castAmount(value, nativeSymbol)}\n`; output += `> balance: ${castAmount(await receipt.provider.getBalance(receipt.from), nativeSymbol)}\n`; output += `> gasUsed: ${receipt.gasUsed}\n`; output += `> gasPrice: ${castAmount(receipt.gasPrice, nativeSymbol)}\n`; output += `> fee: ${castAmount(receipt.fee, nativeSymbol)}\n`; console.log(output); this._storage.storeTransactionReceipt(receipt, value); } summary(totalTransactions, totalCost) { const output = `> ${"Total transactions:".padEnd(20)} ${totalTransactions}\n` + `> ${"Final cost:".padEnd(20)} ${castAmount(totalCost, this._nativeSymbol)}\n`; console.log(`\n${output}`); this.reportWarnings(); this._storage.completeReport(); } notifyDeploymentInsteadOfRecovery(contractName) { const output = `\nCan't recover contract address for ${contractName}. Deploying instead...`; console.log(output); } notifyDeploymentOfMissingLibrary(libraryName) { const output = `\nDeploying missing library ${libraryName}...`; console.log(output); this._storage.storeDeploymentOfMissingLibrary(libraryName); } notifyTransactionSendingInsteadOfRecovery(contractMethod) { const output = `\nCan't recover transaction for ${contractMethod}. Sending instead...`; console.log(output); } notifyContractRecovery(contractName, contractAddress) { const output = `\nContract address for ${contractName} has been recovered: ${contractAddress}`; console.log(output); this._storage.storeContractRecovery(contractName, contractAddress); } notifyTransactionRecovery(methodString, savedTx) { const output = `\nTransaction ${methodString} has been recovered.`; console.log(output); this._storage.storeTransactionRecovery(methodString, savedTx); } reportVerificationBatchBegin() { console.log("\nStarting verification of all deployed contracts"); } reportNothingToVerify() { console.log(`\nNothing to verify. Selected network is ${this._network.name}`); this._storage.storeNothingToVerify(); } reportSuccessfulVerification(contractAddress, contractName) { const output = `\nContract ${contractName} (${contractAddress}) verified successfully.`; console.log(output); this._storage.storeVerificationSuccess(contractName, contractAddress); } reportAlreadyVerified(contractAddress, contractName) { const output = `\nContract ${contractName} (${contractAddress}) already verified.`; console.log(output); this._storage.storeAlreadyVerified(contractName, contractAddress); } reportVerificationError(contractAddress, contractName, message) { const output = `\nContract ${contractName} (${contractAddress}) verification failed: ${message}`; console.log(output); this._storage.storeVerificationFailure(contractName, contractAddress); } reportVerificationFailedToSave(contractName) { const output = `\nFailed to save verification arguments for contract: ${contractName}`; console.log(output); this._storage.storeVerificationSaveFailure(contractName); } notifyContractCollisionByName(oldData, dataToSave) { const output = `\nContract collision by Contract Name detected!`; this._printContractCollision(output, oldData, dataToSave); } notifyContractCollisionByKeyFields(oldData, dataToSave) { let output = `\nContract collision by key fields detected!`; output += `\nKey fields are bytecode, from, chainId, value and contract name`; this._printContractCollision(output, oldData, dataToSave); } notifyTransactionCollision(oldData, dataToSave) { let output = `\nTransaction collision detected!`; output += `\n> Previous Collision Details: `; output += `\n\t- Migration Number: ${oldData.metadata.migrationNumber}`; output += `\n\t- Method Name: ${oldData.metadata.methodName || "N/A"}`; output += `\n> New Collision Details: `; output += `\n\t- Migration Number: ${dataToSave.metadata.migrationNumber}`; output += `\n\t- Method Name: ${dataToSave.metadata.methodName || "N/A"}`; const key = id(output); if (!this._warningsToPrint.has(key)) { console.log(output); } this._warningsToPrint.set(key, output); this._storage.storeTransactionCollision(oldData, dataToSave); } addWarning(warning) { this._warningsToPrint.set(id(warning), warning); } notifyUnknownCollision(metadata, dataToSave) { let output = `\nUnknown collision detected!`; output += `\n> Previous Collision Details: `; output += `\n\t- Migration Number: ${metadata.migrationNumber}`; output += `\n\t- Method Name: ${metadata.methodName || "N/A"}`; output += `\n\t- Contract Name: ${metadata.contractName || "N/A"}`; output += `\n> New Collision Details: `; output += `\n\t- Migration Number: ${dataToSave.metadata.migrationNumber}`; output += `\n\t- Method Name: ${dataToSave.metadata.methodName || "N/A"}`; output += `\n\t- Contract Name: ${dataToSave.metadata.contractName || "N/A"}`; const key = id(output); if (!this._warningsToPrint.has(key)) { console.log(output); } this._warningsToPrint.set(key, output); this._storage.storeUnknownCollision(metadata, dataToSave); } reportWarnings() { if (this.getWarningsCount() === 0) { return; } console.log("\nWarnings:"); this._warningsToPrint.forEach((warning) => { console.log(warning); }); console.log("\n\nDue to the detected collision(s), there's a high likelihood that migration recovery using '--continue' may not function as expected.\n" + "To mitigate this, consider specifying a unique name for the contract during deployment.\n"); console.log(""); } getWarningsCount() { return this._warningsToPrint.size; } _printContractCollision(output, oldData, dataToSave) { output += `\n> Contract: ${oldData.contractKeyData?.name || dataToSave.contractKeyData?.name}`; output += `\n> Previous Collision Details: `; output += `\n\t- Migration Number: ${oldData.metadata.migrationNumber}`; output += `\n\t- Contract Address: ${oldData.contractAddress}`; output += `\n> New Collision Details: `; output += `\n\t- Migration Number: ${dataToSave.metadata.migrationNumber}`; output += `\n\t- Contract Address: ${dataToSave.contractAddress}`; const key = id(`${oldData.contractAddress}-${dataToSave.contractAddress}`); if (!this._warningsToPrint.has(key)) { console.log(output); } this._warningsToPrint.set(key, output); this._storage.storeContractCollision(oldData, dataToSave); } async getExplorerUrl() { const chainId = Number(this._network.chainId); const customChain = this._getInfoFromHardhatConfig(chainId); if (customChain) { return customChain.urls.browserURL; } if (predefinedChains[chainId] && predefinedChains[chainId].explorers !== undefined && predefinedChains[chainId].explorers.length > 0) { return predefinedChains[chainId].explorers[0].url; } const chain = await this._getChainMetadataById(chainId); return chain.explorers[0].url; } reportSuccessfulProxyLinking(proxyAddress, implementationAddress) { console.log(`Proxy ${proxyAddress} linked to implementation ${implementationAddress}`); this._storage.storeSuccessfulProxyLinking(proxyAddress, implementationAddress); } reportFailedProxyLinking(proxyAddress, implementationAddress, result) { console.log(`Failed to link proxy ${proxyAddress} to implementation ${implementationAddress}: ${result}`); this._storage.storeFailedProxyLinking(proxyAddress, implementationAddress); } notifyOfProxyConstructorUsage(proxyFactoryName, constructorName) { console.log(`\nProxy constructor ${constructorName} used in ${proxyFactoryName} (proxy)\n`); } _parseTransactionTitle(tx, instanceName) { if (tx.to === null) { if (instanceName.split(":").length == 1) { return `Deploying ${instanceName}`; } return `Deploying${instanceName ? " " + instanceName.split(":")[1] : ""}`; } return `Transaction: ${instanceName}`; } async _formatPendingTime(tx, startTime, blockStart) { if (this._spinnerMessage) { return this._spinnerMessage; } return `Confirmations: ${await tx.confirmations()}; Blocks: ${(await networkManager.provider.provider.getBlockNumber()) - blockStart}; Seconds: ${((Date.now() - startTime) / 1000).toFixed(0)}`; } _getExplorerLink(txHash) { try { return this._txExplorerUrl !== "" ? new URL(txHash, this._txExplorerUrl).toString() : `tx/${txHash}`; } catch { return `tx/${txHash}`; } } _reportMigrationFiles(files) { console.log("\nMigration files:"); files.forEach((file) => { console.log(`> ${file}`); }); console.log(""); this._storage.storeMigrationFiles(files); } _reportChainInfo() { console.log(`> ${"Network:".padEnd(20)} ${this._network.name}`); console.log(`> ${"Network id:".padEnd(20)} ${this._network.chainId}`); this._storage.storeChainInfo({ network: this._network, explorer: this._explorerUrl }); } async _getNetwork() { try { return networkManager.provider.provider.getNetwork(); } catch { return new Network("Local Ethereum", 1337); } } async _getNativeSymbol() { const chainId = Number(this._network.chainId); if (predefinedChains[chainId]) { return predefinedChains[chainId].nativeCurrency.symbol; } const chain = await this._getChainMetadataById(chainId); return chain.nativeCurrency.symbol; } async _getChainMetadataById(chainId) { let chain; try { const chains = await this._tryGetAllRecords(); chain = chains.find((chain) => chain.chainId === chainId) ?? predefinedChains[1337]; } catch { chain = predefinedChains[1337]; } if (chain.explorers === undefined || chain.explorers.length === 0) { chain.explorers = [ { url: "", name: "", }, ]; } const hardhatChainInfo = this._getInfoFromHardhatConfig(chainId); if (hardhatChainInfo && chain.explorers.length > 0 && hardhatChainInfo.urls.browserURL !== chain.explorers[0].url) { chain.explorers[0].url = hardhatChainInfo.urls.browserURL; // Also we reset the Native Currency symbol as it may be different chain.nativeCurrency.symbol = predefinedChains[1337].nativeCurrency.symbol; } predefinedChains[chainId] = chain; return chain; } _getInfoFromHardhatConfig(chainId) { const cfg = this._hre.config; const etherscanChains = cfg.etherscan?.customChains ?? []; const blockscoutChains = cfg.blockscout?.customChains ?? []; const merged = [...blockscoutChains, ...etherscanChains]; const deduped = merged.filter((chain, index, self) => index === self.findIndex((c) => c.chainId === chain.chainId)); return deduped.find((chain) => chain.chainId === chainId); } async _tryGetAllRecords() { const url = "https://chainid.network/chains.json"; const response = await networkManager.axios.get(url); // Assuming the JSON response is an array of record objects return response.data; } }; return BaseReporter = _classThis; })(); export let Reporter = null; export async function createAndInitReporter(hre) { if (Reporter) { return; } Reporter = new BaseReporter(); await Reporter.init(hre); } /** * Used only in test environments to ensure test atomicity */ export function resetReporter() { Reporter = null; } //# sourceMappingURL=Reporter.js.map