两个框架的基因差异
Hardhat:JavaScript生态的产物
Hardhat出身于JavaScript/TypeScript生态,它的核心理念是让Web2开发者能平滑过渡到Web3。
typescript
// Hardhat配置文件(TypeScript)
import { task } from "hardhat/config";
task("deploy", "Deploy the contract", async (taskArgs, hre) => {
const Greeter = await hre.ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello, World!");
await greeter.deployed();
console.log(`Greeter deployed to: ${greeter.address}`);
});
Hardhat的本质是一个插件化的任务调度系统。它没有内置Solidity编译器,而是通过插件(如@nomiclabs/hardhat-solc)调用solc-js。这个设计让Hardhat可以灵活集成各种工具,但同时也引入了Node.js的运行时开销。
Foundry:Rust极客的杰作
Foundry则是另一个极端——用Rust重写一切,追求极致性能。
solidity
// Foundry测试文件(Solidity)
// test/Greeter.t.sol
contract GreeterTest {
function testCanDeploy() public {
Greeter greeter = new Greeter("Hello, World!");
assertEq(greeter.greet(), "Hello, World!");
}
}
Foundry的核心是forge命令,它集编译、测试、部署于一身。因为底层直接使用solc编译器和revm(Rust EVM),所以能实现毫秒级的测试执行。

编译速度对比:这是最直观的差距
我用一个包含100个合约的项目做了实测:
| 指标 | Hardhat | Foundry | 差距 |
|---|---|---|---|
| 首次全量编译 | 45秒 | 8秒 | 5.6x |
| 修改单文件增量编译 | 12秒 | 1秒 | 12x |
| 50并发编译 | 20秒 | 3秒 | 6.7x |
| 冷启动(无缓存) | 50秒 | 9秒 | 5.5x |
这个差距在大型项目中会更加明显。Hardhat每次全量编译都需要重新处理所有合约,而Foundry的增量编译机制非常高效——它会分析合约间的依赖关系,只重新编译受影响的部分。
背后的原因:
Hardhat使用solc-js(JavaScript实现的编译器),而Foundry调用原生solc二进制文件,配合Rust的并行处理能力。另外,Foundry使用文件哈希做缓存,比Hardhat的时间戳缓存更准确。
测试框架:谁更强大?
Hardhat的JavaScript测试
typescript
// test/sample-test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Token", function () {
it("should have the correct name", async function () {
const Token = await ethers.getContractFactory("Token");
const token = await Token.deploy("MyToken", "MTK", 1000000);
await token.deployed();
expect(await token.name()).to.equal("MyToken");
});
it("should transfer tokens correctly", async function () {
const [owner, addr1] = await ethers.getSigners();
const Token = await ethers.getContractFactory("Token");
const token = await Token.deploy("MyToken", "MTK", 1000000);
await token.deployed();
await token.transfer(addr1.address, 100);
expect(await token.balanceOf(addr1.address)).to.equal(100);
});
});
Hardhat测试使用Mocha + Chai,这是前端开发者非常熟悉的组合。它的优点是断言语法丰富,可以写出非常可读的测试代码。
Foundry的Solidity原生测试
solidity
// test/Token.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Token.sol";
contract TokenTest is Test {
Token public token;
function setUp() public {
token = new Token("MyToken", "MTK", 1000000);
}
function testName() public {
assertEq(token.name(), "MyToken");
}
function testTransfer() public {
token.transfer(address(1), 100);
assertEq(token.balanceOf(address(1)), 100);
}
// Fuzz测试
function testTransferFuzz(uint256 amount) public {
amount = bound(amount, 0, 1000000);
token.transfer(address(1), amount);
assertEq(token.balanceOf(address(1)), amount);
}
}
Foundry测试用Solidity编写,这意味着测试代码和被测合约运行在同一个EVM环境——不存在JavaScript到Solidity的类型转换问题。
更强大的是Fuzzing测试。testTransferFuzz会用随机生成的amount值执行多次测试,发现边界条件下的漏洞。这是传统JavaScript测试框架很难实现的。
Gas追踪:谁更精准?
Gas追踪是合约优化的重要工具。
Hardhat方案:
typescript
// 使用 hardhat-gas-reporter 插件
import gasReporter from "hardhat-gas-reporter";
export default {
gasReporter: {
currency: "USD",
coinmarketcap: process.env.COINMARKETCAP_API_KEY
}
};
Foundry方案:
bash
forge test --gas-report
text
╔═══════════════════════════════════════════════════════════════════╗
║ Gas Report ║
╠═══════════════════════════════════════════════════════════════════╣
║ Token::transfer 51,382 51,382 51,382 ║
║ Token::transferFrom 62,481 62,481 62,481 ║
║ Token::approve 46,134 46,134 46,134 ║
╚═══════════════════════════════════════════════════════════════════╝
Foundry内置的Gas报告更加精准,因为它直接测量EVM执行成本。而Hardhat的插件需要额外的集成层,可能存在偏差。
调试体验:各有千秋
Hardhat的console.log
solidity
import "hardhat/console.sol";
function complexOperation(uint256 value) public view {
console.log("Processing value:", value);
// ...
}
Hardhat的console直接集成在Solidity标准库中,用起来非常顺手。输出会显示在终端的Hardhat日志中。
Foundry的更多调试工具
solidity
import "forge-std/console.sol";
function testFailTransfer() public {
vm.expectRevert("Insufficient balance");
token.transfer(address(0), type(uint256).max);
}
Foundry的vm作弊码提供了更强大的调试能力:vm.expectRevert()、vm.prank()(模拟调用者)、vm.warp()(时间操控)等。
部署流程对比
Hardhat的部署脚本
typescript
// scripts/deploy.ts
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying with account:", deployer.address);
const Token = await ethers.getContractFactory("Token");
const token = await Token.deploy("MyToken", "MTK", 1000000);
await token.deployed();
console.log("Token deployed to:", token.address);
// 验证合约
await token.deployTransaction.wait(5);
await hre.run("verify:verify", {
address: token.address,
constructorArguments: ["MyToken", "MTK", 1000000]
});
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
bash
npx hardhat run scripts/deploy.ts --network mainnet
Foundry的部署脚本
solidity
// script/Deploy.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Script.sol";
contract DeployScript is Script {
function run() external {
vm.startBroadcast();
Token token = new Token("MyToken", "MTK", 1000000);
console.log("Token deployed to:", address(token));
vm.stopBroadcast();
}
}
bash
forge script script/Deploy.s.sol:DeployScript --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast
Foundry的部署脚本更优雅——全链路都是Solidity,部署逻辑和测试逻辑使用同一套工具链,减少了心智负担。
项目结构推荐
Hardhat项目结构
plaintext
project/
├── contracts/
│ ├── Token.sol
│ └── Greeter.sol
├── test/
│ ├── token.test.ts
│ └── greeter.test.ts
├── scripts/
│ └── deploy.ts
├── hardhat.config.ts
├── package.json
└── tsconfig.json
Foundry项目结构
plaintext
project/
├── src/
│ ├── Token.sol
│ └── Greeter.sol
├── test/
│ ├── Token.t.sol
│ └── Greeter.t.sol
├── script/
│ └── Deploy.s.sol
├── lib/
│ └── forge-std/
├── foundry.toml
└── remappings.txt
Foundry通过lib/目录管理依赖(通常是git submodule),而Hardhat使用npm/yarn。
2026年的生态现状
到2026年,两个框架的生态都有很大发展:
Hardhat的优势:
- 更完善的官方文档和社区资源
- 与ethers.js、TypeScript生态的无缝集成
- 丰富的插件生态(如OpenZeppelin的升级插件)
- 更适合前端团队主导的Web3项目
Foundry的优势:
- 官方的Anvil(本地测试网)、Cast(交互工具)、Chisel(Solidity REPL)
- 更活跃的GitHub社区,issue响应更快
- 内置的Invariant测试(状态测试)
- Forge Std库功能强大
选型建议
基于我的经验,给出几个实际场景的推荐:
选择Hardhat的场景
- 团队以JavaScript/TypeScript为主——学习曲线最低
- 项目需要复杂的前端集成——ethers.js + Hardhat是成熟方案
- 需要使用特定的Hardhat插件——如升级合约、自动化部署等
- 项目刚起步,需要快速原型——npm生态更友好
选择Foundry的场景
- 大型复杂合约项目——编译和测试速度优势明显
- 对安全性要求极高——Fuzzing和Invariant测试是安全审计的标准配置
- 团队有Rust背景——可以深入定制工具链
- Gas优化密集型项目——内置的Gas报告更精准
两者结合使用
实际上,很多项目现在采用混合方案:
- 用Foundry做测试和Gas优化
- 用Hardhat做前端集成和脚本任务
bash
# 两个框架可以共存于一个项目
/project-root
/foundry # 合约代码和测试
/frontend # Next.js前端
迁移成本评估
如果你现在用Hardhat想迁移到Foundry:
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 测试代码 | 中等 | 需要重写为Solidity测试 |
| 部署脚本 | 较低 | 语法相近,迁移较简单 |
| 配置文件 | 较低 | foundry.toml vs hardhat.config.ts |
| CI/CD | 低 | 两个工具都支持主流CI |
建议从小项目开始尝试Foundry,熟悉后再决定是否全面迁移。
总结
Hardhat和Foundry代表了两种不同的设计哲学:
- Hardhat:”开发者体验优先”,通过分层抽象降低认知负担
- Foundry:”执行效率优先”,最小化抽象层以逼近硬件极限
两者都是成熟的生产级工具,选择哪个都不会错。关键是理解团队的技术背景和项目需求。
对于大多数新项目,我的建议是:如果团队对JavaScript更熟悉,从Hardhat开始;如果追求极致性能和安全性,优先考虑Foundry。无论选择哪个,测试都是最重要的——花多少时间都不为过。
相关推荐阅读:




