交易失败时,RPC常返回一串0x开头的data。前4字节决定错误类型,后续字节按ABI编码参数。自定义错误与函数调用使用同一种“选择器加参数”结构,因此只要有可信ABI就能还原名称和参数;没有ABI时只能做候选匹配,不能把猜测当成来源证明。
先拆开三种revert数据
- 自定义错误数据按同名同参数函数调用的方式ABI编码:前4字节是错误签名选择器,后面是参数编码。
- 无参数自定义错误只需4字节返回数据,通常比携带长字符串的Error(string)更节省Gas。
- revert data会沿外部调用链冒泡,任何合约都能构造匹配某错误签名的数据,因此不能把解码名称当成来源认证。
选择器怎样得到
例如error InsufficientBalance(uint256 available,uint256 required),规范签名不含参数名和空格。对签名做keccak256并取前4字节,再按两个uint256解码后64字节。错误不能靠显示名称唯一识别,4字节空间存在碰撞,必须结合目标ABI与调用上下文。
| 错误类型 | 常见前4字节 | 后续数据 |
|---|---|---|
| Error(string) | 0x08c379a0 | 动态字符串ABI编码 |
| Panic(uint256) | 0x4e487b71 | 32字节panic code |
| CustomError(…) | keccak签名前4字节 | 按声明类型ABI编码 |
| 空revert | 无 | 无可解码原因 |
先区分低级调用的返回
高级外部调用通常自动冒泡revert;call、delegatecall和staticcall则返回success=false与原始bytes。调试器若只展示外层包装错误,内层data可能被再编码。应沿调用trace找到最早失败帧,再用该帧可能的ABI集合解码。
拿到十六进制错误后怎么做
- 保存完整revert data和失败调用calldata
- 先匹配Error、Panic和空数据
- 从已验证合约ABI计算custom error selector
- 按参数类型和动态偏移解码
- 用trace确认错误最初出现的调用帧
错误数据可以被伪造
任何合约都能返回与某个自定义错误相同的字节,内层错误也可向外冒泡。看到OwnableUnauthorizedAccount不证明错误一定来自OpenZeppelin Ownable。错误用于解释失败,不适合充当授权或控制流依据;业务安全判断必须读取状态和验证调用者。
事件ABI可结合topics和data解码比较;执行结果先读交易回执,合约地址与源码来源按合约地址核验确认。
错误名称不能证明来源
自定义错误更省Gas不等于永远只占4字节;带多个参数仍会增加返回数据。Panic也不等于合约被黑,它可能来自数组越界、除零或算术检查。最可靠的报告会同时写出选择器、原始data、ABI来源和区块上下文。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。