Create your own
Lesson illustration

Local Blockchain Setup with Mainnet Forking

Hello! Welcome to your fourth lesson in Module 2: "Modern Development & Testing Toolchain."

Introduction

In our last lesson, we compared the core philosophies of Hardhat and Foundry, highlighting how Hardhat leverages the JavaScript ecosystem while Foundry champions a Solidity-native approach. We saw that regardless of the framework, all testing and local development happens on a simulated blockchain node.

Today, we'll get hands-on with these nodes. We'll explore Hardhat Network and Foundry's Anvil, the engines that power your local development. More importantly, we'll unlock one of the most powerful features in the modern web3 developer's toolkit: mainnet forking. This technique allows you to test your smart contracts against the live, complex state of the Ethereum mainnet, all from the safety and speed of your local machine.

Learning Outcome: By the end of this lesson, you will be able to set up local blockchain nodes using Anvil or Hardhat Network with mainnet forking capabilities.

1. Local Blockchain Nodes: Your Personal EVM

Before we can fork mainnet, let's clarify what these local nodes are. Think of them as a complete, private Ethereum blockchain running entirely on your computer.

  • Hardhat Network: The integrated local node that comes with any Hardhat project. It's designed to be the default environment for running tests and scripts.
  • Anvil: Foundry's equivalent. It's a separate command-line tool, written in Rust, renowned for its speed.

While they are part of different toolchains, they both expose a standard JSON-RPC interface (usually at http://127.0.0.1:8545). This makes them largely interchangeable. For instance, you could use the faster Anvil node for a Hardhat project if you wished.

Let's get a quick introduction to Anvil and how it compares to Hardhat Network.

Solidity Development with Foundry: Cast, Anvil, Chisel, and Forge

This clip from the 'Solidity Development with Foundry' video by the Ethereum Engineering Group introduces Anvil and its place in the Foundry ecosystem.

Please watch from 11:05 to 12:11. The speaker explains that Anvil is essentially a faster version of the Hardhat node, with the same core features, including the ability to fork mainnet.

As the video mentions, both nodes provide you with pre-funded accounts, instant transaction mining, and the crucial ability to fork other networks.

2. Mainnet Forking: Testing Against Reality

Mainnet forking is the process of creating a local development environment that simulates the exact state of the Ethereum mainnet at a specific point in time. This means you have a local copy of every deployed contract, every account balance, and every piece of storage from the real blockchain.

Why is this a game-changer?

  • Integration Testing: You can test how your new contract interacts with established DeFi protocols like Uniswap, Aave, or Curve without deploying to a testnet or spending real ETH.
  • Realistic State: Some protocols are so complex or lack a maintained testnet presence that forking is the only practical way to test against them.
  • Debugging: You can reproduce a transaction that happened on mainnet locally to debug exactly what went wrong.

To achieve this, your local node needs to communicate with a mainnet node that has access to archive data—the complete history of the blockchain. Services like Alchemy, Infura, or QuickNode provide RPC endpoints for this purpose.

3. Forking Mainnet with Hardhat

Let's walk through setting up a mainnet fork using Hardhat. This is the most common starting point, especially given your background in the JS/TS ecosystem.

Configuration

You can enable forking in two ways: directly from the command line or, more permanently, in your hardhat.config.ts file. We'll focus on the configuration file method as it's better for reproducible setups.

Forking other networks | Ethereum development ...

The official Hardhat documentation provides the definitive guide on how to configure forking. We'll look at the core configuration options.

Please read the sections 'Forking from mainnet' and 'Pinning a block'. In 'Forking from mainnet', focus on the code snippet showing how to add the forking object to your hardhat.config.js. In 'Pinning a block', understand why adding a blockNumber is critical for both reproducibility and performance.

As the documentation explains, your hardhat.config.ts would look something like this:

// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";

dotenv.config();

const ALCHEMY_MAINNET_URL = process.env.ALCHEMY_MAINNET_URL || "";

const config: HardhatUserConfig = {
  solidity: "0.8.24",
  networks: {
    hardhat: { // This is the configuration for the Hardhat Network
      forking: {
        url: ALCHEMY_MAINNET_URL,
        blockNumber: 19_000_000 // Pinned to a specific block for consistency
      }
    }
  }
};

export default config;

Key Points:

  • url: This is your RPC endpoint from a service like Alchemy. It's best practice to store this in an environment file (.env) and not commit it to version control.
  • blockNumber: Pinning the fork to a specific block number ensures your tests always run against the exact same state, making them deterministic. It also allows Hardhat to cache data aggressively, dramatically speeding up subsequent test runs.

Seeing it in Action

Now, let's watch a video that puts all these pieces together, from configuration to interacting with a real mainnet contract on our local fork.

Fork Ethereum Mainnet Locally with Hardhat | Call Contract Methods on Wrapped Ether and CurveFi

This video, 'Fork Ethereum Mainnet Locally with Hardhat' by Blockman Codes, provides a complete, practical walkthrough.

Please watch from 01:11 to 06:36. This extended clip covers: Configuration (01:11): Modifying hardhat.config.js to add the forking URL. Starting & Verifying (02:17): Running npx hardhat node and checking that the local block number matches the mainnet block number. Interaction (03:37): Writing a simple script with ethers.js to connect to the local fork and call a read-only function (name()) on the deployed WETH contract.

This demonstration shows the complete workflow:

  1. Configure hardhat.config.ts.
  2. Run npx hardhat node in a terminal. This starts the local node, which will now be a fork of mainnet.
  3. In another terminal, run your scripts or tests (e.g., npx hardhat run scripts/my-script.ts), which will automatically connect to this local forked instance.

4. Forking Mainnet with Anvil (Foundry)

The concept for forking with Anvil is identical, but the execution is done via command-line flags instead of a config file. If you have Foundry installed, you already have Anvil.

To start a forked Anvil node, you simply run:

# Start Anvil, forking from the latest block
anvil --fork-url <YOUR_ALCHEMY_URL>

# For a reproducible setup, pin the block number
anvil --fork-url <YOUR_ALCHEMY_URL> --fork-block-number 19000000

Anvil will start up and listen on 127.0.0.1:8545, just like Hardhat Network. You can then point your tests or scripts to this endpoint. This command-line approach is consistent with Foundry's philosophy of using simple, powerful CLI tools.

5. Advanced Technique: Impersonating Accounts

One of the most powerful capabilities that forking enables is account impersonation. This allows you to send transactions as if you were any address on the network, without needing its private key.

This is incredibly useful for testing scenarios like:

  • Executing a function that can only be called by a protocol's owner.
  • Testing a swap with an account that you know is a large liquidity provider (a "whale").
  • Checking logic that depends on a user holding a specific NFT.

The Hardhat documentation shows how simple this is with the hardhat-ethers plugin.

Forking other networks | Ethereum development ...

Let's briefly look at the Hardhat documentation on how to impersonate an account. This is a feature you will use frequently when writing integration tests.

Read the short section 'Impersonating accounts'. Focus on the ethers.getImpersonatedSigner method. This is a helper function that makes impersonation a one-liner.

As you saw, you can get a signer for any address with a single line of code:

// In a Hardhat script or test
const whaleAddress = "0x..."; // Address of a large token holder
const whaleSigner = await ethers.getImpersonatedSigner(whaleAddress);

// Now you can use whaleSigner to sign and send transactions
await someToken.connect(whaleSigner).approve(myContract.address, amount);

This technique is fundamental for writing realistic and robust integration tests, which we will cover in later lessons.

Conclusion

Today you've learned how to set up and configure the local blockchain nodes that are central to modern Ethereum development. You've seen how both Hardhat Network and Anvil can be used to create a mainnet fork, a powerful technique for testing against real-world conditions.

Key Takeaways:

  • Local Nodes: Hardhat Network and Anvil are your personal EVM environments for development and testing.
  • Mainnet Forking: This is the process of simulating the mainnet state locally. It is essential for testing integrations with existing protocols.
  • Configuration: Forking is enabled in hardhat.config.ts for Hardhat and via CLI flags (--fork-url) for Anvil.
  • Pinning Blocks: Always specify a blockNumber when forking to ensure your tests are reproducible and benefit from caching.
  • Impersonation: Forking allows you to send transactions from any account, enabling powerful testing scenarios.

Next Steps:

Now that you can set up a sophisticated local testing environment, it's time to start writing tests. In our next lesson, we will begin writing unit tests for a contract's "happy path," ensuring its core functions behave as expected under normal conditions. This will be our first step in building a comprehensive and reliable test suite.

Can't find a good explanation? Sign up and we'll make it for you

Sign up