Migrate from Foundry
Hardhat 3 is a full replacement for Foundry: it runs Solidity tests, understands remappings.txt, and can build contracts laid out the way Foundry expects.
On top of that compatibility, Hardhat 3 brings capabilities Foundry doesn’t:
- An extensible stack — with a plugin system and integration with the TypeScript ecosystem, you can bring your own opinions to deployment, verification and other workflows.
- Dependencies managed with npm — Hardhat supports both git submodules and npm dependencies. You have the option to keep your existing
lib/git submodules, and you can also install dependencies as npm packages to get lockfiles, semver, and native support for transitive dependencies. - Multichain testing — run the same Solidity tests against a simulated Ethereum L1 or OP Stack chain by switching the chain type, so you catch chain-specific behavior before you deploy.
- The right test for the job — write fast Solidity unit tests for contract logic, exactly as you do today, and reach for TypeScript for integration tests (with viem or ethers) when you need to test against a full network, or to build custom scripts in a full-featured language.
This guide will take you, step by step, through migrating a Foundry project to Hardhat 3. We start with a clean Hardhat config, alongside your existing foundry.toml, and migrate one capability at a time.
Before starting the migration
Section titled “Before starting the migration”Before installing Hardhat 3, prepare your project.
-
Check Node.js version
Make sure you have installed Node.js v22.13.0 or later:
Terminal window node --version -
Initialize an npm project
If your repo doesn’t already have a
package.json, create one and set it to ESM:Terminal window npm init -ynpm pkg set type=moduleTerminal window pnpm initpnpm pkg set type=moduleTerminal window yarn init -y# yarn doesn't have a command for type=modulenpm pkg set type=moduleAdd
node_modules/to your.gitignoreif it isn’t there already. -
Keep your Foundry setup for reference
Leave
foundry.toml,remappings.txt, and thelib/directory in place during the migration. Hardhat will pick up yourremappings.txtautomatically, and you can removelib/if you choose to move your dependencies to npm. -
(Optional) Add or adapt your
tsconfig.jsonWe recommend TypeScript over JavaScript for tasks, scripts and deployments. Add or edit your
tsconfig.jsonso thatcompilerOptions.moduleis set to an ESM-compatible value like “node20”. A minimaltsconfig.jsonwould be:tsconfig.json {"compilerOptions": {"lib": ["es2023"],"module": "node20","target": "es2023","skipLibCheck": true,"outDir": "dist","types": ["node"]}}
Setting up Hardhat 3
Section titled “Setting up Hardhat 3”With your npm project ready, you can install Hardhat 3.
-
Install Hardhat 3
Terminal window npm add --save-dev hardhatTerminal window pnpm add --save-dev hardhatTerminal window yarn add --dev hardhat -
Create an empty config file
Create a
hardhat.config.tsfile with the following content:hardhat.config.ts import { defineConfig } from "hardhat/config";export default defineConfig({}); -
Run the help command
Verify that Hardhat 3 is working:
Terminal window npx hardhat --helpTerminal window pnpm hardhat --helpTerminal window yarn hardhat --help
Migrating your Solidity config
Section titled “Migrating your Solidity config”Port the compiler settings and project layout from foundry.toml so Hardhat finds and builds the same files.
-
Add a
solidityentryCopy your
solcversion, optimizer settings,via_irandevm_versionfromfoundry.tomlinto thesolidityentry. For example:hardhat.config.ts import { defineConfig } from "hardhat/config";export default defineConfig({solidity: {version: "0.8.28",settings: {optimizer: {enabled: true,runs: 200,},evmVersion: "cancun",viaIR: false,},},}); -
Point Hardhat at your existing source and test directories
Foundry’s defaults are
src/for contracts andtest/for tests. Hardhat defaults tocontracts/andtest/, so override thesourcespath:hardhat.config.ts import { defineConfig } from "hardhat/config";export default defineConfig({paths: {sources: "./src",},solidity: {/* ... */},}); -
Compile your contracts
Run the
buildtask to verify that the config is working:Terminal window npx hardhat buildTerminal window pnpm hardhat buildTerminal window yarn hardhat build
For the full configuration reference, see the configuration reference.
Migrating dependencies
Section titled “Migrating dependencies”Foundry installs dependencies as git submodules under lib/ and exposes them through remappings.txt. Hardhat supports that approach without changes, but also offers the option to use npm packages as dependencies rather than git submodules.
-
Install npm equivalents of your
lib/dependenciesMost common dependencies are published to npm. For example:
Terminal window npm add --save-dev @openzeppelin/contractsTerminal window pnpm add --save-dev @openzeppelin/contractsTerminal window yarn add --dev @openzeppelin/contractsFor
forge-std, install it directly from GitHub via your package manager:Terminal window npm add --save-dev 'github:foundry-rs/forge-std#v1.9.7'Terminal window pnpm add --save-dev 'github:foundry-rs/forge-std#v1.9.7'Terminal window yarn add --dev 'github:foundry-rs/forge-std#v1.9.7' -
Reconcile remappings
Hardhat 3 reads
remappings.txtfiles automatically, including the ones inside npm packages and git submodules, so your existing entries keep working. Once a dependency is installed via npm, you can either:- Keep your existing imports by updating the remapping target within
remappings.txtto point at the npm package (e.g.@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/), or - Drop the remapping entirely and rewrite your
importstatements to use the npm package path (e.g.import "@openzeppelin/contracts/access/Ownable.sol") — Hardhat resolves npm imports natively, no remapping required.
If your project relies on
forge-generated remappings (noremappings.txtis checked in), install the@nomicfoundation/hardhat-foundryplugin and add it to yourpluginsarray. - Keep your existing imports by updating the remapping target within
-
Optionally remove
lib/once everything buildsRe-run
hardhat buildto confirm the npm-resolved dependencies work. If all of your dependencies have been migrated to npm, you can deletelib/and the corresponding.gitmodulesentries.
Migrating Solidity tests
Section titled “Migrating Solidity tests”Hardhat 3’s Solidity testing is designed for compatibility with Foundry test suites. Solidity tests, cheatcodes, and forge-std are supported out of the box — no plugin required.
-
Confirm your test layout is picked up
By default, Hardhat treats every
.solfile undertest/as a test file, and any.t.solfile undersrc/(or your configuredsourcesdirectory). A contract is treated as a test contract if it has at least onetest*function, andsetUp()runs before each test — the same conventions as Foundry. If your tests live somewhere else, overridepaths.tests.solidity:hardhat.config.ts export default defineConfig({paths: {tests: {solidity: "./test",},},}); -
Run a single test file
Start by running one test file to confirm everything compiles and executes:
Terminal window npx hardhat test solidity test/SomeTest.t.solTerminal window pnpm hardhat test solidity test/SomeTest.t.solTerminal window yarn hardhat test solidity test/SomeTest.t.solThen run the full Solidity suite:
Terminal window npx hardhat test solidityTerminal window pnpm hardhat test solidityTerminal window yarn hardhat test solidityhardhat test(withoutsolidity) runs every test runner registered in your config (e.g.node:test); if you only have Solidity tests today, the two are equivalent. -
Port your test execution settings
Translate settings from
[profile.default]and[fuzz]/[invariant]infoundry.tomlinto thetest.solidityentry:hardhat.config.ts import { defineConfig } from "hardhat/config";export default defineConfig({test: {solidity: {isolate: true,fuzz: {runs: 256,},invariant: {runs: 256,depth: 15,},},},});See the Solidity tests configuration reference for the full list of options, including
from,txOrigin,blockTimestamp, fork settings, and thefsPermissionsallowlist that replaces Foundry’sfs_permissions. -
Enable
ffiand filesystem access if your tests need themThese are off by default for security reasons. If you use
vm.ffi, settest.solidity.ffi: true. If you usevm.readFile/vm.writeFile, declare the allowed paths undertest.solidity.fsPermissions. -
Replace
anvilwith the in-process network (orhardhat node)Solidity tests run against an in-process EDR-powered network, so you don’t need a separate
anvilinstance to execute them — including for fork tests, which are configured undertest.solidity.fork. If you previously rananvilfor ad-hoc interaction (for example to attach a script or external client), run a JSON-RPC node with:Terminal window npx hardhat nodeTerminal window pnpm hardhat nodeTerminal window yarn hardhat node
Migrating scripts and deployments
Section titled “Migrating scripts and deployments”Hardhat doesn’t have a direct equivalent of forge script. For deployments, we recommend Hardhat Ignition, which is declarative and handles resumability, idempotency, and verification. For one-off scripts (admin actions, data migrations), define a Hardhat task and call your contracts through a network connection:
import { defineConfig, task } from "hardhat/config";
const deployCounter = task("deploy-counter", "Deploys the Counter contract") .setInlineAction(async (_taskArgs, hre) => { const { viem } = await hre.network.create(); const counter = await viem.deployContract("Counter");
console.log("Counter deployed at:", counter.address); }) .build();
export default defineConfig({ tasks: [deployCounter],});The viem (or ethers) helpers come from a toolbox plugin; install whichever you prefer and add it to plugins in your config.
Migration blockers
Section titled “Migration blockers”If your migration is blocked by a missing feature, plugin, or cheatcode, please let us know in this issue.