hardhat-gas-reporter
Version:
Gas Analytics plugin for Hardhat
229 lines (197 loc) • 6.93 kB
text/typescript
import { cloneDeep } from "lodash" // used in extendConfig, cannot await import
import { EIP1193Provider, HardhatConfig, HardhatUserConfig } from "hardhat/types";
import { TASK_TEST, TASK_COMPILE } from "hardhat/builtin-tasks/task-names";
import {
extendConfig,
extendEnvironment,
extendProvider,
task,
subtask
} from "hardhat/config";
import "./type-extensions";
import { GasReporterExecutionContext, GasReporterOutput } from "./types";
import { getDefaultOptions } from './lib/options';
import { GasReporterProvider } from "./lib/provider";
import {
TASK_GAS_REPORTER_MERGE,
TASK_GAS_REPORTER_MERGE_REPORTS,
TASK_GAS_REPORTER_MERGE_LEGACY,
TASK_GAS_REPORTER_MERGE_REPORTS_LEGACY,
TASK_GAS_REPORTER_START,
TASK_GAS_REPORTER_STOP
} from "./task-names"
let _globalGasReporterProviderReference: GasReporterProvider;
// ========================
// EXTENSIONS
// ========================
/* Config */
extendConfig(
(config: HardhatConfig, userConfig: Readonly<HardhatUserConfig>) => {
let options = getDefaultOptions(userConfig);
// Deep clone userConfig otherwise HH will throw unauthorized modification error
if (userConfig.gasReporter !== undefined) {
options = Object.assign(options, cloneDeep(userConfig.gasReporter));
}
(config as any).gasReporter = options;
}
);
/* Environment */
extendEnvironment((hre) => {
hre.__hhgrec = {
collector: undefined,
task: undefined,
}
});
/* Provider */
extendProvider(async (provider) => {
const newProvider = new GasReporterProvider(provider);
_globalGasReporterProviderReference = newProvider;
return newProvider;
});
/*
Initialize the provider with the execution context. This is called in
`TASK_GAS_REPORTER_START` at the very end of setup. Provider extension above should
not be used on unrelated tasks.
*/
export async function initializeGasReporterProvider(
provider: EIP1193Provider,
context: GasReporterExecutionContext
) {
// Other plugins (ex: hardhat-tracer) may wrap the provider in a way
// that doesn't expose `init()`, so we init the underlying provider
// here by making a cheap call.
await provider.request({ method: "eth_blockNumber", params: []});
_globalGasReporterProviderReference.initializeGasReporterProvider(context);
}
// ========================
// BUILT-IN OVERRIDES
// ========================
/**
* Overrides Hardhat built-in task TASK_TEST to report gas usage
*/
task(TASK_TEST).setAction(
async (args: any, hre, runSuper) => {
hre.__hhgrec.task = TASK_TEST;
await hre.run(TASK_GAS_REPORTER_START, args);
await runSuper(args);
await hre.run(TASK_GAS_REPORTER_STOP, args);
}
);
// ========================
// GAS REPORTER TASKS
// ========================
/**
* Initializes gas tracking
*/
subtask(TASK_GAS_REPORTER_START).setAction(
async (args: any, hre) => {
const options = hre.config.gasReporter;
if (options.enabled === true) {
// Lazy load all imports to minimize HH startup time
const { getContracts } = await import("./lib/artifacts");
const { Collector } = await import("./lib/collector");
const { warnParallel } = await import("./utils/ui");
// Temporarily skipping when in parallel mode because it crashes and
// unsure how to resolve...
if (args.parallel === true) {
warnParallel();
return;
}
// solidity-coverage disables gas reporter via mocha but that
// no longer works for this version. (No warning necessary)
if ((hre as any).__SOLIDITY_COVERAGE_RUNNING === true) {
return;
}
// Need to compile so we have access to the artifact data.
// This will rerun in TASK_TEST & TASK_RUN but should be a noop there.
if (!args.noCompile) {
await hre.run(TASK_COMPILE, { quiet: true });
}
const contracts = await getContracts(hre, options);
hre.__hhgrec.usingCall = options.reportPureAndViewMethods;
hre.__hhgrec.usingViem = (hre as any).viem;
hre.__hhgrec.usingOZ = (hre as any).upgrades || (hre as any).defender
hre.__hhgrec.collector = new Collector(hre, options);
hre.__hhgrec.collector.data.initialize(hre.network.provider, contracts);
// Custom proxy resolvers are instantiated in the config,
// OZ proxy resolver instantiated in Resolver constructor called by new Collector()
hre.__hhgrec.methodIgnoreList = (options.proxyResolver)
? options.proxyResolver.ignore()
: [];
await initializeGasReporterProvider(hre.network.provider, hre.__hhgrec);
}
}
);
/**
* Completes gas reporting: gets live market data, runs analysis and renders
*/
subtask(TASK_GAS_REPORTER_STOP).setAction(
async (args: any, hre) => {
const options = hre.config.gasReporter;
if (
options.enabled === true &&
args.parallel !== true &&
(hre as any).__SOLIDITY_COVERAGE_RUNNING !== true
) {
const { setGasAndPriceRates } = await import("./utils/prices");
const { render } = await import("./lib/render");
const warnings = await setGasAndPriceRates(options);
await hre.__hhgrec.collector?.data.runAnalysis(hre, options);
render(hre, options, warnings);
}
}
);
/**
* ========================
* CLI COMMAND TASKS
* ========================
*/
subtask(TASK_GAS_REPORTER_MERGE_REPORTS)
.addOptionalVariadicPositionalParam(
"inputFiles",
"Path of several gasReporterOutput.json files to merge",
[]
)
.setAction(
async ({ inputFiles }: { inputFiles: string[] }
): Promise<GasReporterOutput> => {
const { subtaskMergeReportsImplementation } = await import("./tasks/mergeReports")
return subtaskMergeReportsImplementation({ inputFiles })
});
task(TASK_GAS_REPORTER_MERGE)
.addOptionalParam(
"output",
"Target file to save the merged report",
"gasReporterOutput.json"
)
.addVariadicPositionalParam(
"input",
"A list of JSON data files generated by the gas reporter plugin. " +
"Files can be defined using glob patterns"
)
.setAction(async (taskArguments, hre) => {
const { taskMergeImplementation } = await import("./tasks/mergeReports")
return taskMergeImplementation(taskArguments, hre);
});
/**
* ========================
* DEPRECATED TASKS
* ========================
*/
task(TASK_GAS_REPORTER_MERGE_LEGACY)
.addOptionalParam(
"output",
"Target file to save the merged report",
"gasReporterOutput.json"
)
.addVariadicPositionalParam("input")
.setAction(async () => {
const { warnDeprecatedTask } = await import("./utils/ui");
warnDeprecatedTask(TASK_GAS_REPORTER_MERGE)
});
subtask(TASK_GAS_REPORTER_MERGE_REPORTS_LEGACY)
.addOptionalVariadicPositionalParam("inputFiles", "", [])
.setAction(async ({}: { inputFiles: string[] }) => {
const { warnDeprecatedTask } = await import("./utils/ui");
warnDeprecatedTask(TASK_GAS_REPORTER_MERGE_REPORTS);
});