@solarity/hardhat-migrate
Version:
The simplest way to deploy smart contracts
392 lines • 17.7 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;
};
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