Foundry是一个用于以太坊应用开发的便携、快速和模块化的工具包。文章详细介绍了Foundry的优势和特性,包括使用Solidity编写测试、模糊测试、覆盖虚拟机状态以及在真实网络状态下运行测试等,并提供了性能比较和应用示例。
我很高兴地宣布一个我们在过去几个月里开发的项目:Foundry。
Foundry 是一个便携式、快速且模块化的以太坊应用开发工具包。
如果你想要一个最快、最灵活的以太坊开发环境,且无需配置或第三方库,那么你应该使用 Foundry 的工具,forge 和 cast。
致谢: Foundry 是对测试框架 dapptools 的重实现,使用 Rust 编写,速度极快,易于安装,对更广泛的贡献者友好。尽管我们的代码库不是一个分支(并且增加了许多新功能,如支持多个 solc 版本),但这一切都得益于 DappHub 团队多年来的创新工作。谢谢 DappHub!
如果你同意以下以太坊开发建议,那么 Foundry 就适合你。
大多数开发者仍然使用 Javascript 或 Typescript 测试 Solidity,这样并不好。
在 JS 中测试需要大量的样板代码、大量依赖(我指的就是你 node_modules/),以及配置文件。例如,你可以查看 Paul Berg 的 solidity-template。
此外,以太坊在 JS 中的数字需要使用 BigNumber 库,例如 bignumber.js、BigNumber、bn 或 JS 的新原生 BigInt,这些都经常导致不兼容问题和生产力损失。
最后,使用 JS 而非 Solidity 测试意味着你需要在实际想要测试的内容之外再操作一个抽象层,这要求你至少熟悉 Mocha 和 Ethers.js 或 Web3.js。这增加了 Solidity 开发者的门槛。
Forge 让你可以用 Solidity 编写测试,这样你可以专注于重要的事情:编写优秀的测试。
一个简单的 Solidity 测试如下所示:
即使你对代码中的每个函数进行单元测试并尝试达到 100% 测试覆盖率,仍然可能会有你没有测试到的边缘案例。模糊测试允许 Solidity 测试运行器为你随机选择参数,只需将参数传递给你的 Solidity 测试函数。
这里有一个对上述智能合约进行模糊测试的示例:
模糊测试工具会使用随机值 x 自动尝试这个函数。如果它发现一个输入使得测试失败,它会将其返回给你,以便在修复该错误后创建回归测试:
如果你运行了这个测试,你将在 CLI 中收到以下响应:
它还支持缩小,因此你将得到一个导致你的代码失败的“最小”反例(而不是,例如,一个非常大的数字或字节字符串)。
你是否尝试过测试一个需要特定块编号的函数?当然,你可以调用 RPC 方法 evm_mine,但如果你正在测试一个 Compound 治理合约,并且需要前进 40,000 个块怎么办?
你是否尝试模拟一个主网络交易,并希望给你的账户一个特定的代币余额,或者对一个权限函数写入访问?
为了解决这些问题(还有更多),我们提供了 VM 作弊码,这允许在测试运行时修改 VM 的状态。这项服务通过一个在预先配置地址上存在的合约暴露给测试作者。下面的简单示例展示了如何覆盖一个块的时间戳:
关于其他作弊码的更多信息可以在 README 中找到。作弊码非常强大(例如,store 允许你覆盖任意合约存储槽,而 prank 允许你从任意账户发出任意调用)。我们建议花时间使用它们来扩展你的测试所探索的代码路径,并鼓励贡献 新代码。
像大多数以太坊开发工具一样,Forge 通过指定一个节点 URL(并可选地指定一个块编号,如果你有一个归档节点,可以将你的测试锁定到一个块)来支持对远程网络状态的“分叉”。只需运行 forge test --fork-url <你的节点 URL> [--fork-block-number <你想要的块编号>]
。
Forge 支持使用 ds-test 的 emit log_
函数进行运行时调试日志记录,也支持 Hardhat 的 console.log
。
可以通过运行 cargo install --git https://github.com/gakonst/foundry --locked
安装 Forge 和 Cast(如果你还没有安装 Rust,可以 这里 安装)。我们还计划按平台分发静态构建的二进制文件,并提供 brew
和 apt
包。如果你之前做过自动发布流程,欢迎 联系!
安装后,你只需执行 forge init
来创建一个新项目(默认在当前目录)然后 forge build
。
就这样。你在 <2s 内开始了。
我们已针对一些 Dapptools 库进行了基准测试,以比较测试速度。集成测试也可以在 这里 找到。
项目 | Forge | Dapp | 加速 |
---|---|---|---|
guni-lev | 28.6s | 2m36s | 5.45x |
solmate | 6s | 46s | 7.66x |
geb | 11s | 40s | 3.63x |
vaults | 1.4s | 5.5s | 3.9x |
我们还使用 Forge 和 Hardhat 编译了 openzeppelin-contracts。Hardhat 编译耗时 15.244s,而 Forge 只需 9.449s。另一个基准测试也展示了有前景(且细微的!)结果。也许应该为编译和测试框架建立一个基准测试套件?
在 2020 年夏天,我们开始编写 ethers-rs,这是 ethers.js 的 Rust 移植,旨在帮助 MEV 交易者构建更好的机器人。
然后,我们构建了其他基础设施,如 MEV Inspect、Ethers Fireblocks、Ethers Flashbots、Ark Circom、Optics 和 更多。
现在,我们构建了一条灵活的编译管道( ethers-solc 可能支持 新语言 如 Fe),以及 EVM 的抽象( evm-adapters)和 快速测试运行器。
我们正逐渐而稳步地为下一个 100 万以太坊开发者和企业家创造模块化、文档齐全且高性能的构建模块。
有关如何使用 Foundry CLI 的更多信息,请查看 README。
我们还有许多功能想要添加(不仅要达到 dapptools 的功能平衡,还有更多新的令人兴奋的功能)。你应该查看 GitHub 上的 Foundry。
最后,我们正在 Paradigm 内部及其投资组合中招聘 - 请查看我们所有的开放角色,访问 jobs.paradigm.xyz,或通过 georgios@paradigm.xyz 与我联系。
contract Foo {
uint256 public x = 1;
function set(uint256 _x) external {
x = _x;
}
function double() external {
x = 2 * x;
}
}
contract FooTest {
Foo foo;
// 合约的状态在每次测试运行前重置,
// 每次在部署后调用 `setUp()` 函数。可以把它想象成 JavaScript
// 中的 `beforeEach` 块
function setUp() public {
foo = new Foo();
}
// 一个简单的单元测试
function testDouble() public {
require(foo.x() == 1);
foo.double();
require(foo.x() == 2);
}
// 一个失败的单元测试(函数名称以 `testFail` 开头)
function testFailDouble() public {
require(foo.x() == 1);
foo.double();
require(foo.x() == 4);
}
}
function testDoubleWithFuzzing(uint256 x) public {
foo.set(x);
require(foo.x() == x);
foo.double();
require(foo.x() == 2 * x);
}
function testDoubleWithFuzzingCounterExample(uint256 x) public {
foo.set(x);
require(foo.x() == x);
foo.double();
require(foo.x() == 4 * x);
}
[FAIL. 反例:calldata=0x44735ef10000000000000000000000000000000000000000000000000000000000000001, args=[Uint(1)]] testDoubleWithFuzzingCounterExample (gas: [fuzztest])
address constant CHEATCODE_ADDRESS = 0x7cFA93148B0B13d88c1DcE8880bd4e175fb0DeDF;
interace Vm {
// 将 block.timestamp 设置为 `x`。
function warp(uint256 x) external;
}
contract MyTest {
Vm vm = Vm(CHEATCODE_ADDRESS);
function testWarp() public {
vm.warp(100);
require(block.timestamp == 100);
}
}
- 原文链接: paradigm.xyz/2021/12/int...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!