Foundry v1.0 版本引入了多项破坏性变更,使用旧版本的项目可能需要相应调整。本指南记录了从旧版本迁移时的最佳实践建议。

主要变更清单:

  • 默认禁用 Solc 优化器
  • 内部调用默认禁用期望回滚测试辅助代码(expect revert cheatcode)
  • 移除对 testFail 测试的支持
  • 移除对旧版 console.sol 签名的支持
  • 忽略冲突的重映射
  • Forge 覆盖率报告不持久化产物文件
  • 其他变更

默认禁用 Solc 优化器

默认启用优化器可能为表面正确的代码引入潜在缺陷(若优化器本身存在漏洞,参见 #2486)。Foundry v1.0 的默认设置可能对现有项目产生以下影响:导致合约体积增大和/或项目构建失败。建议在 foundry.toml 配置文件中显式启用优化器并指定运行参数:

optimizer = true
optimizer_runs = 200

详见:优化配置

内部调用默认禁用期望回滚测试辅助代码

当在相同调用层级使用 vm.expectRevert 测试辅助代码测试内部函数时,仅首个 vm.expectRevert 会生效(详见 #3437)。Foundry v1.0 默认禁用了该行为,可能导致现有项目出现测试失败,报错信息为 [FAIL: 调用未在比测试辅助代码调用层级更浅的位置触发回滚]。建议检查这些失败的测试用例,选择启用内部调用回滚功能或重构测试代码以避免此类问题。

例如,类似以下测试案例:

contract ContractTest is Test {
    error CustomError();

    function revertWithCustomError() internal {
        revert CustomError();
    }

    function testExample() public {
        vm.expectRevert();
        revertWithCustomError();
    }
}

可以通过修改为使用一个模拟的 CustomContract 并公开公共函数来实现:

contract CustomContract {
    error CustomError();

    function revertWithCustomError() external {
        revert CustomError();
    }
}

contract ContractTest is Test {
    CustomContract internal c;

    function setUp() public {
        c = new CustomContract();
    }

    function testExample() public {
        vm.expectRevert();
        c.revertWithCustomError();
    }
}

或通过配置允许预期回滚检测:

    /// forge-config: default.allow_internal_expect_revert = true
    function testExample() public {
        vm.expectRevert();
        revertWithCustomError();
    }

请参阅:vm.expectRevert 作弊码文档

移除对 testFail 测试的支持

Foundry v1.0 移除了 testFail 测试前缀支持以避免混淆 - 详见 #4437。这可能导致现有项目在非预期位置报告测试失败。迁移现有 testFail 测试至 v1.0 可通过以下方式实现:使用 vm.expectRevert() 作弊码,或者使用 try/catch 方法并断言失败。

例如,原测试代码:

function testFail_IncrementAsNotOwner() public {
    vm.prank(address(0));
    upOnly.increment();
}

可重写为:

function test_RevertWhen_CallerIsNotOwner() public {
    vm.expectRevert(Unauthorized.selector);
    vm.prank(address(0));
    upOnly.increment();
}

移除对旧版 console.sol 签名的支持

Foundry v1.0 移除了对使用错误 ABI 编码的 console.sol 选择器的支持 - 详见 #8910。这可能导致现有项目出现测试构建失败,因此需要相应更新。例如:

console.log("testMisc", 42);
console.log(0);

应重写为:

console.log("testMisc", uint256(42));
console.log(uint256(0));

忽略冲突的重映射

在 Foundry v1.0 之前,forge 会通过子项目的重映射推断全局重映射。当存在冲突时,最长/最具体的路径会优先。此行为存在安全隐患,因为添加子项目可能完全改变实际执行的代码逻辑(即使根项目重映射未修改)- 详见 #8910v1.0 版本中,若相同重映射定义同时存在于根项目和依赖项中,构建将失败。例如:

@openzeppelin/=lib/openzeppelin-contracts/contracts/

修复方法:在根项目的 remappings.txt 中为冲突的重映射添加 src 上下文限定:

src:@openzeppelin/=lib/openzeppelin-contracts/contracts/

这会将重映射范围限制在项目的 src 目录内,避免与依赖项中的同名重映射冲突。

Forge 覆盖率报告不持久化产物文件

在旧版本中,运行 forge coverage 会修改构建产物(这些产物未经过优化,用于提供准确的源码命中映射)且无任何警告。这容易引发混淆,并导致如 #8840 讨论中的意外情况。Foundry v1.0 在运行覆盖率测试时不再生成构建产物,因此项目需注意此项变更。

其他变更

重要变更说明

  1. FORGE_SNAPSHOT_CHECK 需指定布尔值:现在必须明确设置 FORGE_SNAPSHOT_CHECK=trueFORGE_SNAPSHOT_CHECK=false,不再支持隐式值。
  2. forge inspect --pretty 参数已移除:直接使用 forge inspect 查看表格视图,无需 --pretty 参数。
  3. forge bind --ethers 已弃用:默认绑定器切换为 alloy,直接运行 forge bind 即可。
  4. forge debug 子命令移除:调试功能迁移至:
    • 测试场景:使用 forge test --debug
    • 脚本场景:使用 forge script --debug
  5. cast etherscan-source 更名为 cast source:新命令 explorer_urlexplorer_api_url 支持自定义区块浏览器。
  6. foundryup 默认安装稳定版
    • 稳定版:foundryup(默认)
    • 测试版:foundryup -i nightly

孟斯特

声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
腾讯云开发者社区:孟斯特