Skip to main content
In this tutorial, you’ll fork Hedera testnet using Hardhat and interact with a basic ERC-20 token on the forked network. This is an introductory guide to local fork testing with Hardhat using TypeScript. This guide shows how to:
  • Fork Hedera testnet using Hardhat
  • Deploy an ERC-20 contract to Hedera testnet
  • Run Hardhat tests on a fork of Hedera testnet
  • Read and interact with an existing ERC-20 contract by its EVM address (e.g., balanceOf, name, symbol, transfer), with minimal setup
  • The process to set up and run tests is similar for mainnet as well
References:
For a deeper understanding of how Hedera forking works and its limitations, see Forking Hedera Network for Local Testing.
You can take a look at the complete code in the basic-erc20-fork-test-hardhat repository.

Prerequisites

  • Node.js (v18 or later) and npm
  • ECDSA account from the Hedera Portal
  • Basic understanding of Solidity and TypeScript
  • A Hedera JSON-RPC endpoint:
    • mainnet: https://mainnet.hashio.io/api
    • testnet: https://testnet.hashio.io/api

Table of Contents

  1. Step 1: Project Setup
  2. Step 2: Create the ERC-20 Contract and Deploy to Testnet
  3. Step 3: Write Tests for the Forked Network
  4. Step 4: Run Tests on the Forked Network

Step 1: Project Setup

Initialize Project

Create a new directory and initialize the project:

Install Dependencies

Create or update your package.json with all required dependencies:
package.json
Then install all dependencies:
Why these specific versions?The @hashgraph/system-contracts-forking plugin requires Hardhat 2.22.x. Newer versions of Hardhat (2.28+) introduced breaking changes that cause a No known hardfork for execution error when forking Hedera networks.
  • hardhat@2.22.19 - Last compatible version before breaking changes
  • @nomicfoundation/hardhat-toolbox@5.0.0 - Compatible with Hardhat 2.22.x
  • @hashgraph/system-contracts-forking@0.1.2 - The Hedera forking plugin
  • --legacy-peer-deps - Required to resolve dependency conflicts between these versions
Verify Hardhat is installed correctly:

Create Project Structure

Create the necessary directories:

Configure TypeScript

Create tsconfig.json in your project root:
tsconfig.json

Configure Hardhat

Create hardhat.config.ts in your project root. This file must exist before you can run any Hardhat commands:
hardhat.config.ts
Important configuration notes:
  • HEDERA_RPC_URL - Loaded from Hardhat configuration variables
  • HEDERA_PRIVATE_KEY - Loaded securely from configuration variables
  • hederaTestnet - Network configuration for deploying to real testnet
  • hardhat.forking - Configuration for forking testnet locally
  • blockNumber - Pin to a block where your deployed contract exists
  • chainId: 296 - Required for testnet (295 for mainnet)
  • workerPort: 10001 - Any free port for the worker that intercepts Hardhat calls
  • @ts-ignore - Required because chainId and workerPort are custom properties not in Hardhat’s type definitions

Set Configuration Variables

Now that hardhat.config.ts exists, you can set the configuration variables. Hardhat allows you to securely store sensitive values using configuration variables:
When prompted, enter: https://testnet.hashio.io/api
When prompted, enter the HEX Encoded Private Key for your ECDSA account from the Hedera Portal.
Make sure your ECDSA account exists on testnet and has sufficient HBAR for deployment. You can fund your testnet account using the Hedera Portal.
You can verify your variables are set correctly:

Step 2: Create the ERC-20 Contract and Deploy to Testnet

Create the Contract

Create a new file contracts/ERC20Token.sol:
contracts/ERC20Token.sol
This contract:
  • Creates a basic ERC-20 token named “MyToken” with symbol “MTK”
  • Mints 10,000 tokens to a recipient on deployment
  • Has an onlyOwner mint function for additional minting

Compile the Contract

This will also generate TypeScript types in the typechain-types directory.

Create Deployment Script

Create a new file scripts/deploy.ts:
scripts/deploy.ts

Deploy to Testnet

Deploy your contract to Hedera testnet:
You should see output similar to:
Save the deployed contract address and block number! You’ll need these for your fork tests. The contract must exist at the block you’re forking from.

Update Hardhat Config with Deployment Block

After deployment, update your hardhat.config.ts with the block number from the deployment output:
We have already deployed this ERC-20 contract on testnet at 0xea606E2D68Ff9F211756b8cfd9026a7Eb76845C9 so we will be using this for the remainder of this exercise.

Step 3: Write Tests for the Forked Network

Now we’ll write tests that interact with the already deployed contract on the forked testnet. This is the real power of fork testing - you can test against real deployed contracts without spending gas or affecting the live network. Create a new file test/ERC20Token.test.ts:
Make sure to update the DEPLOYED_CONTRACT constant below with the address of your deployed contract from Step 2.
test/ERC20Token.test.ts
Key points about these tests:
  • TypeScript types - Uses generated types from typechain-types for type safety
  • Uses deployed contract - Tests bind to the already deployed contract using getContractAt
  • Impersonation - Uses hardhat_impersonateAccount to act as the real owner
  • Reads real state - Token info, balances, etc. come from the actual testnet deployment
  • Local modifications - All transfers, mints happen only on the local fork
  • No testnet changes - The real testnet is never modified
  • Uses fixtures - loadFixture ensures each test starts with a clean state

Step 4: Run Tests on the Forked Network

Run your tests against the forked Hedera testnet:
You should see output similar to:

Pin to a Specific Block

For reproducible tests, make sure the blockNumber in your hardhat.config.ts is set to a block where your contract exists. If you try to fork at a block before your contract was deployed, you’ll see an error because the contract doesn’t exist yet at that block.

Understanding Fork Testing with Deployed Contracts

Why Test Against Deployed Contracts?

  1. Real-world state - Test against actual balances, allowances, and state
  2. No deployment costs - Don’t spend gas deploying for every test run
  3. Impersonation - Act as any account (even the contract owner) without their private key
  4. Safe experimentation - Try anything without affecting the real network

How Impersonation Works

Local vs. Remote State

ActionAffects Local ForkAffects Testnet
Read balances✅ (cached)❌ (read-only)
Transfer tokens
Mint new tokens
Deploy new contracts
Impersonate accounts
Changes persist after test❌ (reset)N/A

Next Steps

Now that you understand fork testing with deployed contracts, you can:
  1. Test contract upgrades - Fork, deploy upgraded version, compare behavior
  2. Simulate user interactions - Impersonate real users to test edge cases
  3. Move to Part 2 - Learn how to work with HTS System Contracts
In Part 2, you’ll learn how to interact with the Hedera Token Service (HTS) using system contract precompiles, including interacting with existing HTS tokens and understanding the limitations of the forking emulation layer.

Further Learning & Next Steps

  1. How to Fork Hedera with Hardhat (Part 2)
    Learn to work with HTS System Contracts and understand emulation limitations
  2. Forking Hedera Network for Local Testing
    Deep dive into how Hedera forking works under the hood
  3. How to Fork Hedera with Foundry
    Learn fork testing with Foundry framework
  4. hedera-forking Repository
    Explore examples and documentation

Writer: Kiran Pachhai, Developer Advocate