前言

最近在使用Foundry开发智能合约,发现它相较于传统的Hardhat或Truffle工具链确实有明显的优势。Foundry是由Paradigm团队开发的Ethereum智能合约开发工具包,基于Rust构建,包括四个核心组件:forge(编译、测试与部署)、cast(链上交互)、anvil(本地节点模拟)和chisel(Solidity REPL)。特别值得一提的是,它支持原生Solidity测试,无需JavaScript,同时还有模糊测试(fuzzing)支持,性能也相当出色。

最近正好在研究Foundry v0.2.0+版本(Rust 1.80+),结合官方文档和社区实践,总结了以下本地开发、测试与部署的完整流程,分享给大家。

1. 安装与项目初始化

1.1 安装 Foundry

安装Foundry非常简单,使用官方的foundryup脚本即可:

 1# 安装 foundryup
 2curl -L https://github.com/foundry-rs/foundry/releases/latest/download/foundryup-installer.sh | bash
 3
 4# 安装 Foundry 工具链(forge, cast, anvil, chisel)
 5foundryup
 6
 7# 验证安装
 8foundry --version  # 输出类似: forge 0.2.0 (abc1234 2025-11-01)
 9anvil --version
10cast --version

个人建议

  • 在项目中使用foundry.toml配置来锁定版本,避免更新带来的兼容性问题。
  • CI/CD中可以使用foundryup -i来确保环境一致性。

1.2 创建新项目

Foundry项目结构非常简洁,主要包含:src/(合约源代码)、test/(测试)、script/(部署脚本)、lib/(依赖):

1# 初始化新项目
2forge init my-project
3cd my-project
4
5# 项目结构
6ls
7# 输出: foundry.toml  lib/  script/  src/  test/
  • src/Counter.sol:默认示例合约(简单计数器)
  • test/Counter.t.sol:默认测试文件(.t.sol 后缀表示测试)
  • foundry.toml:配置文件(可自定义 Solidity 版本、优化器等)

示例配置文件foundry.toml):

1[profile.default]
2src = "src"
3out = "out"
4libs = ["lib"]
5solc_version = "0.8.27"
6optimizer = true
7optimizer_runs = 200
8via_ir = true  # 启用 Intermediate Representation 优化(提高 Gas 效率)

1.3 本地开发工作流

  • 编写合约:在src/下创建Solidity文件。Foundry支持OpenZeppelin等库,通过Git子模块管理依赖(无需npm)。

    举个例子:扩展Counter.sol为ERC20代币(src/MyToken.sol):

     1// SPDX-License-Identifier: MIT
     2pragma solidity ^0.8.27;
     3
     4import "forge-std/console.sol";  // Foundry 内置日志库
     5
     6contract MyToken {
     7    string public name = "MyToken";
     8    mapping(address => uint256) public balanceOf;
     9
    10    function mint(address to, uint256 amount) external {
    11        balanceOf[to] += amount;
    12        console.log("Minted %d tokens to %s", amount, to);  // 调试日志
    13    }
    14}
    
  • 安装依赖

    1# 添加 OpenZeppelin(Git 子模块)
    2forge install OpenZeppelin/openzeppelin-contracts@v5.0.0
    3
    4# 更新 remappings(自动生成 .solc-remappings.json)
    5forge remappings > remappings.txt
    
  • 编译合约

    1forge build  # 编译 src/ 下所有 .sol 文件,输出到 out/
    2# 或指定优化
    3forge build --optimize --optimize-runs 1000000
    

调试小技巧

  • 使用chisel进行交互式测试:
    1chisel  # 启动 REPL,输入 Solidity 代码实时执行
    2> uint256 x = 42; console.log(x);  # 输出: 42
    
  • 使用forge build --gas-report生成Gas使用报告。

2. 测试智能合约

Foundry 的测试框架(Forge)允许用 Solidity 编写测试,支持模糊测试(fuzzing)、作弊码(cheatcodes,如 vm.prank 模拟调用者)和断言(assertEq)。测试文件置于 test/,继承 Test.sol

2.1 编写测试

示例:为 MyToken 编写全面测试(test/MyToken.t.sol):

 1// SPDX-License-Identifier: UNLICENSED
 2pragma solidity ^0.8.27;
 3
 4import "forge-std/Test.sol";
 5import "../src/MyToken.sol";
 6
 7contract MyTokenTest is Test {
 8    MyToken token;
 9    address user = makeAddr("user");  // 生成测试地址
10
11    function setUp() public {
12        token = new MyToken();
13        deal(address(token), 1000 ether);  // 作弊码:注入 ETH(模拟余额)
14    }
15
16    // 基本断言测试
17    function testName() public {
18        assertEq(token.name(), "MyToken");
19    }
20
21    // 集成测试:铸币与余额检查
22    function testMint() public {
23        vm.prank(user);  // 作弊码:模拟 user 调用
24        token.mint(user, 100);
25
26        assertEq(token.balanceOf(user), 100);
27    }
28
29    // 模糊测试:随机输入验证(fuzzing,覆盖边缘案例)
30    function testMintFuzz(uint256 amount) public {
31        vm.assume(amount > 0 && amount < type(uint256).max / 2);  // 假设边界
32        vm.prank(user);
33        token.mint(user, amount);
34        assertEq(token.balanceOf(user), amount);
35    }
36
37    // 失败案例测试(expectRevert)
38    function testMintFail() public {
39        vm.expectRevert();  // 预期回滚
40        vm.prank(address(0));  // 零地址调用(假设无权限)
41        token.mint(user, 100);
42    }
43}

关键概念

  • setUp():每个测试前执行(类似于Mocha的beforeEach)
  • 作弊码(Cheatcodes)vm实例提供EVM操纵,如vm.prank(伪造msg.sender)、vm.deal(注入余额)、vm.warp(时间戳跳跃)
  • 模糊测试testMintFuzz(uint256 amount)自动生成数千随机输入,检测溢出/边界问题

2.2 运行测试

 1# 运行所有测试
 2forge test
 3
 4# 详细输出 + Gas 报告
 5forge test -vvv --gas-report
 6
 7# 运行特定测试
 8forge test --match-test testMint
 9
10# 模糊测试(增加迭代次数)
11forge test --fuzz-runs 10000
12
13# 覆盖率报告
14forge coverage --report lcov  # 生成 LCOV 报告,可集成 SonarQube

输出示例(成功测试):

[PASS] testMint() (gas: 25000)
[FAIL] testMintFail() (gas: 15000)  # 预期失败
Coverage: 95.2% lines, 88.5% branches

个人经验

  • 测试覆盖率尽量保持在90%以上(使用forge coverage
  • 每个test*函数保持独立,互不影响
  • 重点关注边界情况:零值、最大值、重入(reentrancy)

3. 部署智能合约

部署主要通过Forge的脚本系统(script/下的Solidity脚本)来完成,也支持CLI方式。支持本地(Anvil)、测试网(Sepolia)和主网部署。

3.1 启动本地节点(Anvil)

Anvil是Foundry的本地EVM模拟器,提供10个预资助账户(每个10000 ETH):

1# 启动 Anvil(默认 RPC: http://127.0.0.1:8545)
2anvil
3
4# 输出: Accounts (10): 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
5#       Listener: http://127.0.0.1:8545
  • Fork 模式:可以模拟真实网络(如Sepolia fork):
    1anvil --fork-url https://rpc.sepolia.org --fork-block-number 5000000
    

3.2 编写部署脚本

script/Deploy.s.sol中:

 1// SPDX-License-Identifier: MIT
 2pragma solidity ^0.8.27;
 3
 4import "forge-std/Script.sol";
 5import "../src/MyToken.sol";
 6
 7contract DeployToken is Script {
 8    function run() external {
 9        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");  // 从环境变量加载
10        vm.startBroadcast(deployerPrivateKey);
11
12        MyToken token = new MyToken();
13        console.log("Deployed at: %s", address(token));
14
15        vm.stopBroadcast();
16    }
17}

3.3 执行部署

 1# 设置环境变量
 2export PRIVATE_KEY=your_private_key_here  # 测试网私钥(勿用主网)
 3
 4# 本地部署(Anvil)
 5forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast
 6
 7# 测试网部署(Sepolia)
 8forge script script/Deploy.s.sol --rpc-url https://rpc.sepolia.org --broadcast --verify  # --verify 自动验证 Etherscan
 9
10# 输出示例
11[] Compiling...
12[] Broadcasting...
13Transaction successful: 0xabc123... (Block Confirmation: 1)
14Deployed at: 0x1234567890abcdef...

链上交互(Cast): 部署后,使用 Cast 调用合约:

1# 调用 mint 函数
2cast send 0x1234567890abcdef "mint(address,uint256)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 "100" --private-key $PRIVATE_KEY --rpc-url http://127.0.0.1:8545
3
4# 查询余额
5cast call 0x1234567890abcdef "balanceOf(address)(uint256)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --rpc-url http://127.0.0.1:8545
6# 输出: 100

3.4 多链与升级部署

  • 配置多链:在foundry.toml添加[etherscan]部分,支持自动验证
  • 代理升级:集成OpenZeppelin Upgrades插件(forge install OpenZeppelin/openzeppelin-upgrades-flattener

个人经验

  • 使用--slow标志运行完整测试套件(包括Gas基准测试)
  • CI/CD中使用GitHub Actions(测试+部署到测试网)
  • 安全方面:私钥通过环境变量或DOTENV加载;生产前运行slither .静态分析

最后

最近使用Foundry开发了一些项目,确实感觉比传统的Hardhat工具链效率更高。特别是原生Solidity测试和作弊码功能很大程度上简化了开发流程,模糊测试也提供了更全面的测试覆盖。

进一步阅读

  • 官方手册:https://book.getfoundry.sh/
  • 示例项目:ethereum-blockchain-developer.com 的 ERC721 教程
  • 高级测试:Metana 的 Foundry 测试指南
  • 社区:Foundry Discord / GitHub (foundry-rs/foundry)