Gas statistics for your test runs
Hardhat can optionally show statistics on the gas consumed by your contracts’ public functions during a test run. Use the --gas-stats flag when running your tests to display this information.
Getting gas statistics from your tests
Section titled “Getting gas statistics from your tests”You can pass the --gas-stats flag to either the test task or to one of its subtasks (e.g., test solidity):
npx hardhat test --gas-statsnpx hardhat test solidity --gas-statsnpx hardhat test nodejs --gas-statspnpm hardhat test --gas-statspnpm hardhat test solidity --gas-statspnpm hardhat test nodejs --gas-statsyarn hardhat test --gas-statsyarn hardhat test solidity --gas-statsyarn hardhat test nodejs --gas-statsThis prints a table like the following:
╔═══════════════════════════════════════════════════════════════════════════════════════╗║ Gas Usage Statistics ║╚═══════════════════════════════════════════════════════════════════════════════════════╝╔═══════════════════════════════════════════════════════════════════════════════════════╗║ contracts/Calculator.sol:Calculator ║╟───────────────────────────────────┬────────┬─────────┬────────┬────────┬──────────────╢║ Function name │ Min │ Average │ Median │ Max │ #calls ║╟───────────────────────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ divide │ 44316 │ 44316 │ 44316 │ 44316 │ 1 ║║ multiply(uint256,uint256) │ 44254 │ 44254 │ 44254 │ 44254 │ 2 ║║ multiply(uint256,uint256,uint256) │ 44875 │ 44875 │ 44875 │ 44875 │ 1 ║║ reset │ 21485 │ 21485 │ 21485 │ 21485 │ 1 ║║ result │ 23510 │ 23510 │ 23510 │ 23510 │ 6 ║║ subtract │ 44213 │ 44213 │ 44213 │ 44213 │ 1 ║╟───────────────────────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ Deployment │ Min │ Average │ Median │ Max │ #deployments ║╟───────────────────────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ │ 288115 │ 288115 │ 288115 │ 288115 │ 1 ║╟───────────────────────────────────┼────────┼─────────┴────────┴────────┴──────────────╢║ Bytecode size │ 2294 │ ║╚═══════════════════════════════════╧════════╧══════════════════════════════════════════╝╔═══════════════════════════════════════════════════════════════════════════════════════╗║ contracts/Counter.sol:Counter ║╟───────────────────────────────────┬────────┬─────────┬────────┬────────┬──────────────╢║ Function name │ Min │ Average │ Median │ Max │ #calls ║╟───────────────────────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ add(uint256) │ 43915 │ 43915 │ 43915 │ 43915 │ 1 ║║ add(uint256,bool) │ 44284 │ 44419 │ 44419 │ 44554 │ 2 ║║ inc │ 43482 │ 43482 │ 43482 │ 43482 │ 1 ║║ x │ 23466 │ 23466 │ 23466 │ 23466 │ 5 ║╟───────────────────────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ Deployment │ Min │ Average │ Median │ Max │ #deployments ║╟───────────────────────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ │ 234940 │ 234940 │ 234940 │ 234940 │ 1 ║╟───────────────────────────────────┼────────┼─────────┴────────┴────────┴──────────────╢║ Bytecode size │ 1462 │ ║╚═══════════════════════════════════╧════════╧══════════════════════════════════════════╝The statistics are collected from the functions called by the tests you executed. This means that running test solidity --gas-stats will produce a different result than running test nodejs --gas-stats, because different tests will have been run.
Exporting gas statistics to a JSON file
Section titled “Exporting gas statistics to a JSON file”To save gas statistics to a JSON file, pass the --gas-stats-json <path> flag:
npx hardhat test --gas-stats-json gas-stats.jsonnpx hardhat test solidity --gas-stats-json gas-stats.jsonnpx hardhat test nodejs --gas-stats-json gas-stats.jsonpnpm hardhat test --gas-stats-json gas-stats.jsonpnpm hardhat test solidity --gas-stats-json gas-stats.jsonpnpm hardhat test nodejs --gas-stats-json gas-stats.jsonyarn hardhat test --gas-stats-json gas-stats.jsonyarn hardhat test solidity --gas-stats-json gas-stats.jsonyarn hardhat test nodejs --gas-stats-json gas-stats.jsonThis flag works independently of --gas-stats, so either or both can be used together. For example, to print the table to the terminal and also save the results to a file:
npx hardhat test --gas-stats --gas-stats-json gas-stats.jsonpnpm hardhat test --gas-stats --gas-stats-json gas-stats.jsonyarn hardhat test --gas-stats --gas-stats-json gas-stats.jsonThe JSON file has the following structure:
{ "contracts": { "contracts/Calculator.sol:Calculator": { "sourceName": "contracts/Calculator.sol", "contractName": "Calculator", "proxyChain": [], "deployment": { "min": 288115, "max": 288115, "avg": 288115, "median": 288115, "count": 1, "runtimeSize": 2294 }, "functions": { "divide": { "min": 44316, "max": 44316, "avg": 44316, "median": 44316, "count": 1 }, "multiply(uint256,uint256)": { "min": 44254, "max": 44254, "avg": 44254, "median": 44254, "count": 2 } // ... } }, "contracts/Counter.sol:Counter": { "sourceName": "contracts/Counter.sol", "contractName": "Counter", "proxyChain": [], "deployment": { "min": 234940, "max": 234940, "avg": 234940, "median": 234940, "count": 1, "runtimeSize": 1462 }, "functions": { "add(uint256)": { "min": 43915, "max": 43915, "avg": 43915, "median": 43915, "count": 1 }, "add(uint256,bool)": { "min": 44284, "max": 44554, "avg": 44419, "median": 44419, "count": 2 } // ... } }, "contracts/Counter.sol:Counter (via contracts/Proxy.sol:Proxy)": { "sourceName": "contracts/Counter.sol", "contractName": "Counter", "proxyChain": [ "contracts/Proxy.sol:Proxy", "contracts/Counter.sol:Counter" ], "deployment": null, "functions": { "add(uint256)": { "min": 45100, "max": 45100, "avg": 45100, "median": 45100, "count": 1 } // ... } } }}Understanding the JSON structure
Section titled “Understanding the JSON structure”Each key in the contracts object is a display label. For direct calls, the label is sourceName:contractName (e.g. contracts/Counter.sol:Counter). For proxied calls, it is suffixed with the proxy chain: contracts/Counter.sol:Counter (via contracts/Proxy.sol:Proxy). These labels are meant for display; for programmatic use, rely on sourceName, contractName, and proxyChain instead.
Each contract entry contains:
sourceNameandcontractName: the source file path and the contract name of the implementation contract.proxyChain: an empty array[]for direct calls. For proxied calls, it lists each contract the call passed through, with proxies first and the implementation contract last.deployment: gas statistics for deploying the contract, includingruntimeSize(the deployed bytecode size in bytes). This isnullwhen the contract was not directly deployed during the test run.functions: a map of function signatures to their gas statistics, ornullif no functions were called.
When a contract is called both directly and through a proxy, it appears as separate entries, one without a proxy suffix (for the direct call) and one with it (for the proxied call).
Understanding the gas statistics
Section titled “Understanding the gas statistics”The gas statistics table shows the following information for each function and deployment:
- count: Number of times the function was called or the contract was deployed
- min: Minimum gas consumed in a single call
- max: Maximum gas consumed in a single call
- avg: Average gas consumed across all calls
- median: Median gas consumed across all calls
The deployment section also includes a Bytecode size row showing the contract’s runtime bytecode size in bytes. This is the size of the code stored on-chain after deployment.
Functions included in gas statistics
Section titled “Functions included in gas statistics”Gas statistics only include public functions that are called directly by your tests. If a public function is called by another function but not directly by a test, it won’t be included in the statistics.
For example, consider this contract:
// SPDX-License-Identifier: MITpragma solidity ^0.8.0;
contract Counter { uint256 public count;
function inc() public { _incInternal(); }
function _incInternal() private { count++; }
function incBy(uint256 value) public { count += value; }
function reset() public { count = 0; }}And this test:
// SPDX-License-Identifier: MITpragma solidity ^0.8.0;
import "./Counter.sol";
contract CounterTest { Counter counter;
function setUp() public { counter = new Counter(); }
function testInc() public { counter.inc(); }
function testIncBy() public { counter.incBy(5); }}The output will be:
╔═════════════════════════════════════════════════════════════════════╗║ Gas Usage Statistics ║╚═════════════════════════════════════════════════════════════════════╝╔═════════════════════════════════════════════════════════════════════╗║ contracts/Counter.sol:Counter ║╟─────────────────┬────────┬─────────┬────────┬────────┬──────────────╢║ Function name │ Min │ Average │ Median │ Max │ #calls ║╟─────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ inc │ 43484 │ 43484 │ 43484 │ 43484 │ 1 ║║ incBy │ 43937 │ 43937 │ 43937 │ 43937 │ 1 ║╟─────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ Deployment │ Min │ Average │ Median │ Max │ #deployments ║╟─────────────────┼────────┼─────────┼────────┼────────┼──────────────╢║ │ 179915 │ 179915 │ 179915 │ 179915 │ 1 ║╟─────────────────┼────────┼─────────┴────────┴────────┴──────────────╢║ Bytecode size │ 1462 │ ║╚═════════════════╧════════╧══════════════════════════════════════════╝The statistics include inc and incBy because they’re called directly by the tests. The reset() function doesn’t appear because it’s never called by the tests. The _incInternal() function doesn’t appear because it’s private and only called by inc(), not directly by the tests.