UNPKG

@solarity/hardhat-migrate

Version:
392 lines 17.7 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Reporter = void 0; exports.createAndInitReporter = createAndInitReporter; exports.resetReporter = resetReporter; /* eslint-disable no-console */ const ora_1 = __importDefault(require("ora")); const ethers_1 = require("ethers"); const ReporterStorage_1 = require("./ReporterStorage"); const NetworkManager_1 = require("../network/NetworkManager"); const utils_1 = require("../../utils"); const verifier_1 = require("../../types/verifier"); /** * Global error handling for network-related issues is conducted within the NetworkManager class */ let BaseReporter = class BaseReporter { _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_1.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${(0, utils_1.underline)(`Running ${file}...`)}`); this._storage.storeMigrationFileBegin(file); } reportTransactionResponseHeader(tx, instanceName) { console.log("\n" + (0, utils_1.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_1.networkManager.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 = (0, ora_1.default)(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 += `> blockTimestamp: ${(await receipt.getBlock()).timestamp}\n`; output += `> account: ${receipt.from}\n`; const value = (await receipt.getTransaction()).value; output += `> value: ${(0, utils_1.castAmount)(value, nativeSymbol)}\n`; output += `> balance: ${(0, utils_1.castAmount)(await receipt.provider.getBalance(receipt.from), nativeSymbol)}\n`; output += `> gasUsed: ${receipt.gasUsed}\n`; output += `> gasPrice: ${(0, utils_1.castAmount)(receipt.gasPrice, nativeSymbol)}\n`; output += `> fee: ${(0, utils_1.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)} ${(0, utils_1.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 = (0, ethers_1.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((0, ethers_1.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 = (0, ethers_1.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 = (0, ethers_1.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 (verifier_1.predefinedChains[chainId] && verifier_1.predefinedChains[chainId].explorers !== undefined && verifier_1.predefinedChains[chainId].explorers.length > 0) { return verifier_1.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_1.networkManager.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_1.networkManager.provider.getNetwork(); } catch { return new ethers_1.Network("Local Ethereum", 1337); } } async _getNativeSymbol() { const chainId = Number(this._network.chainId); if (verifier_1.predefinedChains[chainId]) { return verifier_1.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) ?? verifier_1.predefinedChains[1337]; } catch { chain = verifier_1.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 = verifier_1.predefinedChains[1337].nativeCurrency.symbol; } verifier_1.predefinedChains[chainId] = chain; return chain; } _getInfoFromHardhatConfig(chainId) { let customChains = []; if (this._hre.config.etherscan && this._hre.config.etherscan.customChains) { customChains = this._hre.config.etherscan.customChains; } return customChains.find((chain) => chain.chainId === chainId); } async _tryGetAllRecords() { const url = "https://chainid.network/chains.json"; const response = await NetworkManager_1.networkManager.axios.get(url); // Assuming the JSON response is an array of record objects return response.data; } }; BaseReporter = __decorate([ utils_1.catchError ], BaseReporter); exports.Reporter = null; async function createAndInitReporter(hre) { if (exports.Reporter) { return; } exports.Reporter = new BaseReporter(); await exports.Reporter.init(hre); } /** * Used only in test environments to ensure test atomicity */ function resetReporter() { exports.Reporter = null; } //# sourceMappingURL=Reporter.js.map