Tenderly 模拟为什么失败?核对输入、区块、覆盖状态与调用日志
链小知 · 进阶 · 核验 · 依官方文档整理
围绕已有模拟结果或待检查的输入,重建 network/from/to/input/value/gas/区块条件,定位失败调用;明确覆写状态后的成功不能证明真实账户可执行。
开始前准备
- 已有原交易字段或已保存的 Tenderly 模拟详情;至少包含网络、发送方、目标、calldata、value 与区块线索。
- 本教程按文档检查输入和已有结果,不调用付费 API、不创建模拟、不广播交易。需要执行模拟时,先核对自己账户当前配额与收费。
1. 固定输入并保留原始交易字段
把 network_id、from、to、input、value、gas 与 block_number 写成一份记录。若是 Node RPC,calldata 参数名是 data;REST Simulation API 使用 input,不能混淆两套请求结构。
输入必须对应预期合约和函数。看到 decoded_input 为空,先检查 ABI、地址和 selector,不把“无法解码”直接判为恶意或链上失败。不要在公开记录放 Access Key。
检查结果:字段齐全,API 与 RPC 参数名分清,缺失字段保留未知。
2. 核对区块和当前状态的差别
使用已保存结果中的区块号重建解释范围。latest 表示查询时状态;拿很早的区块模拟新交易,余额、授权、价格或 deadline 都可能不同。
比较两次模拟前先固定网络、区块和输入。只有同一条件下的差异才值得继续查,否则先解释测试条件变化。
检查结果:报告包含模拟区块与观察时间,没有把历史结果当成当前承诺。
3. 单独列出状态覆盖与虚拟余额
查看是否使用 state_objects、余额覆盖或区块覆盖。Tenderly 支持改变合约存储;官方例子甚至用覆盖权限来让原本无权限的地址执行。
因此必须区分原状态结果与假设覆盖后的结果。把授权或余额临时改足后模拟成功,只能说明该假设下可执行,不能说明真实链上地址已有这些权限或资产。
检查结果:所有 overrides 有清单,结论标明原状态或假设状态。
4. 先读执行错误,再沿调用树定位
在 full 模拟详情中检查 error_message、error_info 和 call_trace,定位最早的相关失败子调用及其目标地址。Deadline breached 一类信息先回查时间条件,而不是仅提高 gas。
失败来自子合约时,核对该调用的参数、余额或权限条件;顶层错误可能只是向上传播。没有解码日志时保留原错误和调用路径,不根据一条简短文案编造原因。
检查结果:失败解释指向具体调用、地址和输入条件;推测与已读日志分开。
5. 检查成功返回与实际效果
如果结果显示成功,再核对资产变化、余额变化和 storage/state 变化是否符合目标。函数不 revert 也可能没有完成预期资产动作。
模拟返回中的 transaction_id 或结果标识不是公共链成功回执。查找真实发送结果时,需另外使用原网络的链上交易证据;本次不发送任何交易。
检查结果:模拟状态、模拟效果和实际链上执行分别记录。
6. 整理可重现的排查包
保存脱敏后的完整输入、区块、模拟模式、覆盖项、失败调用和预期效果。接口认证或速率错误与合约执行 revert 分开说明,不把服务请求失败叫做合约失败。
最终写成“在某区块及某覆盖条件下失败于某调用”或“该条件下模拟通过,实际执行待确认”。行情、授权、nonce 等后续变化仍可能改变真实执行结果。
检查结果:另一位研究者能理解条件与错误,无需读取密钥或签名。
怎样确认完成
- 原始字段、区块与覆盖项完整。
- 失败日志定位到具体调用,证据不足处保留未知。
- 模拟结果与真实链上执行明确区分。
常见错误与边界
- 混用 REST input 与 RPC data。
- 隐去 state overrides 后宣称真实地址有资产或权限。
- 把 API 认证错误当合约 revert。
- 看到模拟成功或 transaction_id 就报告主网成交。
- 只改 gas 而没有查失败调用与 deadline。