什么是 revert 和自定义错误?交易失败时,那串提示从哪里来
Revert 会撤销合约状态变化并返回错误数据。本文讲清 require、assert、Custom Error、错误冒泡、低级调用和失败交易排查。
钱包提示“交易失败”,有时写 InsufficientBalance,有时是一串陌生十六进制,还有时什么原因都没有。合约中的 require、revert、assert 和外部调用失败,最终都可能让执行回滚,但错误类型、数据和调用者处理方式并不相同。
理解 revert 的关键有两层:状态发生什么,以及失败信息怎样返回。回滚能撤销本次调用链中的状态变化,却不会把已经消耗的计算和 Gas 全部还给用户。
Revert 是“这次执行不能成立,请撤销状态变化”;错误数据只是解释,不应被当成可信身份或唯一控制流依据。
Revert 时哪些东西会撤销

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 为什么更省

自定义错误例如 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 上限不足的情况有帮助。盲目调高前要先解码错误和参数。
开发者安全清单
- 用 require 或 custom error 清楚检查输入、权限和状态。
- assert 只用于不应被外部输入正常触发的不变量。
- 所有低级调用检查 success 与 returndata。
- 捕获错误时定义安全降级、事件和用户反馈。
- 错误参数避免泄露不应公开的信息。
- 前端使用匹配当前实现的 ABI 解码 Error、Panic 和自定义错误。
- 测试嵌套失败、空数据、伪造错误、Gas 不足和部分执行。
普通用户遇到失败怎样排查
- 确认网络、合约地址、函数、参数和交易 value。
- 查看钱包或浏览器解码的错误类型,而非只看网页文案。
- 检查余额、Gas 资产、授权、滑点和期限。
- 确认协议是否暂停、升级或状态已变化。
- 不要反复提交完全相同的高额失败交易。
三个误区
- “Revert 会退回全部 Gas”:状态回滚,但已执行计算仍有成本。
- “错误名称证明来源”:深层合约可冒泡或伪造相同错误数据。
- “加大 Gas 就能解决所有失败”:业务条件不满足时仍会回滚。
Revert 是合约维护原子性和表达失败的重要机制。读懂错误时,先区分状态是否回滚、错误从哪层传来、外层是否捕获,再判断用户能否安全重试。
风险提示:错误解码依赖正确 ABI 和执行上下文,第三方界面可能误译。重复交易前请核验合约、状态、参数和资产变化。本文仅作技术科普,不构成任何投资建议。
本文为链上指南原创内容。技术依据参考 Solidity Error Handling 官方文档与Solidity Custom Errors 文档。