智能账户交易卡在Bundler:3步定位失败原因
智能账户交易卡在 Bundler,通常卡在三个环节——模拟验证失败、Gas 估算不足、或 Nonce 冲突。按下面三步定位,比盲目重发有效率得多。
1. 第一步:查 Bundler 返回的 AA 错误码
Bundler 在收到你的 UserOperation 后,会先做链下模拟验证(Simulation)。ERC-4337 规范为 EntryPoint 的报错保留了 AA 前缀的错误码。如果你在调用 sendUserOperation 时抛出 UserOperationExecutionError,错误信息里通常会包含一个 AA 开头的错误码。
步骤1:捕获并解析 sendUserOperation 返回的错误
做什么:从你的代码或钱包日志里,找到 Bundler 返回的具体错误信息。
怎么做:在代码的
catch块里打印完整错误对象。如果用的是 permissionless.js 或 aa-sdk,可以先用debugUserOperation替代sendUserOperation,它会在控制台输出详细的 AA 错误码和原因。做到什么程度算完成:你拿到了一个具体的 AA 错误码(如
AA13、AA23、AA31、AA40等)。
常见 AA 错误码速查:
| 错误码 | 含义 | 可能原因 |
|---|---|---|
| AA13 | InitCode 失败 | 账户工厂部署失败,检查 initCode 或增加 verificationGasLimit |
| AA21 | 账户未预存 Gas 费 | 智能账户余额不足以支付 Gas,或 Paymaster 未正确配置 |
| AA23 | validateUserOp 回滚 | 签名无效、Nonce 过期或账户验证逻辑本身报错 |
| AA25 | Nonce 无效 | Nonce 不匹配当前链上状态——用 account.getNonce() 重新获取 |
| AA31 | Paymaster 存款不足 | 你的 Gas Tank 或 Paymaster 账户余额不够 |
| AA33 | Paymaster 验证失败 | Paymaster 配置错误,或 Gas Tank 余额不足 |
| AA40 | verificationGasLimit 超限 | 验证阶段消耗的 Gas 超过了设置的 verificationGasLimit |
| AA41 / AA42 | callGasLimit 过低/过高 | 执行阶段的 Gas 设置不当,尝试让 Bundler 自动估算,不要手动覆盖 |
2. 第二步:查 Gas 估算相关报错(Precheck Failed)
如果 Bundler 在链下模拟阶段就因为"Gas 给少了"而拒绝你的 UserOperation,你会收到 "precheck failed" 类型的错误。
步骤2:检查 Gas 限制参数是否过低或过高
做什么:检查
callGasLimit、verificationGasLimit、preVerificationGas这三个字段。怎么做:
情况A(报错
callGasLimit is X but must be at least XXXX):说明你手动设的callGasLimit太低。解决方案是去掉手动覆盖,让 Bundler 通过eth_estimateUserOperationGas自动估算。情况B(报错
verificationGasLimit is X but must be at most XXX):说明验证逻辑太复杂了,超出了 Bundler 的限额。尝试简化签名逻辑,或联系 Paymaster 服务方确认上限。情况C(报错
maxFeePerGas或maxPriorityFeePerGas过低):网络费率波动导致你的估算过时了。用getUserOperationGasPrice获取最新费率,或给估算值加一个 10%-20% 的乘数。
做到什么程度算完成:你确认了是哪个 Gas 参数导致 Precheck 失败,并调整了对应值。
3. 第三步:检查 Nonce 冲突(最隐蔽的坑)
如果你同时向两个不同的 Bundler 发送了相同 Nonce 的 UserOperation,或者你的钱包在本地 Nonce 计数与链上不一致,会发生 Nonce 碰撞——即其中一个 Bundler 的交易上链后,另一个 Bundler 提交的包含相同 Nonce 的交易会被回滚,导致该 Bundler 白白损失 Gas 费,你的交易也卡住了。
步骤3:确认并重置 Nonce
做什么:检查你的账户 Nonce 是否被"吃掉"了。
怎么做:
情况A(怀疑并发提交):用一个工具或脚本查询 EntryPoint 合约上该账户的当前
nonce值。确保你的下一次提交使用的是正确的 Next Nonce。情况B(收到 AA25 错误):在签名前,调用
account.getNonce()获取最新值,而不是用缓存。
做到什么程度算完成:你的提交使用了正确的 Nonce,不再收到 AA25 错误。
前置条件:你正在通过钱包或代码向 Bundler(如 Alchemy、Pimlico、Gelato 的 Bundler 节点)提交 UserOperation。
风险提醒:如果 UserOperation 在链上执行阶段回滚(比如目标合约方法不存在、余额不足),但验证阶段通过了,Bundler 依然会收取 Gas 费。如果 Bundler 因 Nonce 碰撞导致交易被回滚,它只会损失 Gas 费,不会从你账户扣钱,但你需要重新发起 UserOperation。
做完以上排查,怎么确认问题解决了?
在区块浏览器(如 Etherscan)中找到 Bundler 提交的 handleOps 交易,查看内部的 UserOperationEvent 日志 —— 如果 success 字段为 true,说明你的 UserOperation 已执行成功。如果你使用的是 Pimlico 或 Alchemy 的 Bundler,也可以调用 getUserOperationReceipt 直接查询 UserOperationHash 的链上执行状态。



