Hardhat 和 Foundry 是当前最主流的两个智能合约开发框架,都致力于简化从编码、测试到部署的完整开发流程。
Hardhat 基于 JavaScript/TypeScript 生态,用 js 或 ts 写测试,依赖 nodejs 环境,需要配置驱动 hardhat.config.js,将 Solidity 开发无缝融入 JavaScript 生态,特别适合全栈开发者(前端+合约)。。
Foundry 基于 Rust(性能相对 nodejs 快很多),可用 Solidity 写测试,测试即合约,消除语言边界,并且约定优于配置:目录结构即配置(src/ → 合约, test/ → 测试)。
Foundry
1
2
3
4
5
6
7
8
9
10
11
|
┌─────────────────────────────────────────────────────┐
│ Foundry 四大核心组件(Rust 实现) │
├───────────┬───────────┬───────────┬─────────────────┤
│ forge │ cast │ anvil │ chisel │
│ (编译/测试)│ (链交互) │ (本地链) │ (REPL 调试) │
└─────┬─────┴─────┬─────┴─────┬─────┴────────┬────────┘
│ │ │ │
▼ ▼ ▼ ▼
编译合约 读写链状态 提供测试环境 快速验证逻辑
运行测试 发送交易 分叉主网 实时计算
部署合约 解码事件 模拟攻击 调试 Gas
|
forge
test/ 目录下的 .t.sol 文件本质是继承 Test.sol 的合约,控制台执行 forge test 命令会:
- 编译所有合约(含测试合约)
- 部署测试合约到本地 EVM
- 调用以 test 开头的函数(如 testIncrement)进行测试
- 捕获断言失败/Gas 消耗/覆盖率
同时还有模糊测试内置,函数参数带 (uint256 x) 会自动运行 256 次随机输入,无需额外配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
test/Counter.t.sol
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Counter.sol";
// 测试合约本质是普通合约 + 特殊断言库
contract CounterTest is Test { // Test 来自 forge-std
function testFuzz_Increment(uint256 start) public {
Counter c = new Counter();
c.setNumber(start);
c.increment();
assertEq(c.number(), start + 1); // 断言在 EVM 中执行
}
}
|
cast
cast 无需写脚本就可以和任意 EVM 链交互
1
2
3
4
5
6
|
# 查询指定地址在以太坊主网上的 ETH 余额(单位:wei)
cast balance 0x... --rpc-url https://eth.llamarpc.com
# 调用某个合约地址的只读函数(view/pure),无需支付 Gas,不改变链上状态
cast call 0x... "name()" --rpc-url sepolia
# 广播交易到区块链,调用可修改状态的函数(如转账),需支付 Gas,--private-key $PK是发送方私钥(极度敏感!)
cast send 0x... "transfer(address,uint256)" 0x... 100 --private-key $PK --rpc-url sepolia
|
cast 本质是 命令行版的 Web3.js,但用 Rust 实现,速度极快且无 JS 依赖。
anvil
快速启动本地以太坊节点
- anvil –fork-url https://eth.llamarpc.com 瞬间复制主网状态,测试真实合约交互
- 启动即提供 10 个带 10,000 ETH 的测试账户(私钥固定,方便调试)
- 测试中用 vm.warp(timestamp) 操控区块时间(无需重启节点)
chisel
REPL(Read-Eval-Print Loop)环境,实时验证 Solidity 逻辑
1
2
3
4
5
6
|
chisel
>> uint256 x = 100
>> x * 2
200
>> address(this).balance # 查看当前合约余额
0
|
hardhat
1
2
3
4
5
6
7
8
9
10
11
12
|
┌──────────────────────────────────────────────────────────────┐
│ Hardhat 核心架构(Node.js + 插件系统) │
├──────────────┬──────────────┬──────────────┬─────────────────┤
│ Hardhat │ Hardhat │ 插件系统 │ 开发者工具链 │
│ Core │ Network │ (Plugins) │ (VS Code etc.) │
│ (任务调度) │ (本地EVM) │ │ │
└──────┬───────┴──────┬───────┴──────┬───────┴────────┬────────┘
│ │ │ │
▼ ▼ ▼ ▼
编译合约 模拟区块链 扩展功能: 深度集成:
运行测试 捕获堆栈 部署/验证/ 调试器/测试覆盖率
执行脚本 时间旅行 类型生成 实时错误提示
|
Hardhat 测试(TypeScript)
foundry 使用 solidity 编写测试,而 hardhat 使用 js/ts 编写测试,如:
1
2
3
4
5
6
7
8
9
10
|
it('should transfer tokens', async () => {
await token.transfer(user.address, amount);
expect(await token.balanceOf(user.address)).to.equal(amount);
// 复杂逻辑示例:循环测试 100 次不同金额
for (let i = 0; i < 100; i++) {
await token.transfer(user.address, ethers.utils.parseEther(i.toString()));
// ... 验证逻辑
}
});
|
hardhat.config.ts 配置中心
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import { HardhatUserConfig } from 'hardhat/config';
import '@nomicfoundation/hardhat-toolbox';
const config: HardhatUserConfig = {
solidity: {
version: '0.8.20',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
networks: {
hardhat: {
chainId: 1337,
accounts: [
{
privateKey:
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
balance: '10000000000000000000000', // 10,000 ETH
},
],
},
sepolia: {
url: process.env.SEPOLIA_RPC_URL || '',
accounts: [process.env.PRIVATE_KEY || ''],
},
},
paths: {
sources: './contracts',
tests: './test',
cache: './cache',
artifacts: './artifacts', // 生成的 ABI/字节码存放处
},
};
export default config;
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# 1. Core 编译合约(触发 solidity 任务)
npx hardhat compile
# 2. Network 启动本地链(自动触发)
npx hardhat test # 内部自动启动 hardhat network
# 3. 插件提供能力
- hardhat-ethers: 部署合约
- typechain: 生成 TS 类型
- chai-matchers: 断言事件
- hardhat-gas-reporter: 测试时显示 Gas 消耗
- hardhat-deploy: 部署脚本管理 + 多网络配置
|