@solarity/hardhat-migrate
Version:
The simplest way to deploy smart contracts
498 lines • 24.8 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 { join } from "path";
import { format } from "prettier";
import { existsSync, mkdirSync, writeFile, writeFileSync } from "fs";
import { Reporter } from "./Reporter.js";
import { Stats } from "../Stats.js";
import { castAmount, CatchClassError } from "../../utils/index.js";
/**
* Class that manages the storage of the reported operation.
*
* Produces reports, metrics and logs in a file format.
*/
let ReporterStorage = (() => {
let _classDecorators = [CatchClassError];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
var ReporterStorage = 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);
ReporterStorage = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
__runInitializers(_classThis, _classExtraInitializers);
}
_hre;
_currentReportID = null;
_state;
_currentlyDeployingInstance = "";
_defaultVerificationState = "Nothing to verify";
constructor(_hre) {
this._hre = _hre;
this._state = {
title: "Migration Report",
generalInfo: {
title: "General Information",
migrationFiles: new Set(),
networks: {},
},
reportedContracts: new Set(),
detailedMigrationFiles: [],
stats: {
totalContracts: 0,
totalTransactions: 0,
gasUsed: 0n,
totalGasPrice: 0n,
feePayed: 0n,
nativeCurrencySent: 0n,
},
missingLibraries: new Set(),
recoveredContracts: new Set(),
recoveredTransactions: new Set(),
verificationStats: {
status: this._defaultVerificationState,
verifiedContracts: new Set(),
failedContracts: new Set(),
failedToSaveContracts: new Set(),
alreadyVerifiedContracts: new Set(),
proxyLinkingSuccess: new Set(),
proxyLinkingFailure: new Set(),
},
collisions: {
contracts: new Set(),
transactions: new Set(),
unknown: new Set(),
},
allContracts: new Set(),
allTransactions: new Set(),
};
}
storeMigrationBegin() {
this._commitReport();
}
storeMigrationFileBegin(migrationName) {
this._state.detailedMigrationFiles[Stats.currentMigration] = {
migrationName,
links: new Set(),
responses: new Set(),
receipts: new Set(),
};
}
storeTransactionResponseHeader(transactionResponse, instanceName, txLink) {
if (!this._state.detailedMigrationFiles[Stats.currentMigration]) {
Reporter.addWarning(`Direct deploy without migration file. Cannot process detailed migration file data for ${instanceName} and ${txLink}`);
return;
}
this._state.detailedMigrationFiles[Stats.currentMigration].links.add(txLink);
this._state.detailedMigrationFiles[Stats.currentMigration].responses.add(transactionResponse);
this._currentlyDeployingInstance = instanceName;
this._commitReport();
}
storeTransactionReceipt(transactionReceipt, value) {
if (!this._state.detailedMigrationFiles[Stats.currentMigration]) {
Reporter.addWarning(`Direct deploy without migration file. Cannot process detailed migration file data for ${transactionReceipt.hash}`);
return;
}
this._state.detailedMigrationFiles[Stats.currentMigration].receipts.add(transactionReceipt);
if (transactionReceipt.contractAddress) {
this._state.stats.totalContracts += 1;
this._state.allContracts.add([this._currentlyDeployingInstance, transactionReceipt.contractAddress]);
}
else {
this._state.stats.totalTransactions += 1;
this._state.allTransactions.add([this._currentlyDeployingInstance, transactionReceipt.hash]);
}
this._state.stats.gasUsed += BigInt(transactionReceipt.gasUsed);
const gasPrice = transactionReceipt.gasPrice ?? 0n;
this._state.stats.totalGasPrice += BigInt(gasPrice);
this._state.stats.feePayed += BigInt(transactionReceipt.fee);
this._state.stats.nativeCurrencySent += BigInt(value);
this._commitReport();
}
storeReportedContracts(contracts) {
for (const contract of contracts) {
this._state.reportedContracts.add(contract);
}
this._commitReport();
}
storeDeploymentOfMissingLibrary(libraryName) {
this._state.missingLibraries.add(libraryName);
this._commitReport();
}
storeContractRecovery(contractName, contractAddress) {
this._state.recoveredContracts.add([contractName, contractAddress]);
this._commitReport();
}
storeTransactionRecovery(methodString, savedTx) {
const receipt = savedTx.receipt;
this._state.recoveredTransactions.add([methodString, receipt.hash ? receipt.hash : "N/A"]);
this._commitReport();
}
storeNothingToVerify() {
this._commitReport();
}
storeVerificationSuccess(contractName, contractAddress) {
if (this._state.verificationStats.status === this._defaultVerificationState) {
this._state.verificationStats.status = "Verification successful";
}
this._state.verificationStats.verifiedContracts.add([contractName, contractAddress]);
this._commitReport();
}
storeAlreadyVerified(contractName, contractAddress) {
if (this._state.verificationStats.status === this._defaultVerificationState) {
this._state.verificationStats.status = "Verification successful with already verified contracts";
}
this._state.verificationStats.alreadyVerifiedContracts.add([contractName, contractAddress]);
this._commitReport();
}
storeVerificationFailure(contractName, contractAddress) {
this._state.verificationStats.status = "Verification failed";
this._state.verificationStats.failedContracts.add([contractName, contractAddress]);
this._commitReport();
}
storeVerificationSaveFailure(contractName) {
this._state.verificationStats.status = "Verification failed";
this._state.verificationStats.failedToSaveContracts.add(contractName);
this._commitReport();
}
storeTransactionCollision(oldData, dataToSave) {
this._state.collisions.transactions.add({
prevMigrationNumber: oldData.metadata.migrationNumber,
prevMethodName: oldData.metadata.methodName || "N/A",
newMigrationNumber: dataToSave.metadata.migrationNumber,
newMethodName: dataToSave.metadata.methodName || "N/A",
});
this._commitReport();
}
storeUnknownCollision(metadata, dataToSave) {
this._state.collisions.unknown.add({
prevMigrationNumber: metadata.migrationNumber,
prevContractAddress: metadata.contractName || "N/A",
prevMethodName: metadata.methodName || "N/A",
newMigrationNumber: dataToSave.metadata.migrationNumber,
newContractAddress: dataToSave.metadata.contractName || "N/A",
newMethodName: dataToSave.metadata.methodName || "N/A",
});
this._commitReport();
}
storeContractCollision(oldData, dataToSave) {
this._state.collisions.contracts.add({
prevMigrationNumber: oldData.metadata.migrationNumber,
prevContractAddress: oldData.metadata.contractName || "N/A",
newMigrationNumber: dataToSave.metadata.migrationNumber,
newContractAddress: dataToSave.metadata.contractName || "N/A",
});
this._commitReport();
}
storeMigrationFiles(migrationFiles) {
for (const migrationFile of migrationFiles) {
this._state.generalInfo.migrationFiles.add(migrationFile);
}
this._commitReport();
}
storeChainInfo(network) {
this._state.generalInfo.networks[network.network.name] = network;
this._commitReport();
}
storeSuccessfulProxyLinking(contractName, contractAddress) {
this._state.verificationStats.proxyLinkingSuccess.add([contractName, contractAddress]);
this._commitReport();
}
storeFailedProxyLinking(contractName, contractAddress) {
this._state.verificationStats.proxyLinkingFailure.add([contractName, contractAddress]);
this._commitReport();
}
completeReport() {
this._commitReport();
}
_commitReport() {
const reportID = this._getReportID();
const pathToReportDir = join(this._hre.config.paths.root, this._hre.config.migrate.paths.reportPath);
if (!existsSync(pathToReportDir)) {
mkdirSync(pathToReportDir, { recursive: true });
}
this._getReportContent().then((content) => {
writeFile(join(pathToReportDir, reportID), content, { flag: "w" }, (err) => {
if (err) {
console.error(`Error writing report to ${pathToReportDir}`);
console.error(err);
}
});
});
}
async finishReport() {
const reportID = this._getReportID();
const pathToReportDir = join(this._hre.config.paths.root, this._hre.config.migrate.paths.reportPath);
if (!existsSync(pathToReportDir)) {
mkdirSync(pathToReportDir, { recursive: true });
}
const content = await this._getReportContent();
writeFileSync(join(pathToReportDir, reportID), content, { flag: "w" });
}
_getReportID() {
if (this._currentReportID !== null) {
return this._currentReportID;
}
this._state.title = this._getReportName();
this._currentReportID = this._getReportName();
return this._currentReportID;
}
_getReportName() {
const date = new Date(Date.now()).toISOString();
const extension = this._hre.config.migrate.paths.reportFormat === "json" ? "json" : "md";
return `Migration Report ${date}.${extension}`;
}
_getReportContent() {
const reportFormat = this._hre.config.migrate.paths.reportFormat;
if (reportFormat === "json") {
const serializableState = this._getSerializableState();
return Promise.resolve(JSON.stringify(serializableState, (_, value) => {
if (typeof value === "bigint") {
return value.toString();
}
if (value instanceof Set) {
return Array.from(value);
}
if (value instanceof Map) {
return Object.fromEntries(value);
}
return value;
}, 2));
}
else {
return this._getMarkdownReportContent();
}
}
_getAverageGasPrice() {
const totalExecutions = this._state.stats.totalContracts + this._state.stats.totalTransactions;
if (totalExecutions === 0) {
return 0n;
}
return this._state.stats.totalGasPrice / BigInt(totalExecutions);
}
_getSerializableState() {
return {
...this._state,
stats: {
...this._state.stats,
averageGasPrice: this._getAverageGasPrice(),
},
};
}
async _getMarkdownReportContent() {
const { title, generalInfo, reportedContracts } = this._state;
const actualState = [];
actualState.push({ h1: title });
actualState.push({ h2: generalInfo.title });
actualState.push({ h3: "Migration Files" });
actualState.push({ ul: Array.from(generalInfo.migrationFiles) });
actualState.push({ h3: "Networks" });
actualState.push({
ul: Object.values(generalInfo.networks).map((network) => {
const name = network.network.name;
const chainId = network.network.chainId;
const explorer = network.explorer;
return `${name} - Chain ID: ${chainId}. Explorer: ${explorer}`;
}),
});
if (reportedContracts.size > 0) {
actualState.push({ h2: "Reported Contracts" });
actualState.push({ table: { headers: ["Name", "Address"], rows: Array.from(reportedContracts) } });
}
actualState.push({ h2: "Detailed Migration Files" });
for (const migrationFile of this._state.detailedMigrationFiles) {
if (!migrationFile) {
continue;
}
actualState.push({ h3: migrationFile.migrationName });
actualState.push({ ul: Array.from(migrationFile.links) });
}
actualState.push({ h2: "Stats" });
const averageGasPrice = this._getAverageGasPrice();
actualState.push({
table: {
headers: [
"Total Contracts",
"Total Transactions",
"Gas Used",
"Average Gas Price",
"Fee Payed",
"Native Currency Sent",
],
rows: [
[
this._state.stats.totalContracts,
this._state.stats.totalTransactions,
String(this._state.stats.gasUsed),
castAmount(averageGasPrice),
castAmount(this._state.stats.feePayed),
castAmount(this._state.stats.nativeCurrencySent),
],
],
},
});
actualState.push({
p: ["Total Cost: ", castAmount(this._state.stats.feePayed + this._state.stats.nativeCurrencySent)],
});
if (this._state.missingLibraries.size > 0) {
actualState.push({ h2: "Missing Libraries" });
actualState.push({ ul: Array.from(this._state.missingLibraries) });
}
if (this._state.recoveredContracts.size > 0) {
actualState.push({ h2: "Recovered Contracts" });
actualState.push({ table: { headers: ["Name", "Address"], rows: Array.from(this._state.recoveredContracts) } });
}
if (this._state.recoveredTransactions.size > 0) {
actualState.push({ h2: "Recovered Transactions" });
actualState.push({ table: { headers: ["Name", "Hash"], rows: Array.from(this._state.recoveredTransactions) } });
}
if (this._state.verificationStats.status !== this._defaultVerificationState) {
actualState.push({ h2: `Verification Stats. ${this._state.verificationStats.status}` });
if (this._state.verificationStats.verifiedContracts.size > 0) {
actualState.push({ h3: "Verified Contracts" });
actualState.push({
table: { headers: ["Name", "Address"], rows: Array.from(this._state.verificationStats.verifiedContracts) },
});
}
if (this._state.verificationStats.alreadyVerifiedContracts.size > 0) {
actualState.push({ h3: "Already Verified Contracts" });
actualState.push({
table: {
headers: ["Name", "Address"],
rows: Array.from(this._state.verificationStats.alreadyVerifiedContracts),
},
});
}
if (this._state.verificationStats.failedContracts.size > 0) {
actualState.push({ h3: "Failed Contracts" });
actualState.push({
table: { headers: ["Name", "Address"], rows: Array.from(this._state.verificationStats.failedContracts) },
});
}
if (this._state.verificationStats.failedToSaveContracts.size > 0) {
actualState.push({ h3: "Failed to Save Contracts" });
actualState.push({ ul: Array.from(this._state.verificationStats.failedToSaveContracts) });
}
if (this._state.verificationStats.proxyLinkingSuccess.size > 0) {
actualState.push({ h3: "Proxy Linking Success" });
actualState.push({
table: { headers: ["Name", "Address"], rows: Array.from(this._state.verificationStats.proxyLinkingSuccess) },
});
}
if (this._state.verificationStats.proxyLinkingFailure.size > 0) {
actualState.push({ h3: "Proxy Linking Failure" });
actualState.push({
table: { headers: ["Name", "Address"], rows: Array.from(this._state.verificationStats.proxyLinkingFailure) },
});
}
}
if (this._state.collisions.contracts.size > 0 ||
this._state.collisions.transactions.size > 0 ||
this._state.collisions.unknown.size > 0) {
actualState.push({ h2: "Collisions" });
if (this._state.collisions.contracts.size > 0) {
actualState.push({ h3: "Contracts" });
actualState.push({
table: {
headers: ["Prev Migration Number", "Prev Contract Address", "New Migration Number", "New Contract Address"],
rows: Array.from(this._state.collisions.contracts).map((contract) => [
contract.prevMigrationNumber,
contract.prevContractAddress,
contract.newMigrationNumber,
contract.newContractAddress,
]),
},
});
}
if (this._state.collisions.transactions.size > 0) {
actualState.push({ h3: "Transactions" });
actualState.push({
table: {
headers: ["Prev Migration Number", "Prev Method Name", "New Migration Number", "New Method Name"],
rows: Array.from(this._state.collisions.transactions).map((transaction) => [
transaction.prevMigrationNumber,
transaction.prevMethodName,
transaction.newMigrationNumber,
transaction.newMethodName,
]),
},
});
}
if (this._state.collisions.unknown.size > 0) {
actualState.push({ h3: "Unknown" });
actualState.push({
table: {
headers: [
"Prev Migration Number",
"Prev Contract Address",
"Prev Method Name",
"New Migration Number",
"New Contract Address",
"New Method Name",
],
rows: Array.from(this._state.collisions.unknown).map((unknown) => [
unknown.prevMigrationNumber,
unknown.prevContractAddress,
unknown.prevMethodName,
unknown.newMigrationNumber,
unknown.newContractAddress,
unknown.newMethodName,
]),
},
});
}
}
if (this._state.allContracts.size > 0) {
actualState.push({ h2: "All Contracts" });
actualState.push({ table: { headers: ["Name", "Address"], rows: Array.from(this._state.allContracts) } });
}
if (this._state.allTransactions.size > 0) {
actualState.push({ h2: "All Transactions" });
actualState.push({ table: { headers: ["Name", "Hash"], rows: Array.from(this._state.allTransactions) } });
}
return format((await import("json2md")).default(actualState), {
parser: "markdown",
printWidth: 80,
proseWrap: "always",
});
}
};
return ReporterStorage = _classThis;
})();
export { ReporterStorage };
//# sourceMappingURL=ReporterStorage.js.map