Using Hardhat Network Forking to Debug Production Contracts
Hardhat’s network forking lets developers spin up a local node that mirrors a live blockchain snapshot. This article walks through configuring, running, and verifying a forked network, and discusses trade‑offs and best practices.
10 Jul 2026, 16:26 UTC

Why Forking Matters
When you’re ready to ship a smart contract, the last thing you want is a surprise from the live network. Hardhat’s network forking feature lets you spin up a local node that mirrors the state of a public blockchain at a chosen block. You can then run tests, scripts, and the console against that snapshot, all while keeping your real funds safe.
Configuring a Fork
Forking is enabled by adding a forking block to hardhat.config.js. The minimal configuration looks like this:
require('@nomicfoundation/hardhat-toolbox');
module.exports = {
solidity: '0.8.20',
networks: {
localhost: {
url: 'http://127.0.0.1:8545',
forking: {
url: 'https://eth-mainnet.alchemyapi.io/v2/your-api-key',
blockNumber: 18_000_000, // optional
},
},
},
};
Replace your-api-key with a key from a provider such as Alchemy, Infura, or your own node. The blockNumber field is optional; if omitted, Hardhat will fork from the latest block.
Running the Forked Node
- Open a terminal and navigate to your project directory.
- Start the node:
npx hardhat node --network localhost. - The console will print a line like
Forked from https://eth-mainnet.alchemyapi.io/v2/… at block 18,000,000. This confirms the snapshot was loaded.
Using the Fork in Tests and Scripts
Once the node is running, you can target it from a separate terminal. For example, to check the balance of the DAI contract on the forked mainnet:
npx hardhat console --network localhost
> const dai = await ethers.getContractAt('IERC20', '0x6B175474E89094C44Da98b954EedeAC495271d0F');
> const balance = await dai.balanceOf('0xYourAddress');
> console.log(balance.toString());
Replace 0xYourAddress with any address you want to inspect. The returned balance should match the value on the real chain at block 18,000,000.
Running Tests Against the Fork
In your hardhat.config.js, the default network is localhost, so running npx hardhat test will automatically use the forked node. Your test suite can interact with real contracts, query balances, and even send transactions—all on the local snapshot.
Fine‑Grained Control with CLI Flags
If you prefer not to hard‑code the fork in the config, you can pass CLI arguments:
npx hardhat node --fork https://eth-mainnet.alchemyapi.io/v2/your-api-key --fork-block-number 18_000_000
Or for a one‑off test run:
npx hardhat test --network localhost --fork https://eth-mainnet.alchemyapi.io/v2/your-api-key --fork-block-number 18_000_000
Trade‑Offs and Limitations
- Static Snapshot: The forked state is a snapshot. If the remote chain moves forward after the fork, your local node won’t reflect those changes until you restart the fork.
- Provider Reliability: Forking depends on a stable RPC endpoint. Rate limits or outages will prevent the node from starting or cause incomplete state loading.
- Gas Calculations: Hardhat uses its own block parameters (e.g., block gas limit). Gas estimates may differ slightly from the live chain, so performance benchmarks should be interpreted cautiously.
- Storage Size: Forking the mainnet can consume several gigabytes of disk space, especially if you keep the node running for extended periods.
Practical Verification Checklist
- Node Startup: Verify the console shows
Forked from … at block …. - Contract Interaction: Query a known contract (e.g., DAI) and compare the result to a block explorer snapshot at the same block.
- Transaction Test: Deploy a simple contract that reads the DAI balance of an address, run it on the fork, and ensure it returns the expected value.
- Refresh Fork: If you need a newer state, stop the node and restart with a higher
blockNumberor without specifying a block to fork from the latest.
When to Use Forking
- Debugging a contract that interacts with live contracts (e.g., Uniswap, Aave).
- Testing edge cases that involve real token balances or on‑chain data.
- Benchmarking gas usage before a mainnet deployment.
- Validating that a migration script behaves as expected against the current chain state.
Actionable Take‑Away
1. Add a forking block to your hardhat.config.js.
2. Start the node and confirm the fork message.
3. Run a console or test that queries a known contract to verify the snapshot.
4. Refresh the fork when you need a newer state or if the provider changes.
5. Use the forked network for all local development to catch issues that only surface against real on‑chain data.
With these steps, you can confidently iterate on contracts that depend on the live Ethereum state, all while keeping your deployments safe and deterministic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.