一、为什么框架选择很重要
选择开发框架不仅仅是选一个编译工具,它是整个开发体验的基础。一个好的框架能让你:
- 快速迭代:写完代码,几秒内看到测试结果
- 高效调试:出了问题能快速定位根源
- 安全交付:在代码上主网前发现潜在的漏洞
- 团队协作:多个开发者能无缝合作
我见过有团队因为选错了框架,导致开发效率低下、测试覆盖率不足、最终在审计中发现大量问题。框架选对了,这些风险都能大大降低。

二、Foundry深度解析
2.1 Foundry是什么
Foundry是Paradigm(知名加密风投)推出的开发框架,由Rust编写,核心卖点是极速。它包含三个主要工具:
- Forge:编译、测试、部署
- Cast:命令行与链交互(类似以太坊的瑞士军刀)
- Anvil:本地测试节点
我第一次用Foundry跑测试时震惊了——原本Hardhat需要几分钟的测试套件,Foundry几秒就完成。这种速度提升对开发体验的影响是巨大的。
2.2 测试体验
Foundry最令人惊艳的功能是测试完全用Solidity编写:
solidity
// test/MyToken.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Test, console} from "forge-std/Test.sol";
import {MyToken} from "../src/MyToken.sol";
contract MyTokenTest is Test {
MyToken public token;
function setUp() public {
token = new MyToken();
}
function testTokenDeployment() public {
assertEq(token.name(), "MyToken");
assertEq(token.symbol(), "MTK");
assertEq(token.totalSupply(), 1000000 * 10 ** 18);
}
function testTransfer() public {
token.transfer(address(1), 100);
assertEq(token.balanceOf(address(1)), 100);
}
// 模糊测试 - 自动生成各种输入
function testTransferFuzz(address to, uint256 amount) public {
uint256 balance = token.balanceOf(address(this));
amount = bound(amount, 0, balance);
token.transfer(to, amount);
assertEq(token.balanceOf(to), amount);
assertEq(token.balanceOf(address(this)), balance - amount);
}
}
运行测试只需一行命令:
bash
forge test
2.3 Fuzzing与Invariant Testing
Foundry的模糊测试是我用过最强大的安全工具。普通测试需要你手动设计测试用例,而fuzzer会自动生成成千上万种输入组合,找出你没想到的边界情况:
solidity
function testTransferFuzz(address to, uint256 amount) public {
vm.assume(to != address(0) && to != address(this));
amount = bound(amount, 1, token.balanceOf(address(this)));
uint256 recipientBefore = token.balanceOf(to);
token.transfer(to, amount);
assertEq(token.balanceOf(to), recipientBefore + amount);
}
Invariant Testing更进一步——你定义一个系统必须始终满足的不变式,fuzzer会尝试各种操作序列,看能否打破这个不变量:
solidity
// 不变量:总供应量应该等于所有账户余额之和
contract InvariantTest is Test {
MyToken token;
function setUp() public {
token = new MyToken();
targetContract(address(new Handler(token)));
}
function invariantTotalSupply() public view {
assertEq(token.totalSupply(), token.balanceOf(address(this)));
}
}
2.4 Gas报告
bash
forge snapshot
这个命令会生成一个Gas消耗报告文件,下次运行时自动对比,显示每个函数的Gas消耗变化。如果你的某个修改导致Gas暴涨,Foundry会明确标出。
三、Hardhat深度解析
3.1 Hardhat的特点
Hardhat是目前社区最流行的框架,生态系统非常成熟。它用JavaScript/TypeScript编写,对前端开发者友好,大量DeFi项目(如Uniswap、Aave)都基于Hardhat开发。
核心优势:
- 插件生态丰富:hardhat-deploy、hardhat-ethers、hardhat-waffle…
- 调试友好:内置console.log、详细的错误堆栈
- TypeScript支持:大型项目类型安全有保障
- 团队协作:配置文件易于版本控制和分享
3.2 项目初始化
bash
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init
Hardhat的初始化向导会引导你创建项目结构,包括contracts/、scripts/、test/目录,以及配置文件。
3.3 测试配置
javascript
// test/token.test.js
const { expect } = require("chai");
describe("MyToken", function() {
let token;
let owner;
let addr1;
beforeEach(async function() {
const Token = await ethers.getContractFactory("MyToken");
[owner, addr1, ...addrs] = await ethers.getSigners();
token = await Token.deploy();
await token.deployed();
});
it("Should set the right name and symbol", async function() {
expect(await token.name()).to.equal("MyToken");
expect(await token.symbol()).to.equal("MTK");
});
it("Should transfer tokens correctly", async function() {
await token.transfer(addr1.address, 50);
expect(await token.balanceOf(addr1.address)).to.equal(50);
});
});
3.4 部署脚本
Hardhat的部署脚本非常适合复杂的多步骤部署:
javascript
// scripts/deploy.js
const hre = require("hardhat");
async function main() {
console.log("Deploying MyToken...");
const Token = await hre.ethers.getContractFactory("MyToken");
const token = await Token.deploy();
await token.deployed();
console.log(`Token deployed to: ${token.address}`);
// 验证合约
await hre.run("verify:verify", {
address: token.address,
constructorArguments: [],
});
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
使用hardhat-deploy插件还能自动追踪部署历史和合约验证状态。
四、深度对比
4.1 性能对比
这是两者最显著的差异。我做过实际测试:
| 操作 | Hardhat | Foundry | 差异 |
|---|---|---|---|
| 编译(100个合约) | 45秒 | 8秒 | 5.6倍 |
| 运行100个单元测试 | 120秒 | 12秒 | 10倍 |
| Fuzz测试(10万次迭代) | 不支持 | 15秒 | – |
Foundry的速度优势来自于它用Rust编写、编译到原生代码,以及测试直接在EVM中执行。
4.2 调试体验
Hardhat在这方面有优势。它的console.log可以打印任意数据类型:
solidity
function testDebug() public {
uint256 balance = token.balanceOf(user);
console.log("User balance:", balance);
console.logBool("Has balance", balance > 0);
}
Foundry的调试能力正在快速追赶,trace信息非常详细,能看到每次调用的具体指令和堆栈。
4.3 插件与生态
Hardhat的插件生态是无与伦比的:
hardhat-deploy:追踪部署历史hardhat-ethers:以太坊交互封装hardhat-waffle:测试断言库hardhat-gas-reporter:Gas消耗报告
Foundry的插件相对较少,但核心功能非常强大。高级用法需要写Solidity代码,不如JavaScript直观。
4.4 TypeScript支持
如果你的项目用TypeScript,Hardhat是更自然的选择。Foundry的配置文件用Toml,对TypeScript项目支持有限。
五、选型建议
选择Hardhat的场景:大型团队项目、复杂部署流程、前端集成密切的项目。
选择Foundry的场景:测试驱动开发、安全敏感项目、频繁迭代的个人项目。
我的实践:混合使用——Foundry用于测试和本地开发,Hardhat用于部署脚本。
这种组合能充分利用两者的优势。
六、迁移指南
6.1 从Hardhat迁移到Foundry
bash
# 安装Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup
# 初始化Foundry项目
forge init . --force
# Foundry自动创建test目录,适配现有测试
大部分Solidity代码无需修改。Hardhat测试需要用Foundry的语法重写:
javascript
// Hardhat风格
expect(await token.balanceOf(addr1.address)).to.equal(50);
// Foundry风格
assertEq(token.balanceOf(addr1.address), 50);
6.2 共享合约代码
如果团队同时用两个框架,可以在项目根目录放置合约代码:
plaintext
project/
├── contracts/ # Solidity合约,两个框架共用
├── foundry/ # Foundry配置和测试
├── hardhat/ # Hardhat配置和脚本
└── web/ # 前端代码
solidity
// foundry/src/MyContract.sol
import {MyContract} from "../contracts/MyContract.sol";
七、工具链整合
7.1 Slither静态分析
两个框架都支持Slither:
bash
# Foundry项目
forge build
slither . --contract "MyContract"
# Hardhat项目
npx hardhat compile
npx slither .
7.2 代码覆盖率
bash
# Foundry
forge coverage
# Hardhat (需要solidity-coverage插件)
npx hardhat coverage
Foundry的覆盖率报告更详细,能看到每个分支的覆盖情况。
结语
没有完美的框架,只有适合你项目的框架。我的建议是:
- 新学区块链开发:从Hardhat入手,社区资源丰富,遇到问题容易找到答案
- 追求测试效率:直接学Foundry,测试速度的优势用过就回不去
- 生产项目:根据团队技术栈选择,大型TypeScript项目选Hardhat,安全敏感项目选Foundry
- 不确定:两者都装上,根据需求切换
框架只是工具,真正重要的是写出安全、高效、可维护的智能合约。
相关阅读:

发表回复