blockchainsv
Developer Tools & Infrastructure·July 05, 2026·12 min read

Migrate legacy Truffle projects to Hardhat now?

If you're still running truffle compile in 2025, your build pipeline depends on infrastructure that officially no longer receives security patches or maintenance updates.

Migrate legacy Truffle projects to Hardhat now?

The Deadline Already Passed — And Your Truffle Project Is Living on Borrowed Time

If you're still running truffle compile in 2025, your build pipeline depends on infrastructure that officially no longer receives security patches or maintenance updates. Consensys formally announced the end-of-life for both Truffle and Ganache in September 2024, recommending developers migrate to Hardhat or Foundry. That's not a rumour floating through Discord channels — it's a published decision with a fixed date behind it. Your existing deployments won't evaporate overnight, and smart contracts already on-chain obviously don't care which framework compiled them, but every day you keep building on a deprecated toolchain, you're accumulating technical debt at the infrastructure layer — the one place you can least afford to accumulate it.

The question isn't whether to migrate. It's how to do it without blowing up your test coverage, breaking your deployment scripts, or losing a week to configuration rabbit holes. This guide walks through the architectural differences between the two frameworks and lays out a phased migration strategy that keeps your project shippable at every intermediate step.

Why Consensys Pulled the Plug — And What It Means for Your Stack

Truffle served the ecosystem well for years. It was arguably the first tool that made Ethereum development feel like software development rather than alchemy. But the landscape shifted. Hardhat's plugin architecture, faster local execution via its custom EVM implementation (Hardhat Network), and an active open-source community created a widening gap in developer experience and maintenance velocity. Foundry, coming from the Rust side, pushed performance even further with Solidity-native testing.

The practical implication of the September 2024 EOL is straightforward: Truffle will continue to compile and deploy contracts that worked before the cutoff, but any newly discovered vulnerabilities in the toolchain itself — dependency conflicts, RPC compatibility issues, Solidity compiler bugs in the bundled solc — will not be patched. For a hobby project, that's manageable. For anything touching real capital or running in CI/CD pipelines that other engineers depend on, it's an unacceptable risk surface.

Staying on a deprecated toolchain doesn't break your project today — it breaks your ability to fix it tomorrow.

The recommended migration targets are Hardhat and Foundry. Each has a distinct architectural philosophy. Hardhat leans into JavaScript/TypeScript-native workflows with Ethers.js and Mocha, making it the more natural landing zone for teams already embedded in the Truffle ecosystem. Foundry demands a shift to Solidity-native testing and Rust-influenced CLI patterns — a steeper curve but one that pays dividends in execution speed. For this guide, we're focusing on the Truffle-to-Hardhat path because it preserves more of your existing mental model and allows incremental adoption.

Architectural Differences: Configuration, Artifacts, and the Compiler Pipeline

The first friction point in any migration is configuration. Truffle's world revolves around truffle-config.js — a single file that defines networks, compiler versions, and project paths. Hardhat uses hardhat.config.js (or .ts for TypeScript projects), and while the intent is similar, the structure and available options are meaningfully different.

AspectTruffleHardhat
Config filetruffle-config.jshardhat.config.js / .ts
Artifact storagebuild/contracts/artifacts/
Default test runnerMocha (standalone)Mocha + Chai via @nomicfoundation/hardhat-toolbox
Deployment modelMigration scripts (migrations/)Hardhat Ignition or deploy tasks
Solidity compiler managementBundled solc per configPer-project solc with multi-version support (0.4.x through 0.8.x+)
JavaScript libraryweb3.js or Ethers.js (manual)Ethers.js v6 (default in Toolbox)
Local nodeGanache (separate process)Hardhat Network (built-in, in-process)

The artifact path difference is a subtle but recurring source of confusion. Truffle stores compiled contract ABIs and bytecode in build/contracts/, and your existing scripts, tests, and frontend integrations likely reference that path directly. Hardhat outputs to artifacts/ with a nested directory structure organized by contract name. Any script that hardcodes the old path will break silently — it'll either throw a file-not-found error or, worse, read stale artifacts and produce misleading test results.

Hardhat's Solidity version management deserves particular attention. Truffle ties your compiler version to whatever is specified in the config — one version for the whole project. Hardhat lets you define multiple Solidity compiler versions in hardhat.config.js, which matters if your project imports OpenZeppelin v4 contracts (Solidity 0.8.x) alongside older libraries pinned to 0.6.x. This isn't just a convenience; it's a prerequisite for some dependency trees that Truffle handled only through workarounds or by manually installing specific solc binaries.

Porting Your Test Suite Without Losing Coverage

This is where most migration attempts stall. Your tests aren't just code — they're your specification of expected behaviour, accumulated over months or years of development. Rewriting them from scratch is not just tedious; it's dangerous, because you'll inevitably introduce gaps.

The good news is that Hardhat's default testing stack is Mocha and Chai, which is what most Truffle projects already use. The migration friction comes from two sources: assertion libraries and contract interaction patterns.

In a typical Truffle test, you interact with contracts using either the Truffle contract abstraction (which wraps web3.js) or Ethers.js if you've already made that switch. The Truffle abstraction provides convenience methods like MyContract.deployed() and automatic gas estimation. Hardhat, via @nomicfoundation/hardhat-toolbox, uses Ethers.js v6 as the default contract interaction layer. The API is different enough that you can't simply search-and-replace.

Here's what the migration looks like in practice for a typical test pattern:

Truffle-style (before):

const MyToken = artifacts.require("MyToken");
contract("MyToken", (accounts) => {
it("should have correct initial supply", async () => {
const instance = await MyToken.deployed();
const supply = await instance.totalSupply();
assert.equal(supply.toString(), "1000000000000000000000");
});
});

Hardhat-style (after):

const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("MyToken", function () {
it("should have correct initial supply", async function () {
const MyToken = await ethers.getContractFactory("MyToken");
const instance = await MyToken.deploy();
const supply = await instance.totalSupply();
expect(supply).to.equal(ethers.parseEther("1000"));
});
});

The differences are structural, not just syntactic. artifacts.require() becomes ethers.getContractFactory(). The contract() wrapper (which provides a clean-room blockchain snapshot per test file) is replaced by describe() with Hardhat's snapshot/revert mechanism configured through helpers. assert.equal() migrates to Chai's expect() syntax, and numeric comparisons shift from string coercion to BigNumber-aware matchers.

The scale of this work depends on your test suite. A project with 20 test files and straightforward assertions might port in a day. A project with 200 test files, custom assertion helpers, and direct web3.js calls to raw transaction encoding could take a week of focused engineering time. There's no shortcut here — but there is a phased approach that keeps your project green throughout.

Deployment Scripts: From Migrations to Ignition

Truffle's migration system — the numbered JavaScript files in migrations/ — is one of its most distinctive features. Each migration file exports a function that receives a deployer object, and Truffle tracks which migrations have been executed on-chain using a Migrations contract. It's a clever state machine, but it's also Truffle-specific infrastructure that has no direct equivalent in Hardhat.

Hardhat offers two deployment paths:

1. Hardhat Ignition — the newer, declarative deployment framework that models deployments as a graph of modules with explicit dependencies. It's more robust for complex multi-contract deployments and provides built-in idempotency.

2. Hardhat-deploy plugin — a community-maintained plugin that provides a migration-style workflow more familiar to Truffle users, with named deployments and fixture support for tests.

For a migration context, Hardhat-deploy reduces friction because it maps more directly to what you already have. Each Truffle migration file roughly corresponds to a Hardhat-deploy deploy function. You lose the on-chain tracking (the Migrations contract pattern), but you gain named deployments and the ability to tag deployments for selective re-execution — which is actually more flexible than Truffle's sequential model.

If your migrations are simple — deploy contract A, then deploy contract B with A's address — the port is mechanical. If they involve conditional logic, proxy upgrades, or multi-step orchestrations with verification checks, expect to spend time reasoning about how to represent that dependency graph in Ignition modules rather than imperative scripts.

The Phased Approach: Hardhat-Truffle5 as a Bridge

Here's where the migration gets genuinely practical. You don't have to flip a switch. Hardhat provides a plugin called hardhat-truffle5 that lets you run your existing Truffle tests inside the Hardhat environment with minimal modification. This is the single most valuable tool for a risk-managed migration.

The phased workflow looks like this:

1. Install Hardhat alongside Truffle. Initialise a Hardhat project in your existing Truffle repository — they can coexist. Your truffle-config.js and hardhat.config.js live side by side without conflict.

2. Install @nomicfoundation/hardhat-truffle5. This plugin registers the Truffle contract abstraction (artifacts.require()) within Hardhat's runtime, allowing your existing test files to run against Hardhat Network instead of Ganache.

3. Run your Truffle tests under Hardhat. Execute npx hardhat test and see what passes. Most well-structured Truffle tests will pass with zero or minimal changes — the plugin handles the abstraction layer.

4. Fix failures incrementally. Issues typically arise from assumptions about Ganache's default behaviour (block timestamps, account ordering, gas limits) that differ from Hardhat Network's defaults. Address these one at a time while keeping your CI pipeline green.

5. Migrate tests to native Hardhat syntax. Once all tests pass under the compatibility layer, start converting files to native Ethers.js + Chai patterns at your own pace. There's no rush — the compatibility plugin is stable.

6. Replace deployment scripts. Port your migrations to Hardhat-deploy or Ignition modules. This is typically the last step because deployment scripts often interact with external systems (block explorers, multisig wallets, governance timelocks) that need careful testing.

The plugin-based bridge strategy means your project never enters a broken state — each commit is deployable, each test run is meaningful.

This phased approach has a throughput advantage that's easy to underestimate: it lets your team keep shipping features on the current branch while the migration proceeds in parallel. You're not asking product managers to freeze the roadmap for two weeks while engineering rewrites infrastructure. You merge the Hardhat config, get tests green under the compatibility layer, and then peel off individual files for native conversion as time permits.

What Changes for Your CI/CD Pipeline

Your continuous integration configuration is another artefact that needs updating, and it's one that teams often forget until the first broken build after migration. Truffle-based CI typically runs truffle test and truffle migrate --network <target>. Hardhat replaces these with npx hardhat test and npx hardhat run scripts/deploy.js --network <target>.

The dependency installation step also changes. Your package.json swaps truffle for hardhat and @nomicfoundation/hardhat-toolbox (which bundles Ethers.js, Chai, and the gas reporter). If you were using Ganache as a standalone process in CI — spinning it up as a Docker container or background service — you can eliminate that entirely, since Hardhat Network runs in-process and starts automatically when tests execute. This simplifies your CI configuration and removes a common source of flaky tests caused by Ganache port conflicts in parallel builds.

One detail worth noting: Hardhat's default gas reporting and stack traces are significantly more informative than Truffle's, but they also change the output format of your test logs. If you have log-parsing scripts or CI notifications keyed on specific Truffle output patterns, those will need updating.

Compiler Version Alignment and Dependency Conflicts

A frequent stumbling block during migration is the Solidity compiler version. Truffle allows only one compiler version in truffle-config.js, and many projects pin it to whatever version was current when the project started — often 0.7.x or even 0.6.x. Hardhat supports multiple compiler versions simultaneously through the solidity field in its config, using a compiler version list:

solidity: {
compilers: [
{ version: "0.8.20" },
{ version: "0.6.12" }
]
}

This is necessary when your dependency tree mixes contracts targeting different compiler ranges. OpenZeppelin v5 targets 0.8.20+, while some legacy libraries in your project may still require 0.6.x. Hardhat resolves the correct compiler per file automatically based on the pragma statement. Truffle forced you to pick one and hope your dependencies compiled — or required manual solc binary management.

The practical benefit is that your node_modules dependencies compile correctly without you having to vendor or fork them. It also means that upgrading your own contracts to a newer Solidity version can happen file-by-file rather than as a big-bang refactor.

Finality: Where You Should Land

The migration from Truffle to Hardhat is not a leap of faith — it's a series of well-understood engineering steps with known failure modes and documented solutions. The architecture is close enough that your existing knowledge transfers, but different enough that you need to respect the structural gaps: configuration format, artifact paths, contract interaction APIs, and deployment model.

The phased approach using the hardhat-truffle5 compatibility plugin is the strategy I'd recommend for any production project with meaningful test coverage. It eliminates the risk of a broken intermediate state and lets your team maintain velocity throughout the transition. Start by getting your existing tests green under Hardhat's runtime. Then convert to native patterns. Then port deployments. Then update CI. Each step is independently valuable and independently verifiable.

The September 2024 deadline has come and gone. Your Truffle project still compiles, still deploys, still works — for now. But the dependency graph that holds it together is no longer being maintained, and every month that passes increases the probability of a breakage you can't fix by opening a GitHub issue. Treat this migration as you would any other infrastructure hardening work: methodically, with a rollback plan, and with the understanding that the cost of doing it now is lower than the cost of doing it under pressure later.

For developers also thinking about building diversified income streams alongside their engineering work — whether through token-based revenue models or dividend-focused investment strategies — the same principle applies: infrastructure decisions compound over time, and the cost of deferred maintenance is always higher than it looks in the moment.

FAQ

Why should I migrate from Truffle to Hardhat?
Truffle is officially deprecated and no longer receives security updates. Migrating to Hardhat removes the technical debt of using unmaintained infrastructure and provides better performance, faster local execution, and superior Solidity compiler management.
Will my existing smart contracts break if I switch to Hardhat?
No, your smart contracts already on-chain are unaffected by the framework used to compile them. The migration primarily involves updating your build pipeline, test environment, and deployment scripts.
Can I keep my existing Truffle tests during the migration?
Yes, you can use the hardhat-truffle5 plugin to run your existing Truffle tests within the Hardhat environment. This allows you to verify your tests pass under Hardhat before converting them to native Hardhat syntax.
How do I handle deployment scripts that used Truffle migrations?
You can replace Truffle's migration system with Hardhat Ignition for declarative deployments or use the hardhat-deploy plugin, which provides a workflow similar to Truffle's numbered migration files.
Does Hardhat support projects with multiple Solidity versions?
Yes, Hardhat allows you to define multiple Solidity compiler versions in your configuration file. It automatically resolves the correct compiler for each file based on its pragma statement, unlike Truffle which is limited to one version per project.

By Lucas Meade