钱包显示已确认。浏览器页面上是一个绿色对勾。而代币并不在账户里。没有任何东西卡住,也不需要重发:状态字段回答的问题比你正在问的那个更窄。本文讲清楚这个字段证明了什么、漏掉了什么,以及你真正要找的答案记录在哪里。
成功状态到底证明了什么
EIP-658 在交易收据里放进一个状态码,并用一句话定义了它的两个取值:0 代表失败,源于任何可能导致该交易或顶层调用回退的操作;1 代表成功。区块浏览器把这个 1 渲染成一个对勾。
这条定义管到哪里,才是关键。状态码描述的是顶层调用。它报告最外层那段执行没有回退,除此之外什么都不报告:不报告某个代币余额变了,不报告你想转的那笔数额动了,也不报告你调用的合约做了你要求它做的事。状态为 0 的收据就是浏览器标记为 reverted 的那种。状态为 1 的收据排除了这一种,然后就到此为止。
下面每一种情形都住在这道缝隙里。链把发生的事完整记了下来。而页面顶部那个一个词的摘要,回答的是另一个问题。
| 你在问的问题 | 答案记录在哪里 |
|---|---|
| 它进区块了吗 | 收据上的区块号 |
| 最外层调用避开回退了吗 | 收据状态码 |
| 这个代币余额变了吗 | 日志里的转账事件 |
| 我的用户操作办成事了吗 | 它自己那条事件上的成功标志 |
代币转账为什么可以不回退地失败
ERC-20 代币标准把 transfer 声明为一个返回布尔值的函数,而标准本身对后果说得很硬:调用方必须处理 returns bool success 返回的 false,调用方不得假定 false 永远不会被返回。失败是允许以返回值的形式抵达的,不必以回退的形式。
当一个代币用这种方式报告失败时,结果取决于调用它的那个合约。检查这个布尔值的调用方可以在 false 上回退,收据回来时状态是 0。丢掉这个布尔值的调用方继续往下走,最外层调用完成,收据说成功,而余额从来没有动过。
返回值还有第二重差异。OpenZeppelin 的 SafeERC20 库这样描述自己:它是包在 ERC-20 操作外面、在代币合约返回 false 时抛出的封装器;并补充说完全不返回值的代币同样受支持,不回退的调用被视为成功。一个专门为归一这些返回值而写的库,本身就是这些返回值并不统一的直接证据。
合约有意吞掉的失败
一个合约可以调用另一个合约,并事先决定不跟着它一起失败。Solidity 的 try 与 catch 结构,以及那种把成功标志交回来、而不把回退往上传的低层调用,存在的意义就是让执行能越过内层失败继续走下去。路由器、批处理器和中继器有意用这一点:一批里的某一条腿失败了,其余的腿照样结清,而这笔交易整体上是成功的。
从收据上看,这与上一种情形无法区分。内层调用失败了,外层调用把这个失败吸收掉了,状态字段记录的是外层结果。内层失败仍然在链上,但它待在执行追踪里、待在日志里没有的那些东西里,而不在状态字段里。
授权额度不足是通往这个形态的一条路。用 transferFrom 搬走你代币的合约,花的是你用代币授权设下的那个额度。当额度小于被拉走的数额时,内层调用失败,而整笔交易是否跟着失败,由调用方决定,不由代币决定。
到账的数额小于发出的数额
第三种情形里根本没有失败,而算术照样对不上。代币合约可以在它自己的 transfer 函数里扣掉一部分,记给接收方的比发送方交出去的少。发出 5,000 单位一个每次转账收 3% 的代币,到账的是 4,850。收据说成功,转账事件也在,而事件里的那个数不是当初输进去的那个数。
变基代币从相反的方向产生一种相关的错位。它们的余额是被合约改写的,不是被一次转账搬走的,所以余额可以在背后没有任何转账事件的情况下发生变化。拿钱包显示去和交易历史里的数额对账,对这类代币是对不上的,而两边的读数都没有错。
paymaster 交易失败这句提示是什么意思
在账户抽象之下,你提交的并不是一笔交易。ERC-4337 定义了一个 UserOperation 对象,它有自己的内存池,还有一个 bundler 把这些对象打包进一笔发往 EntryPoint 合约的普通交易。paymaster 是一个同意替发送方支付这笔交易的合约。一句点了 paymaster 名字的失败提示,覆盖的是两种情形,它们之间除了失败这个词以外没有共同点。
第一种是验证阶段的拒收。ERC-4337 要求:如果任何 validateUserOp 调用失败,handleOps 必须至少跳过那一个用户操作的执行;它还要求 bundler 把无效的操作从自己的内存池里拒掉,而不是把它们放上链。一个不肯赞助这次调用、押金余额太少、或者自己验证不过的 paymaster,会把这个操作推进这一支。什么都没有被纳入,什么都没有被扣费,也没有哈希可查,因为失败发生在链看见这个操作之前。
第二种发生在纳入之后,而它正是那个会产生对勾的情形。规范说,一旦验证通过,执行就会发生且只发生一次,并且手续费的支付也得到保证。随后 EntryPoint 会为每个用户操作发出一条事件,事件里带着一个成功标志,其文档写明:发送方交易成功为 true,回退为 false;旁边还有由账户或 paymaster 实际支付的金额。这笔打包交易成功了,paymaster 被扣了费,而你那个操作上的标志是 false。三件事同时成立。
| 失败发生在哪 | 是否进了区块 | 谁付的钱 | 有什么可查 |
|---|---|---|---|
| 验证阶段,纳入之前 | 否 | 无人 | 来自 bundler 或钱包的一条报错,链上没有记录 |
| 执行阶段,纳入之后 | 是 | 账户或 paymaster | 一笔成功的交易,而它的用户操作事件带着一个为 false 的成功标志 |
不看状态字段,该看哪里
代币的移动记录在日志的事件里。浏览器的代币转账视图就是这些事件的一种呈现,所以如果你的地址没有出现在其中任何一条里,那个代币就没有动过,不管状态怎么说。日志才是核对的对象,状态只是摘要。
三个问题就能把事情办完。有没有一条牵涉到你地址的转账事件,还是这一栏是空的?如果有,它的数额是不是发出去的那个数额?发出这条事件的合约,是不是你要的那个代币合约,在不在你要的那条网络上。
还有第四种可能能挺过上面三关:转账完全按指令发生了,只是找错了地方。同一句助记词派生出的另一个地址、另一条网络上的同一个地址、以及一个不手动添加合约就不显示的代币,都会在一张完好的收据旁边给你一块空屏。
小结
收据状态是关于最外层调用的一句陈述,仅此而已。成功的意思是这笔交易没有回退。它从来不意味着某个代币动了、动的是对的数额,或者合约用它做了你想要的事。这些事实住在事件日志里,而在 ERC-4337 之下,住在一个与承载它的那笔交易的状态相分离的成功标志里。
当一笔转账消失在绿色对勾背后时,按层次逐级往下查:先看有没有转账事件,再看它的数额,最后看代币合约与网络。想继续打基础,请关注 Bitbase 学院的更多内容。
相关阅读
币贝上与本主题相关的其他文章:
风险披露:本文为 Bitbase(币贝)学院的科普内容,仅供教育与信息参考,不构成任何投资、交易、税务或财务建议。加密资产波动剧烈,请自行评估风险。本文撰写于 2026 年 9 月,请以官方最新信息为准。
参考资料
[1] Ethereum Improvement Proposals,EIP-20: Token Standard(transfer 函数及其布尔返回值) eips.ethereum.org
[2] Ethereum Improvement Proposals,EIP-658: Embedding transaction status code in receipts(交易收据里的状态字段) eips.ethereum.org
[3] Ethereum Improvement Proposals,ERC-4337: Account Abstraction Using Alt Mempool(验证、bundler 与 paymaster) eips.ethereum.org
[4] eth-infinitism,account-abstraction,contracts/interfaces/IEntryPoint.sol,标签 v0.7.0(UserOperationEvent 的声明) raw.githubusercontent.com
[5] OpenZeppelin,openzeppelin-contracts,contracts/token/ERC20/utils/SafeERC20.sol,标签 v5.1.0(库的文档注释) raw.githubusercontent.com






