Bytecode Debugger(字节码调试器)

在没有源码或需要核对底层执行时,按 EVM 指令查看调用、栈、内存和存储。

Bytecode Debugger 用来查看一笔交易在 EVM 中实际执行的指令。它适合没有验证源码、Source Map 不完整,或需要确认某个分支、外部调用和 REVERT 原因的场景。

它不会替代 Execution Trace:先用 Trace 找到要解释的调用,再切换到 Bytecode Mode 查看该调用内部到底执行了什么。

有哪些功能

功能用来做什么
Instructions按合约地址查看已执行的 opcode,并定位当前指令。
Stack查看当前指令的操作数和计算结果。
Memory查看调用数据、返回数据或临时编码的内容。
Storage查看当前执行位置可见的 Storage Slot 与值。
地址与字节码在多个参与执行的合约之间切换,确认当前 opcode 属于哪个合约。
Commands查看预设的 Stack、Memory 或 Storage 修改命令,并跳回对应指令。

常见 opcode 的含义:CALL 表示外部调用,SSTORE 表示写入 Storage,JUMPI 表示条件分支,REVERT 表示本次调用失败。不要只根据 opcode 名称下结论;同时查看 Stack、Memory 和前后的指令。

Instructions 中的 Previous / Next 怎么用

Instructions 面板也有一组 PreviousNext。它们不是在不同调用之间跳转,而是在当前交易的实际 EVM 执行序列中前后移动一条指令:

  • Previous 看某个 CALLSSTOREREVERT 之前准备了什么数据;
  • Next 看该指令执行后,Stack、Memory、Storage 或后续控制流发生了什么;
  • 到达这段执行的首条或末条指令时,对应按钮会不可用。

Commands 仅在可修改 Bytecode Mode 中出现。它列出已经为 Stack、Memory 或 Storage 设置的修改命令;点击一项会回到对应指令,方便比较修改前后的执行结果。

跟着一个例子操作

以下继续使用 XPEPE 交易 的执行过程。交易中,中间合约在 WETH.withdraw(...) 后收到 ETH,并继续向外转出。假设这段合约没有可用源码,我们可以用字节码确认这笔 ETH 转出是如何发生的。

下面的示例直接复用 Bytecode Debugger。mock 使用这笔交易中的中间合约、TokenStaker、UniswapV3Pool 和 WETH 地址,并包含 PUSH1MSTOREJUMPISSTORECALL 等指令,以及当前指令对应的 Stack、Memory、Storage 和 Commands:

1. 从 Trace 进入 Bytecode Mode

Execution Trace 找到 WETH.withdraw(...) 后面的调用,进入 Debugger 并打开 Bytecode Mode。先确认当前选中的是中间合约的执行位置,而不是 WETH 合约本身。

2. 找到外部调用

Instructions 中找到 CALL 后,用 Previous 查看它之前如何准备数据,再用 Next 查看调用之后的执行。CALL 说明 EVM 正准备向另一个地址执行外部调用;结合当前合约地址,先判断是谁发起了这次调用。

3. 用 Stack 和 Memory 确认调用内容

打开 Stack,核对 CALL 使用的目标地址、转出金额和输入输出范围。再查看 Memory:原生 ETH 转账通常不需要函数参数;如果 Memory 中有编码数据,说明它可能同时调用了目标合约的函数。

4. 判断是否写入状态或失败

继续前进:

  • 遇到 SSTORE 时,到 Storage 查看写入前后的 Slot;
  • 遇到 JUMPI 时,结合 Stack 判断走了哪个分支;
  • 遇到 REVERT 时,回看前几条指令和 Memory,确认失败条件或返回数据。

这样可以确认:这次 ETH 是由哪段字节码发起、是否带有调用数据,以及执行过程中有没有状态写入或失败。

使用建议

  • 先用 Execution Trace 缩小范围,再进入 Bytecode Mode;不要从整笔交易的第一条 opcode 开始读。
  • 一次只回答一个问题,例如“这次 CALL 的目标是谁?”或“为什么会执行 REVERT?”。
  • 有源码时优先用 Source Debugger 理解业务逻辑;Bytecode Mode 用来验证底层执行细节。
  • 最终是否真的修改了链上数据,仍应回到 State Changes 核对。