foundry
学习web3一个多月了,刚开始用remix,然后用hardhat,然后是truffle。
为了测试Damn Vulnerable DeFi的合约,我必须掌握foundry
foundry的作弊码是什么
核心需求理解
你想了解 Foundry 中的「作弊码(Cheatcodes)」是什么,以及它们的核心用途、常用类型和使用方式,这能帮助你理解之前测试失败时涉及的 vm.prank、vm.expectEmit 等代码的本质。
Foundry 作弊码(Cheatcodes)详解
1. 作弊码的核心定义
Foundry 的作弊码(Cheatcodes)是通过 Vm 合约(forge-std/Vm.sol)提供的一系列特殊函数,允许你在测试中操控区块链环境、模拟账户行为、修改状态、捕获事件等——这些操作在真实链上无法实现,但对测试智能合约至关重要(比如模拟任意地址发起交易、修改区块时间、断言事件是否触发)。
简单来说:作弊码是 Foundry 为测试量身打造的「上帝模式」,让你能灵活控制测试环境,覆盖各种边界场景。
2. 作弊码的使用前提
使用作弊码前,需在测试合约中引入 Vm 合约,并声明一个 vm 实例(Foundry 会自动注入,无需手动部署):
1 | |
3. 常用作弊码分类及核心示例
按用途可分为以下几类,结合你之前的测试日志,重点解释相关作弊码:
| 分类 | 常用作弊码 | 核心用途 |
|---|---|---|
| 账户模拟 | prank(address) |
让接下来的一次调用以指定地址(如 attacker/player)的身份执行 |
startPrank(address) |
让接下来的所有调用以指定地址执行(直到 stopPrank()) |
|
stopPrank() |
结束 startPrank 的身份模拟 |
|
| 事件断言 | expectEmit() |
声明「预期接下来会触发某个事件」,需配合事件发射代码使用 |
expectRevert() |
断言接下来的调用会触发回滚(可指定回滚原因) | |
| 环境操控 | warp(uint256) |
修改当前区块时间(模拟时间推移,测试锁仓/过期逻辑) |
roll(uint256) |
修改当前区块高度 | |
deal(address, uint256) |
直接给指定地址分配 ETH(绕过转账,快速测试余额相关逻辑) | |
deal(address, address, uint256) |
给指定地址分配 ERC20 代币(比如快速给测试账户转 DVT 代币) | |
| 状态修改 | mockCall() |
模拟合约调用的返回值(比如伪造某个合约的 balanceOf 结果) |
hoax(address) |
等价于 deal(address, 100 ether) + prank(address)(快速给地址转 ETH+模拟身份) |
4. 你之前测试中涉及的作弊码详解
结合你失败的测试日志,重点解释两个核心作弊码:
(1)vm.prank(address) / vm.startPrank()
1 | |
你日志中的 VM::prank(deployer) 就是测试代码中用 vm.prank(deployer) 让 checkFlashLoan 以部署者身份执行。
(2)vm.expectEmit()
这是你测试失败的核心关联作弊码,用于断言「某个事件会被触发,且参数匹配」。
正确使用流程:
1 | |
你测试失败的原因是:实际触发的 FlashLoanStatus(true) 与你预期的 FlashLoanStatus(false) 参数不匹配。
5. 其他高频作弊码示例
1 | |
总结
- 核心定义:Foundry 作弊码是
Vm合约提供的测试工具,用于操控区块链环境、模拟行为、断言事件/回滚等,是测试智能合约的核心能力。 - 关键用途:模拟任意地址调用(
prank)、断言事件触发(expectEmit)、修改环境状态(warp/deal)、验证回滚(expectRevert)。 - 你的测试关联:失败源于
expectEmit断言的FlashLoanStatus(false)与实际触发的true参数不匹配,需检查业务逻辑为何闪电贷未失败。
掌握这些作弊码能帮你精准调试 Unstoppable 挑战的测试用例,比如用 vm.deal 快速给测试账户转代币,用 vm.expectRevert 断言闪电贷触发回滚,用 vm.prank 模拟攻击者操作等。
什么是foundry中的vm合约?
你想了解 Foundry 中核心的 vm 合约(也常叫 Cheatcode 合约),这是 Foundry 测试/开发中最关键的工具之一,我会用通俗易懂的方式帮你讲清楚。
核心需求理解
你想知道 Foundry 里的 vm 合约到底是什么、有什么用、以及怎么用——本质上是想理解这个核心工具的定位和核心能力,帮你更好地解读之前的测试失败日志,也能在自己的测试中用好它。
vm 合约的完整解读
1. 先给一个通俗定义
vm 是 Foundry 内置的作弊码合约(Cheatcode Contract),它不是以太坊主网/测试网上真实存在的合约,而是 Foundry 为了方便开发者测试智能合约,在本地模拟环境中提供的“超级工具”。
你可以把它理解成:在测试智能合约时,你拥有了一个“上帝视角”的控制台,通过调用 vm 的函数,你可以随意修改区块链的状态、模拟各种特殊场景(比如修改区块高度、模拟不同地址调用、伪造事件预期等),而这些操作在真实链上是完全做不到的。
2. vm 合约的核心作用
它的核心价值是降低智能合约测试的复杂度,让你能精准模拟各种边界场景、异常场景,比如:
- 模拟不同地址发起交易(比如你之前日志里的
vm.prank()/vm.startPrank()) - 预期某个事件会被触发(比如你之前日志里的
vm.expectEmit()) - 修改区块高度、时间、gas 价格
- 模拟合约调用失败、回滚
- 读取/修改合约的私有变量
- 伪造签名、模拟链上余额变动等
3. 核心常用方法(结合你之前的日志举例)
先看你测试日志里出现的几个 vm 方法,再扩展核心常用方法:
| 方法名 | 作用(通俗解释) | 你日志中的场景 |
|---|---|---|
vm.prank(address) |
临时切换调用者身份,让接下来的一次合约调用看起来是由 address 发起的 |
vm.prank(deployer):让后续调用以部署者地址执行 |
vm.startPrank(address) |
永久切换调用者身份,直到调用 vm.stopPrank() 为止(批量模拟某地址调用) |
vm.startPrank(player):模拟玩家地址执行一系列操作,之后用 stopPrank() 恢复 |
vm.stopPrank() |
结束 startPrank 的身份模拟,恢复默认调用者 |
日志中结束玩家身份模拟 |
vm.expectEmit() |
预期接下来的操作会触发某个事件,若没触发/参数不匹配,测试直接失败 | 你测试中预期触发 FlashLoanStatus(false),但实际触发 true,导致测试失败 |
其他高频使用的 vm 方法:
vm.warp(uint256 timestamp):修改当前区块的时间(比如模拟几天/几个月后的场景)vm.roll(uint256 blockNumber):修改当前区块高度vm.deal(address, uint256 amount):给指定地址设置 ETH 余额vm.expectRevert(bytes):预期接下来的调用会回滚,并验证回滚的原因(比如特定错误信息)vm.load(address, bytes32 slot):读取合约指定存储槽的值(即使是私有变量)
4. 基础使用示例(新手友好)
下面是一个简单的 Foundry 测试示例,展示 vm 合约的核心用法:
1 | |
5. 关键注意事项
vm合约仅在测试环境生效:部署到真实链上的合约无法调用vm的任何方法,它是 Foundry 本地模拟环境的专属工具。- 必须继承
forge-std/Test.sol:只有继承这个合约,你的测试合约才能使用vm实例(Test.sol内部已经封装了vm)。 - 方法调用是“即时生效”的:比如
vm.prank()调用后,下一次合约交互就会以指定地址执行,无需额外配置。
总结
vm是 Foundry 内置的作弊码合约,仅用于本地测试,能模拟各种链上无法实现的操作;- 核心作用是切换调用者身份、预期事件/回滚、修改链状态(时间/余额/区块)等,大幅提升测试灵活性;
- 你之前测试失败的
vm.expectEmit()就是vm合约的核心方法,用于验证事件触发是否符合预期。
理解 vm 合约是用好 Foundry 做智能合约测试的关键,它能让你精准复现各种场景,包括你之前 Unstoppable 挑战中需要模拟的“让闪电贷失败”的场景。
vm合约类似于vmware,虚拟机操作系统,为智能合约提供了虚拟以太坊环境,方便了安全审计人员对contract.sol进行测试
你的这个类比很有想象力,核心方向是对的,但我会帮你把这个理解校准得更精准,避免混淆概念~
核心需求理解
你想确认 vm 合约是否可以类比为 VMware 这类虚拟机,以及它在智能合约测试/安全审计中的核心价值,我会先肯定你的核心认知,再补充关键差异和精准定位。
类比的精准解读
1. 你的类比「对的部分」
你把 vm 合约类比为 VMware 虚拟机,核心抓准了“虚拟环境” 这个关键:
- VMware 是在物理操作系统上模拟出独立的操作系统环境,隔离且可随意操作(比如重启、装软件、测试漏洞);
- Foundry 的
vm合约(作弊码)是在本地计算机上模拟出以太坊区块链的虚拟环境,同样隔离且可随意操作(比如修改地址余额、切换调用者、篡改区块时间)。
对安全审计人员来说,vm 合约确实像给 contract.sol 搭建了一个“可任意调试的以太坊虚拟机(EVM)沙箱”,不用连接真实测试网/主网,就能低成本、高灵活度地测试合约的各种场景(包括攻击场景)。
2. 类比「需要修正的部分」(避免概念混淆)
⚠️ 注意:vm 合约≠ 以太坊虚拟机(EVM),而是运行在 EVM 模拟环境之上的“超级控制工具”:
- VMware 本身是“完整的虚拟机”,包含操作系统内核、硬件模拟等;
- Foundry 的
vm合约不是“虚拟机”,而是 Foundry 给 EVM 模拟环境(Foundry 内置的 EVM 实现)提供的控制接口/作弊工具。
用更精准的类比:
| 组件 | 类比对象 | 核心角色 |
|---|---|---|
| Foundry 内置的 EVM | VMware 虚拟机(硬件+系统层) | 提供智能合约运行的基础环境 |
vm 合约(作弊码) |
VMware 的“虚拟机设置面板”+“作弊器” | 控制/修改 EVM 环境的各种参数 |
简单说:
- EVM 是“虚拟的以太坊操作系统”,负责执行智能合约代码;
vm合约是“操作系统的超级管理员权限”,让你能修改这个虚拟系统的规则(比如给任意地址加钱、让A地址冒充B地址发交易)。
3. 安全审计视角下的价值(补充你的认知)
对安全审计人员来说,vm 合约的核心价值体现在:
- 低成本复现攻击场景:比如你之前的 Unstoppable 挑战,审计人员可以用
vm.prank()模拟攻击者地址、vm.deal()给攻击者加代币,快速验证合约是否存在漏洞; - 精准验证异常场景:用
vm.expectRevert()验证合约是否能正确拦截恶意操作,用vm.expectEmit()验证关键事件是否按预期触发; - 隔离测试环境:每个测试用例的环境都是独立的,不会互相干扰,审计人员可以并行测试多个漏洞点,不用清理“测试残留”;
- 突破真实链的限制:真实链上无法修改区块时间、无法读取私有变量,但
vm.warp()/vm.load()能轻松做到,方便审计人员测试时间锁、私有变量逻辑等边界场景。
总结
- 你的核心认知是对的:
vm合约和 VMware 类似,都是通过“虚拟环境”让测试/审计更灵活,且不影响真实系统; - 精准定位:
vm不是“虚拟机本身”,而是 Foundry 给 EVM 虚拟环境提供的超级控制工具; - 对安全审计来说,
vm是测试智能合约漏洞的核心利器,能模拟各种攻击场景、验证合约的异常处理逻辑。
这个类比能帮你快速理解核心,但记住“EVM 是虚拟机,vm 是控制虚拟机的工具”这个细节,能让你对 Foundry 的测试逻辑理解更透彻~