diff --git a/src/extend.ts b/src/extend.ts deleted file mode 100644 index 1b175b9..0000000 --- a/src/extend.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { cloneDeep } from "lodash" -import { extendConfig, extendEnvironment, extendProvider } from "hardhat/config"; -import { EIP1193Provider, HardhatConfig, HardhatUserConfig } from "hardhat/types"; - -import "./type-extensions"; - -import { getDefaultOptions } from './lib/options'; -import { GasReporterProvider } from "./lib/provider"; -import { GasReporterExecutionContext } from "./types"; - -let _globalGasReporterProviderReference: GasReporterProvider; - - -/* Config */ -extendConfig( - (config: HardhatConfig, userConfig: Readonly) => { - 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 -) { - await (provider as any).init() - _globalGasReporterProviderReference.initializeGasReporterProvider(context); -} diff --git a/src/index.ts b/src/index.ts index e4292b1..5b18f73 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,216 @@ -import "./tasks/mergeReports"; -import "./tasks/gasReporter"; -import "./tasks/builtins"; +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) => { + 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 +) { + await (provider as any).init() + _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, runSuper) => { + 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) { + const result: any = await runSuper(); + warnParallel(); + return result; + } + + // 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) { + 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 => { + 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); +}); + diff --git a/src/tasks/builtins.ts b/src/tasks/builtins.ts deleted file mode 100644 index 739d057..0000000 --- a/src/tasks/builtins.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - TASK_TEST, - // TASK_RUN -} from "hardhat/builtin-tasks/task-names"; - -import { task } from "hardhat/config"; -import { - TASK_GAS_REPORTER_START, - TASK_GAS_REPORTER_STOP -} from "../task-names" - - -/** - * 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); - } -); - -/** - * Overrides Hardhat built-in task TASK_RUN to report gas usage - */ -/* task(TASK_RUN).setAction( - async (args: any, hre, runSuper) => { - hre.__hhgrec.task = TASK_RUN; - await hre.run(TASK_GAS_REPORTER_START, args); - await runSuper(args); - await hre.run(TASK_GAS_REPORTER_STOP, args); - } -);*/ diff --git a/src/tasks/gasReporter.ts b/src/tasks/gasReporter.ts deleted file mode 100644 index 525b808..0000000 --- a/src/tasks/gasReporter.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { subtask } from "hardhat/config"; -import { TASK_COMPILE } from "hardhat/builtin-tasks/task-names"; -import { TASK_GAS_REPORTER_START, TASK_GAS_REPORTER_STOP } from "../task-names"; - -// This has to be a top level import -import { initializeGasReporterProvider} from "../extend"; - -/** - * Initializes gas tracking - */ -subtask(TASK_GAS_REPORTER_START).setAction( - async (args: any, hre, runSuper) => { - 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) { - const result = await runSuper(); - warnParallel(); - return result; - } - - // 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); - } - } -); - -/** - * Initializes gas tracking - */ -subtask(TASK_GAS_REPORTER_STOP).setAction( - async (args: any, hre) => { - const options = hre.config.gasReporter; - - if (options.enabled === true && args.parallel !== 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); - } - } -); diff --git a/src/tasks/mergeReports.ts b/src/tasks/mergeReports.ts index 7114221..a6bdf74 100644 --- a/src/tasks/mergeReports.ts +++ b/src/tasks/mergeReports.ts @@ -1,11 +1,8 @@ -import { subtask, task } from "hardhat/config"; import { HardhatPluginError } from "hardhat/plugins"; +import { HardhatRuntimeEnvironment, TaskArguments } from "hardhat/types"; import { generateJSONData } from "../lib/render/json"; import { - TASK_GAS_REPORTER_MERGE, TASK_GAS_REPORTER_MERGE_REPORTS, - TASK_GAS_REPORTER_MERGE_LEGACY, - TASK_GAS_REPORTER_MERGE_REPORTS_LEGACY } from "../task-names"; import { GasReporterOutput } from "../types"; @@ -87,93 +84,51 @@ export async function mergeReports( * This task is necessary when we want to generate different parts of the reports * parallelized on different jobs. */ +export async function subtaskMergeReportsImplementation( + { inputFiles }: { inputFiles: string[] } +): Promise { + const fs = await import("fs"); + + const reports = inputFiles.map((input) => JSON.parse(fs.readFileSync(input, "utf-8"))); + return mergeReports(reports, inputFiles); +}; + + +export async function taskMergeImplementation( + taskArguments: TaskArguments, + hre: HardhatRuntimeEnvironment +): Promise { + const path = await import("path"); + const { globSync } = await import("glob"); + const { uniq } = await import("lodash"); + const { reportMerge } = await import("../utils/ui"); + const { GasData } = await import("../lib/gasData"); + const { setGasAndPriceRates } = await import("../utils/prices"); + + const output = path.resolve(process.cwd(), taskArguments.output); + + // Parse input files and calculate glob patterns + const taskArgs = uniq(taskArguments.input.map((input: string) => globSync(input)).flat()); + const files = taskArgs.map((file) => path.resolve(file as string)) + + if (files.length === 0) { + throw new HardhatPluginError( + `hardhat-gas-reporter`, + `No files found for the given input: ${taskArguments.input.join(" ")}` + ); + } -subtask(TASK_GAS_REPORTER_MERGE_REPORTS) - .addOptionalVariadicPositionalParam( - "inputFiles", - "Path of several gasReporterOutput.json files to merge", - [] - ) - .setAction( - async ({ inputFiles }: { inputFiles: string[] } - ): Promise => { - const fs = await import("fs"); - - const reports = inputFiles.map((input) => JSON.parse(fs.readFileSync(input, "utf-8"))); - return mergeReports(reports, 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 path = await import("path"); - const { globSync } = await import("glob"); - const { uniq } = await import("lodash"); - const { reportMerge } = await import("../utils/ui"); - const { GasData } = await import("../lib/gasData"); - const { setGasAndPriceRates } = await import("../utils/prices"); - - const output = path.resolve(process.cwd(), taskArguments.output); - - // Parse input files and calculate glob patterns - const taskArgs = uniq(taskArguments.input.map((input: string) => globSync(input)).flat()); - const files = taskArgs.map((file) => path.resolve(file as string)) - - if (files.length === 0) { - throw new HardhatPluginError( - `hardhat-gas-reporter`, - `No files found for the given input: ${taskArguments.input.join(" ")}` - ); - } - - reportMerge(files, output); - - const result = await hre.run(TASK_GAS_REPORTER_MERGE_REPORTS, { inputFiles: files }); - const warnings = await setGasAndPriceRates(result.options); - const data = new GasData(result.data.methods, result.data.deployments); - await data.runAnalysis(hre, result.options); - - // Write warnings - for (const warning of warnings) console.log(warning); - - // Write json - result.options.outputJSONFile = output; - generateJSONData(data, result.options); - }); - - - /** - * ======================== - * DEPRECATED TASK WARNINGS - * ======================== - */ - 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); - }); + reportMerge(files, output); + + const result = await hre.run(TASK_GAS_REPORTER_MERGE_REPORTS, { inputFiles: files }); + const warnings = await setGasAndPriceRates(result.options); + const data = new GasData(result.data.methods, result.data.deployments); + await data.runAnalysis(hre, result.options); + // Write warnings + for (const warning of warnings) console.log(warning); + // Write json + result.options.outputJSONFile = output; + generateJSONData(data, result.options); +};