Foundry框架
Foundry 的优势是完全使用 Solidity 进行开发与测试,且Foundry 构建、测试的执行速度非常快。
对比HardHat
| 维度 | Foundry | Hardhat |
|---|---|---|
| 开发语言 | 纯 Solidity(测试、部署、脚本都用 Solidity) | JavaScript/TypeScript(测试、脚本)+ Solidity(合约) |
| 学习曲线 | 对 Solidity 开发者更友好,上手快 | 需掌握 JS/TS,适合全栈背景开发者 |
| 测试效率 | 原生支持多线程,测试速度极快 | 基于 Node.js,测试速度较快但略逊于 Foundry |
| 生态集成 | 生态较新,工具链集成度高(铸币、Fuzz 测试等) | 生态成熟,插件丰富(如 Hardhat Etherscan、Hardhat Deploy) |
| 部署流程 | 部署脚本用 Solidity 编写,简洁直接 | 部署脚本用 JS/TS,灵活性高 |
| Fuzz 测试 | 原生支持,内置模糊测试工具 | 需依赖第三方插件(如hardhat-fuzz) |
| 调试体验 | 调试功能较弱,主要依赖日志输出 | 内置调试器,支持断点、单步执行 |
| 适用场景 | 适合纯 Solidity 项目、追求极致测试速度 | 适合复杂项目、需要丰富插件生态、或全栈开发 |
开始
安装Foundry
如果你是windows系统,可能要去Git Bash里面输入以下命令。
1 | curl -L https://foundry.paradigm.xyz | bash |
初始化
1 | cd <你的项目所在的文件夹名称> |
初始化在forge init <你的项目名字>中,init命令会自动安装forge-std库,如果安装失败(在lib文件夹里面查看是否有forge-std文件),可以手动输入以下命令。forge install foundry-rs/forge-std
编译合约
输入命令forge build来编译src目录下的所有合约:
1 | D:\AllCode\Solidity\Foundry\Program\Start01\start01>forge build |
测试合约
输入命令forge test来编译test目录下的所有合约测试:
1 | D:\AllCode\Solidity\Foundry\Program\Start01\start01>forge test |
如果想要获得更详细的测试:forge test -vvvv
部署合约
首先处理好隐私数据.env与foundry.toml文件,可以先编写好脚本文件,然后在Git Bash里输入:
1 | $ forge script script/Counter.s.sol --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY --broadcast |
OK,部署成功!
我在cmd和powershell里面输入命令去部署会失败,建议在GitBash里面输入命令。因为Git Bash 一般会自动读取
.env文件中的变量,且支持$变量名格式。
如果Git Bash 无法读取.env文件中的变量,尝试手动输入:$ source .env
然后查看是否读取:$ echo $SEPOLIA_RPC_URL(私钥同理)
提示:.env文件里的等号两边似乎不能有空格。
隐私数据的处理
在项目根目录下添加.env文件,写入关键信息:
1 | PRIVATE_KEY=0x(钱包私钥) |
在foundry.toml文件里添加:
1 | [rpc_endpoints] |
编写测试合约
solidity语言
导入Test.sol
1 | import "forge-std/Test.sol"; |
Test.sol 是 Foundry 提供的测试基类,它包含:
- 断言函数(用于验证结果):
assertEq(a, b)- 断言 a 等于 bassertGt(a, b)- 断言 a 大于 bassertTrue(condition)- 断言条件为真
- 作弊码(Cheatcodes):通过
vm对象提供:vm.deal()-> 给地址发钱vm.prank()-> 切换某地址调用一次vm.startPrank()/vm.stopPrank()- 持续模拟某地址调用
| 作弊码 | 作用 | 示例 |
|---|---|---|
vm.deal(地址, 金额) |
给地址发 ETH | vm.deal(alice, 100 ether) |
vm.prank(地址) |
下一次调用模拟该地址 | vm.prank(alice); pool.deposit(); |
vm.startPrank(地址) |
持续模拟该地址 | vm.startPrank(alice); ... |
vm.stopPrank() |
停止模拟 | vm.stopPrank(); |
vm.warp(时间戳) |
设置区块时间 | vm.warp(1234567890); |
vm.roll(块号) |
设置区块号 | vm.roll(100); |
vm.expectRevert() |
期望下一次调用回滚 | vm.expectRevert(); pool.withdraw(); |
Foundry 规则:测试合约必须继承
Test,继承后,你就可以使用vm、assertEq等功能。
初始化
setUp() 是特殊函数,每个测试函数运行前都会自动执行,类似于其他测试框架的 beforeEach(),用于初始化测试环境。
1 | /** |
new 关键字用于部署新合约,部署者是 address(this)(测试合约本身)。部署后,pool 变量指向新合约的地址。
等价于在JavaScript中:
const pool = await deployer.deploy(ShadowLendPool);
vm是Foundry提供的全局对象,包含各种”作弊”功能,可以操纵区块链状态,只在测试环境有效,真实链上无法使用。
vm.deal(地址, 金额)的作用:在真实区块链上,你需要有 ETH 才能转账。在测试中,可以用 vm.deal() 凭空创造 ETH,这样测试更方便,不需要真实的资金。
测试函数
1 | function test_exploit() public { |
Foundry 规则:测试函数必须以
test开头,Foundry 会自动识别并运行这些函数。
ssertEq(a, b)的作用是断言a == b;如果不相等,测试失败并抛出错误。vm.startPrank(player)的作用是从现在开始,所有函数调用都会”假装”是 player 发起的,即msg.sender == player。assertGt(a, b)的作用是断言a > b(greater than);如果a <= b,测试失败。
JavaScript语言
详见《合约测试》笔记



