Skip to main content
Hardhat tests are legacy. All new tests should be written in Foundry. This framework is maintained for existing test suites only.

Overview

Hardhat was the original testing framework for Across Protocol. The project is actively migrating to Foundry for better performance and developer experience.

Why Migrate from Hardhat?

  • Slower execution: JavaScript/TypeScript overhead vs native EVM execution
  • Complex setup: Requires compilation, typechain generation, and multiple dependencies
  • Less gas-efficient: No built-in gas profiling like Foundry
  • Harder debugging: Less granular trace information

Migration Status

  • New tests: Must be written in Foundry (see Foundry Tests)
  • Existing tests: Maintained in Hardhat until migration is complete
  • CI: Both Foundry and Hardhat tests run in continuous integration

Running Hardhat Tests

Basic Commands

# Run all Hardhat tests
yarn test-evm-hardhat

# Run with gas reporting
yarn test:report-gas

# Run directly with hardhat
IS_TEST=true hardhat test

# Run with gas reporter enabled
IS_TEST=true REPORT_GAS=true hardhat test

Environment Variables

VariableDescriptionDefault
IS_TESTRequired for test buildstrue (set in scripts)
REPORT_GASEnable hardhat-gas-reporterfalse
NODE_URL_1Ethereum mainnet RPC (for forks)-

Test Structure

Directory Layout

test/evm/hardhat/
  constants.ts       # Shared test constants
  MerkleLib.utils.ts # Utility functions for tests
Most legacy Hardhat tests have been migrated. Remaining files are utilities and constants.

Test File Pattern (Historical)

import { expect } from "chai";
import { ethers } from "hardhat";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";

let owner: SignerWithAddress;
let user: SignerWithAddress;

describe("MyContract", function () {
  beforeEach(async function () {
    [owner, user] = await ethers.getSigners();
    // Deploy contracts
  });

  it("should handle deposits", async function () {
    await myContract.connect(user).deposit(amount);
    expect(await myContract.balanceOf(user.address)).to.equal(amount);
  });
});

Configuration

hardhat.config.ts

Hardhat configuration includes:
  • Solidity compiler: Version 0.8.30 with optimizations
  • Networks: Mainnet, Arbitrum, Optimism, Base, etc.
  • Plugins:
    • @nomiclabs/hardhat-ethers - Ethers.js integration
    • @nomiclabs/hardhat-waffle - Waffle testing
    • hardhat-gas-reporter - Gas usage analysis
    • @typechain/hardhat - TypeScript contract types

TypeChain

Hardhat automatically generates TypeScript types for contracts:
# Generate typechain types
yarn generate-evm-artifacts

# Types are output to:
typechain/

Gas Reporting

Hardhat includes gas reporting via hardhat-gas-reporter:
# Run tests with gas reporting
yarn test:report-gas
Output shows:
  • Gas used per function call
  • Average gas consumption
  • Comparison against previous runs (if configured)

Sample Output

·----------------------------------------|---------------------------|-------------|----------------------------·
|  Solc version: 0.8.30                  ·  Optimizer enabled: true  ·  Runs: 800  ·  Block limit: 30000000 gas  │
·········································|···························|·············|·····························
|  Methods                                                                                                        │
··················|······················|·············|·············|·············|··············|···············
|  Contract       ·  Method              ·  Min        ·  Max        ·  Avg        ·  # calls     ·  usd (avg)   │
··················|······················|·············|·············|·············|··············|···············
|  SpokePool      ·  deposit             ·      95000  ·     120000  ·     107500  ·          15  ·       -      │
·····················································|·············|·············|··············|···············

Common Patterns

Mock Contracts with Smock

Hardhat tests use @defi-wonderland/smock for mocking:
import { smock, FakeContract } from "@defi-wonderland/smock";

let mockToken: FakeContract<IERC20>;

beforeEach(async function () {
  mockToken = await smock.fake<IERC20>("IERC20");
  mockToken.balanceOf.returns(ethers.utils.parseEther("100"));
});
In Foundry, use vm.mockCall instead (see Foundry Tests for examples).

Time Manipulation

import { ethers } from "hardhat";

// Increase time by 1 hour
await ethers.provider.send("evm_increaseTime", [3600]);
await ethers.provider.send("evm_mine", []);

// Set specific timestamp
await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp]);
await ethers.provider.send("evm_mine", []);
Foundry equivalent:
vm.warp(block.timestamp + 3600);  // Increase by 1 hour
vm.warp(timestamp);                // Set specific timestamp

Event Testing

import { expect } from "chai";

await expect(myContract.deposit(amount))
  .to.emit(myContract, "Deposited")
  .withArgs(user.address, amount);
Foundry equivalent:
vm.expectEmit(address(myContract));
emit Deposited(user, amount);
myContract.deposit(amount);

Differences from Foundry

FeatureHardhatFoundry
LanguageTypeScript/JavaScriptSolidity
ExecutionEVM via ethers.jsNative EVM (fast)
MockingSmock libraryvm.mockCall
Time travelevm_increaseTimevm.warp
EventsChai matchersvm.expectEmit
FuzzingManual with loopsBuilt-in fuzzing
Gas profilinghardhat-gas-reporterforge snapshot
PerformanceSlower (~minutes)Faster (~seconds)

When to Use Hardhat

Only use Hardhat for:
  • Maintaining existing test suites
  • Integration tests with complex JavaScript logic
  • Tests that require npm packages not available in Solidity
Do NOT use Hardhat for:
  • New unit tests (use Foundry)
  • Gas-sensitive tests (use Foundry)
  • Simple contract interactions (use Foundry)

Migration Guide

To migrate a Hardhat test to Foundry:
  1. Convert setup from beforeEach to setUp()
  2. Replace ethers.js calls with native Solidity
  3. Convert mocks from Smock to vm.mockCall
  4. Update assertions from Chai to Forge assertions
  5. Move file to test/evm/foundry/local/

Example Migration

Before (Hardhat):
import { expect } from "chai";

describe("HubPool", function () {
  beforeEach(async function () {
    [owner] = await ethers.getSigners();
    hubPool = await ethers.getContract("HubPool");
  });

  it("should enable token", async function () {
    await hubPool.enableL1TokenForLiquidityProvision(token.address);
    const pooledToken = await hubPool.pooledTokens(token.address);
    expect(pooledToken.isEnabled).to.be.true;
  });
});
After (Foundry):
import { Test } from "forge-std/Test.sol";

contract HubPool_AdminTest is Test {
    HubPool hubPool;
    address owner;
    address token;

    function setUp() public {
        owner = makeAddr("owner");
        vm.prank(owner);
        hubPool = new HubPool(...);
        token = makeAddr("token");
    }

    function test_EnableToken() public {
        hubPool.enableL1TokenForLiquidityProvision(token);
        (, bool isEnabled, , , , ) = hubPool.pooledTokens(token);
        assertTrue(isEnabled);
    }
}

Build Commands

Compile Contracts

# Compile all contracts
yarn build-hardhat

# Clean build artifacts
yarn clean

# Fast clean (moves dirs to background delete)
yarn clean-fast

Generate Artifacts

# Generate TypeChain types
yarn generate-evm-artifacts

# Export deployment addresses
yarn process-hardhat-export

Troubleshooting

”Cannot find module” errors

# Regenerate typechain
yarn generate-evm-artifacts

Out of memory

# Increase Node.js memory limit
export NODE_OPTIONS="--max-old-space-size=4096"
yarn test-evm-hardhat

Slow test execution

# Run specific test file
npx hardhat test test/evm/hardhat/MyTest.ts

# Or consider migrating to Foundry

Available Scripts

From package.json:
ScriptDescription
yarn build-hardhatCompile contracts with Hardhat
yarn test-evm-hardhatRun Hardhat tests
yarn test:report-gasRun tests with gas reporting
yarn generate-evm-artifactsGenerate TypeChain types
yarn process-hardhat-exportExport deployment addresses

Next Steps

Migrate to Foundry for all new tests. See the Foundry Tests guide.

Foundry Tests

Learn the modern testing framework

Testing Overview

Return to testing overview

Build docs developers (and LLMs) love