Skip to content

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.

You can pass the --gas-stats flag to either the test task or to one of its subtasks (e.g., test solidity):

Terminal window
npx hardhat test --gas-stats
npx hardhat test solidity --gas-stats
npx hardhat test nodejs --gas-stats

This 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.

To save gas statistics to a JSON file, pass the --gas-stats-json <path> flag:

Terminal window
npx hardhat test --gas-stats-json gas-stats.json
npx hardhat test solidity --gas-stats-json gas-stats.json
npx hardhat test nodejs --gas-stats-json gas-stats.json

This 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:

Terminal window
npx hardhat test --gas-stats --gas-stats-json gas-stats.json

The JSON file has the following structure:

{
"contracts": {
"contracts/Calculator.sol:Calculator": {
"sourceName": "contracts/Calculator.sol",
"contractName": "Calculator",
"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",
"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
}
// ...
}
}
}
}

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.

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: MIT
pragma 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: MIT
pragma 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.