什么是 revert 和自定义错误?交易失败时,那串提示从哪里来

Revert 会撤销合约状态变化并返回错误数据。本文讲清 require、assert、Custom Error、错误冒泡、低级调用和失败交易排查。

什么是 revert 和自定义错误?交易失败时,那串提示从哪里来

钱包提示“交易失败”,有时写 InsufficientBalance,有时是一串陌生十六进制,还有时什么原因都没有。合约中的 require、revert、assert 和外部调用失败,最终都可能让执行回滚,但错误类型、数据和调用者处理方式并不相同。

理解 revert 的关键有两层:状态发生什么,以及失败信息怎样返回。回滚能撤销本次调用链中的状态变化,却不会把已经消耗的计算和 Gas 全部还给用户。

Revert 是“这次执行不能成立,请撤销状态变化”;错误数据只是解释,不应被当成可信身份或唯一控制流依据。

Revert 时哪些东西会撤销

Solidity 异常会撤销当前调用及其未被捕获子调用的状态变化,并向调用者返回错误
Solidity 异常会撤销当前调用及其未被捕获子调用的状态变化,并向调用者返回错误

Solidity 官方文档说明,状态回退异常会撤销当前调用及子调用的状态变化,并把失败传给调用者。若子调用异常未被捕获,通常会向上冒泡,使更外层也失败。交易收据最终标记失败,成功日志也不会留下。

但调用前已经存在的链上状态不会被抹掉;用户为失败交易消耗的 Gas 通常仍支付给执行者。链下系统已经发送的通知或外部操作也不会被 EVM 自动撤销。

require、revert 和 assert 的区别

方式典型用途常见错误数据
require输入、权限和外部条件空、Error(string) 或自定义错误
revert主动终止复杂分支字符串或自定义错误
assert检查内部不变量Panic(uint256)

assert 失败通常提示程序内部假设被破坏,不应用来处理普通用户输入。Solidity 0.8 还会在溢出、除零、数组越界等情况产生带代码的 Panic。

字符串错误怎样编码

revert("reason") 或 require(condition,"reason") 通常返回内置 Error(string) 的 ABI 编码:四字节选择器加动态字符串数据。字符串便于人读,但越长会增加部署或执行相关成本。

Custom Error 为什么更省

自定义错误用四字节选择器和 ABI 编码参数描述失败,可携带结构化上下文
自定义错误用四字节选择器和 ABI 编码参数描述失败,可携带结构化上下文

自定义错误例如 InsufficientBalance(available,required),返回数据像调用同名函数:选择器是错误签名哈希前四字节,后面是参数。无参数错误只需很少数据,详细解释可写在 NatSpec 中,不必把长字符串存进字节码。

类型优势注意
空 revert数据最少用户难以定位
Error(string)直接可读成本较高、难结构化
Custom Error省数据、参数明确需要正确 ABI 解码
Panic区分内部异常类别通常代表不变量或运行时错误

错误为什么会向上冒泡

普通外部函数调用失败时,错误通常传播给上层。上层可通过 try/catch 处理某些外部调用或合约创建失败。但内部调用的 revert 不能用同样方式在本函数里随意捕获。

低级 call 是重要例外

call、delegatecall、staticcall 等低级调用失败时通常返回 success=false 和 returndata,而不是自动抛出。若开发者忽略 success,外层可能继续执行,形成“内部失败、外部却成功”的错误状态。必须检查结果并决定冒泡、恢复还是记录部分失败。

Try/Catch 不等于可以随便吞错

捕获错误适合处理可预期的外部失败和降级,但要明确哪些状态已更新、是否安全继续、用户如何知道部分成功。宽泛 catch 后什么都不做,会隐藏真实故障。重试还要考虑幂等和重复资产操作。

为什么不能完全相信错误数据

Solidity 官方文档提醒,错误数据可以从深层外部调用冒泡,任意合约也能返回看似来自其他合约的错误选择器。错误用于解释失败,不应作为“某可信合约确认了什么”的安全证明,也不宜仅凭错误名称决定敏感控制流。

钱包模拟成功,上链为何仍失败

  • 区块状态、价格、余额或 nonce 在等待期间变化。
  • Gas limit 不足,或实际执行路径更贵。
  • deadline、滑点或允许额度已变化。
  • 模拟节点与提交网络、区块或状态不同。
  • MEV 和交易排序改变了前置条件。

“估算 Gas 失败”意味着什么

钱包会先模拟交易估算 Gas。若当前状态下必然 revert,估算可能失败。提高 Gas limit 通常不能修复权限、余额、滑点或逻辑条件,只对确实因为 Gas 上限不足的情况有帮助。盲目调高前要先解码错误和参数。

开发者安全清单

  1. 用 require 或 custom error 清楚检查输入、权限和状态。
  2. assert 只用于不应被外部输入正常触发的不变量。
  3. 所有低级调用检查 success 与 returndata。
  4. 捕获错误时定义安全降级、事件和用户反馈。
  5. 错误参数避免泄露不应公开的信息。
  6. 前端使用匹配当前实现的 ABI 解码 Error、Panic 和自定义错误。
  7. 测试嵌套失败、空数据、伪造错误、Gas 不足和部分执行。

普通用户遇到失败怎样排查

  1. 确认网络、合约地址、函数、参数和交易 value。
  2. 查看钱包或浏览器解码的错误类型,而非只看网页文案。
  3. 检查余额、Gas 资产、授权、滑点和期限。
  4. 确认协议是否暂停、升级或状态已变化。
  5. 不要反复提交完全相同的高额失败交易。

三个误区

  • “Revert 会退回全部 Gas”:状态回滚,但已执行计算仍有成本。
  • “错误名称证明来源”:深层合约可冒泡或伪造相同错误数据。
  • “加大 Gas 就能解决所有失败”:业务条件不满足时仍会回滚。

Revert 是合约维护原子性和表达失败的重要机制。读懂错误时,先区分状态是否回滚、错误从哪层传来、外层是否捕获,再判断用户能否安全重试。

风险提示:错误解码依赖正确 ABI 和执行上下文,第三方界面可能误译。重复交易前请核验合约、状态、参数和资产变化。本文仅作技术科普,不构成任何投资建议。


本文为链上指南原创内容。技术依据参考 Solidity Error Handling 官方文档与Solidity Custom Errors 文档。