Testing Against Live State with Hardhat Mainnet Forking
Learn how to use Hardhat's mainnet forking to test smart contracts against real-world state and impersonate accounts without private keys.
25 Aug 2026, 16:47 UTC

The Problem: Testing Against Real-World State
Testing smart contracts in isolation often fails to catch bugs that only emerge when interacting with existing protocols, such as Uniswap pools or Aave lending markets. Deploying a full replica of these protocols to a local network is time-consuming and often impossible due to complex initialization requirements.
The solution is Mainnet Forking. This allows the Hardhat Network to act as a local proxy for a live network. Instead of maintaining a full copy of the blockchain, the local node fetches state data from a remote provider (like Alchemy or Infura) on demand, allowing you to interact with real contracts and balances without spending actual ETH.
Configuring the Fork
To enable forking, you must modify the hardhat.config.js file. The Hardhat Network needs a remote JSON-RPC endpoint to pull state from. This requires a valid API key from a node provider.
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: "0.8.24",
networks: {
hardhat: {
forking: {
url: "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY",
// Optional: Pin to a specific block to ensure deterministic tests
blockNumber: 19000000
}
}
}
};
Running the Node
To start the local node with the fork active, run the following command in your terminal:
npx hardhat node
Permissions: No special system permissions are required beyond standard Node.js execution rights. Ensure your API key is kept secret; using an .env file with dotenv is recommended for production environments.
Practical Example: Impersonating a Whale Account
A common requirement when forking is the need to trigger functions that only specific addresses (like a protocol governor or a large liquidity provider) can call. Since you do not have the private keys for these accounts, you can use Account Impersonation.
This is a Hardhat-specific JSON-RPC method that tells the local node to skip signature verification for a specific address.
const { ethers } = require("hardhat");
async function main() {
const WHALE_ADDRESS = "0x... whale address ...";
// 1. Tell Hardhat to allow transactions from this address
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [WHALE_ADDRESS],
});
// 2. Get a signer for the impersonated account
const signer = await ethers.getSigner(WHALE_ADDRESS);
// 3. Execute a transaction as that account
const tx = await someContract.connect(signer).transfer(target, amount);
await tx.wait();
console.log("Transaction successful using impersonated account");
}
main().catch((error) => { console.error(error); process.exit(1); });
Limitations and Common Pitfalls
- Network Latency: Every time your local node requests a piece of state not already in its cache, it makes an HTTP request to your provider. This makes forked tests significantly slower than pure local tests.
- Volatile State: Any changes you make (deploying new contracts, transferring tokens) exist only in the local node's memory. Once you stop
npx hardhat node, all changes are lost. - Provider Rate Limits: Heavy testing suites can quickly exhaust the request limits of free-tier RPC providers.
- Signature Bypass: Impersonation only works on the Hardhat Network. It does not provide the actual private key, meaning you cannot use these accounts to sign messages for external services or deploy to a real testnet.
Verifying the Fork
To verify that your fork is correctly mirroring the mainnet, you can check the balance of a known address using a simple script:
- Create a script that calls
ethers.provider.getBalance("KNOWN_ADDRESS"). - Run the script using
npx hardhat run script.js --network hardhat. - Compare the output with a block explorer (like Etherscan) for the block number specified in your config.
Rollback Procedure
Because forking happens in a local ephemeral environment, there is no state to "roll back" on the actual blockchain. To reset your local environment to the original mainnet state, simply terminate the npx hardhat node process (Ctrl+C) and restart it.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.