hardhat-gas-reporter
Version:
Gas Analytics plugin for Hardhat
376 lines • 16.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateTerminalTextTable = void 0;
const chalk_1 = __importDefault(require("chalk"));
const lodash_1 = __importDefault(require("lodash"));
const cli_table3_1 = __importDefault(require("cli-table3"));
const units_1 = require("@ethersproject/units");
const constants_1 = require("../../constants");
const ui_1 = require("../../utils/ui");
/**
* Generates a gas statistics text table formatted for terminal or file.
* Based on Alan Lu's (github.com/cag) stats for Gnosis
* @param {HardhatRuntimeEnvironment} hre
* @param {GasData} data
* @param {GasReporterOptions} options
* @param {string} string
*/
function generateTerminalTextTable(hre, data, options, toolchain) {
// Default cols (without L2)
let numberOfCols = 7;
let blockLimitColumnWidth = 2;
let deploymentsTitleSpacerWidth = 2;
let contractTitleSpacerWidth = 5;
let executionGasAverageTitle = "Avg";
let calldataGasAverageTitle = "";
let optionalColor;
if (options.noColors || options.outputFile !== undefined) {
chalk_1.default.level = 0;
}
else {
chalk_1.default.level = 1;
}
if (options.darkMode) {
optionalColor = chalk_1.default.cyan;
}
else {
optionalColor = chalk_1.default.bold;
}
if (options.L2 !== undefined) {
numberOfCols = 8;
blockLimitColumnWidth = 3;
deploymentsTitleSpacerWidth = 3;
contractTitleSpacerWidth = 6;
executionGasAverageTitle = "L2 Avg (Exec)";
if (options.L2 === "optimism" || options.L2 === "base") {
calldataGasAverageTitle = "L1 Avg (Data)";
}
if (options.L2 === "arbitrum") {
calldataGasAverageTitle = "L1 Avg (Bytes)";
}
}
// eslint-disable-next-line
const emptyRow = [{ colSpan: numberOfCols, content: "" }];
// ---------------------------------------------------------------------------------------------
// Methods: section assembly
// ---------------------------------------------------------------------------------------------
const methodRows = [];
const addedContracts = [];
lodash_1.default.forEach(data.methods, (method) => {
if (!method)
return;
// Contracts name row
if (!addedContracts.includes(method.contract) && method.gasData.length > 0) {
addedContracts.push(method.contract);
const contractNameSection = {
row: [
{ hAlign: "left", colSpan: 2, content: `${optionalColor(method.contract)}` },
{ hAlign: "left", colSpan: contractTitleSpacerWidth, content: "" }
],
contractName: method.contract,
methodName: "0"
};
methodRows.push(contractNameSection);
}
const stats = {};
if (method.gasData.length > 0) {
stats.executionGasAverage = (0, units_1.commify)(method.executionGasAverage);
stats.cost = (method.cost === undefined) ? chalk_1.default.grey("-") : method.cost;
// Also writes dash when average is zero
stats.calldataGasAverage = (method.calldataGasAverage)
? (0, units_1.commify)(method.calldataGasAverage)
: chalk_1.default.grey("-");
if (options.trackGasDeltas) {
stats.executionGasAverage = (0, ui_1.renderWithGasDelta)(stats.executionGasAverage, method.executionGasAverageDelta || 0, true);
stats.calldataGasAverage = (0, ui_1.renderWithGasDelta)(stats.calldataGasAverage, method.calldataGasAverageDelta || 0, true);
}
}
else {
stats.executionGasAverage = chalk_1.default.grey("-");
stats.cost = chalk_1.default.grey("-");
}
if ((0, ui_1.costIsBelowPrecision)(stats.cost, options)) {
stats.cost = chalk_1.default.magenta.bold(constants_1.UNICODE_TRIANGLE);
}
if (method.min && method.max) {
const uniform = (method.min === method.max);
let min = chalk_1.default.cyan((0, units_1.commify)(method.min));
let max = chalk_1.default.red((0, units_1.commify)(method.max));
if (options.trackGasDeltas) {
min = (0, ui_1.renderWithGasDelta)(min, method.minDelta || 0, true);
max = (0, ui_1.renderWithGasDelta)(max, method.maxDelta || 0, true);
}
stats.min = uniform ? chalk_1.default.grey("-") : min;
stats.max = uniform ? chalk_1.default.grey("-") : max;
}
const fnName = options.showMethodSig ? method.fnSig : method.method;
const indented = (method.isCall)
? (0, ui_1.indentTextWithSymbol)(fnName, chalk_1.default.magenta.bold(constants_1.UNICODE_CIRCLE))
: (0, ui_1.indentText)(fnName);
if (options.showUncalledMethods || method.numberOfCalls > 0) {
const row = [];
row.push({ hAlign: "left", colSpan: 2, content: indented });
row.push({ hAlign: "right", colSpan: 1, content: stats.min });
row.push({ hAlign: "right", colSpan: 1, content: stats.max });
row.push({ hAlign: "right", colSpan: 1, content: stats.executionGasAverage });
if (options.L2 !== undefined) {
row.push({ hAlign: "right", colSpan: 1, content: stats.calldataGasAverage });
}
row.push({ hAlign: "right", colSpan: 1, content: method.numberOfCalls });
row.push({
hAlign: "right",
colSpan: 1,
content: chalk_1.default.green(stats.cost.toString())
});
const section = {
row,
contractName: method.contract,
methodName: fnName
};
methodRows.push(section);
}
});
// ---------------------------------------------------------------------------------------------
// Deployments: section assembly
// ---------------------------------------------------------------------------------------------
const deployRows = [];
// Alphabetize contract names
data.deployments.sort((a, b) => a.name.localeCompare(b.name));
data.deployments.forEach(deployment => {
const stats = {};
if (deployment.gasData.length === 0)
return;
stats.cost = (deployment.cost === undefined) ? chalk_1.default.grey("-") : deployment.cost;
if ((0, ui_1.costIsBelowPrecision)(stats.cost, options)) {
stats.cost = chalk_1.default.magenta.bold(constants_1.UNICODE_TRIANGLE);
}
stats.executionGasAverage = (0, units_1.commify)(deployment.executionGasAverage);
stats.calldataGasAverage = (deployment.calldataGasAverage === undefined)
? ""
: (0, units_1.commify)(deployment.calldataGasAverage);
if (options.trackGasDeltas) {
stats.executionGasAverage = (0, ui_1.renderWithGasDelta)(stats.executionGasAverage, deployment.executionGasAverageDelta || 0, true);
stats.calldataGasAverage = (0, ui_1.renderWithGasDelta)(stats.calldataGasAverage, deployment.calldataGasAverageDelta || 0, true);
}
if (deployment.min && deployment.max) {
const uniform = (deployment.min === deployment.max);
let min = chalk_1.default.cyan((0, units_1.commify)(deployment.min));
let max = chalk_1.default.red((0, units_1.commify)(deployment.max));
if (options.trackGasDeltas) {
min = (0, ui_1.renderWithGasDelta)(min, deployment.minDelta || 0, true);
max = (0, ui_1.renderWithGasDelta)(max, deployment.maxDelta || 0, true);
}
stats.min = uniform ? chalk_1.default.grey("-") : min;
stats.max = uniform ? chalk_1.default.grey("-") : max;
}
const section = [];
section.push({ hAlign: "left", colSpan: 2, content: chalk_1.default.bold(deployment.name) });
section.push({ hAlign: "right", colSpan: 1, content: stats.min });
section.push({ hAlign: "right", colSpan: 1, content: stats.max });
section.push({ hAlign: "right", colSpan: 1, content: stats.executionGasAverage });
if (options.L2 !== undefined) {
section.push({ hAlign: "right", colSpan: 1, content: stats.calldataGasAverage });
}
section.push({
hAlign: "right",
colSpan: 1,
content: `${deployment.percent} %`
});
section.push({
hAlign: "right",
colSpan: 1,
content: chalk_1.default.green(stats.cost.toString())
});
deployRows.push(section);
});
// ---------------------------------------------------------------------------------------------
// Headers: section assembly
// ---------------------------------------------------------------------------------------------
// Configure indentation for RTD
const leftPad = options.rst ? " " : "";
// Format table
const table = new cli_table3_1.default({
style: { head: [], border: [], "padding-left": 2, "padding-right": 2 },
colWidths: [numberOfCols],
chars: {
mid: "·",
"top-mid": "|",
"left-mid": `${leftPad}·`,
"mid-mid": "|",
"right-mid": "·",
left: `${leftPad}|`,
"top-left": `${leftPad}·`,
"top-right": "·",
"bottom-left": `${leftPad}·`,
"bottom-right": "·",
middle: "·",
top: "·",
bottom: "·",
"bottom-mid": "|"
}
});
const title = [
{
hAlign: "left",
colSpan: numberOfCols,
content: chalk_1.default.green.bold(`Solidity and Network Configuration`)
}
];
// ============
// SOLC CONFIG
// ============
// Format and load methods metrics
const solcConfig = [
{
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`Solidity: ${options.solcInfo.version}`)
},
{
hAlign: "left",
colSpan: 1,
content: chalk_1.default.cyan(`Optim: ${options.solcInfo.optimizer}`)
},
{
hAlign: "left",
colSpan: 1,
content: chalk_1.default.cyan(`Runs: ${options.solcInfo.runs}`)
},
{
hAlign: "left",
colSpan: 1,
content: chalk_1.default.cyan(`viaIR: ${options.solcInfo.viaIR.toString()}`)
},
{
hAlign: "center",
colSpan: blockLimitColumnWidth,
content: chalk_1.default.cyan(`Block: ${(0, units_1.commify)(options.blockGasLimit)} gas`)
}
];
// ==============
// NETWORK CONFIG
// ==============
let networkConfig = [];
const opStackConfig = [];
if (options.tokenPrice && options.gasPrice) {
const { l1gwei, l2gwei, l1gweiNote, l2gweiNote, l1GweiBlobBaseFee, network, rate, currency, token } = (0, ui_1.getCommonTableVals)(options);
networkConfig.push({
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`Network: ${network}`)
});
networkConfig.push({
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`L1: ${l1gwei} gwei ${l1gweiNote}`)
});
if (options.L2 !== undefined) {
networkConfig.push({
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`L2: ${l2gwei} gwei ${l2gweiNote}`)
});
}
else {
networkConfig.push({ colSpan: 1, content: " " });
}
networkConfig.push({
hAlign: "center",
colSpan: 2,
content: chalk_1.default.magenta(`${rate} ${currency}/${token}`)
});
if (options.L2 === "optimism" || options.L2 === "base") {
opStackConfig.push({ colSpan: 2, content: " " });
opStackConfig.push({
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`L1: ${l1GweiBlobBaseFee} gwei (blobBaseFee)`)
});
opStackConfig.push({
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`Base Fee Scalar: ${options.opStackBaseFeeScalar}`)
});
opStackConfig.push({
hAlign: "left",
colSpan: 2,
content: chalk_1.default.cyan(`Blob Fee Scalar: ${options.opStackBlobBaseFeeScalar}`)
});
}
}
else {
networkConfig = [
{ hAlign: "left", colSpan: numberOfCols, content: chalk_1.default.green.bold("Methods") }
];
}
// ===============
// METHODS HEADER
// ===============
const methodsHeader = [];
methodsHeader.push({ hAlign: "left", colSpan: 2, content: chalk_1.default.green.bold("Contracts / Methods") });
methodsHeader.push({ hAlign: "left", colSpan: 1, content: chalk_1.default.bold("Min") });
methodsHeader.push({ hAlign: "left", colSpan: 1, content: chalk_1.default.bold("Max") });
methodsHeader.push({ hAlign: "left", colSpan: 1, content: chalk_1.default.bold(executionGasAverageTitle) });
if (options.L2 !== undefined) {
methodsHeader.push({ hAlign: "left", colSpan: 1, content: chalk_1.default.bold(calldataGasAverageTitle) });
}
methodsHeader.push({ hAlign: "left", colSpan: 1, content: chalk_1.default.bold("# calls") });
methodsHeader.push({ hAlign: "left", colSpan: 1, content: chalk_1.default.bold(`${options.currency.toLowerCase()} (avg)`) });
// ===============
// SYMBOL KEY
// ===============
const { intrinsicMsg, nonZeroMsg } = (0, ui_1.getCommonTableVals)(options);
const keyTitle = [{
hAlign: "left", colSpan: numberOfCols, content: chalk_1.default.green.bold("Key")
}];
const keyCall = [{
hAlign: "left", colSpan: numberOfCols, content: `${chalk_1.default.magenta.bold(constants_1.UNICODE_CIRCLE)} ${intrinsicMsg}`
}];
const keyAir = [{
hAlign: "left", colSpan: numberOfCols, content: `${chalk_1.default.magenta.bold(constants_1.UNICODE_TRIANGLE)} ${nonZeroMsg}`
}];
const keyToolchain = [{
hAlign: "left", colSpan: numberOfCols, content: `${chalk_1.default.magenta("Toolchain:")} ${toolchain}`
}];
// ---------------------------------------------------------------------------------------------
// Final table assembly
// ---------------------------------------------------------------------------------------------
table.push(title);
table.push(solcConfig);
table.push(networkConfig);
if (opStackConfig.length > 0) {
table.push(opStackConfig);
}
table.push(methodsHeader);
methodRows.sort((a, b) => {
const contractName = a.contractName.localeCompare(b.contractName);
const methodName = a.methodName.localeCompare(b.methodName);
return contractName || methodName;
});
const rows = methodRows.map(val => val.row);
rows.forEach(row => table.push(row));
if (deployRows.length) {
const deploymentsSubtitle = [
{
hAlign: "left",
colSpan: 3,
content: chalk_1.default.green.bold("Deployments")
},
{ hAlign: "right", colSpan: deploymentsTitleSpacerWidth, content: "" },
{ hAlign: "left", colSpan: 1, content: chalk_1.default.bold(`% of limit`) },
{ hAlign: "left", colSpan: 1, content: "" }
];
table.push(deploymentsSubtitle);
deployRows.forEach((row) => table.push(row));
}
table.push(keyTitle);
table.push(keyCall);
table.push(keyAir);
table.push(keyToolchain);
return table.toString();
}
exports.generateTerminalTextTable = generateTerminalTextTable;
//# sourceMappingURL=terminal.js.map