代码 USDT TRC20 自动归集:广播返回 true 不等于上链,六类失败怎么查

2026-09-13 09:01:10

USDT TRC20 自动归集:广播返回 true 不等于上链,六类失败怎么查

场景前提

多地址收款(每个订单/用户一个充值地址)之后,要把散落在各地址的 USDT 归集(sweep)到热钱包。入账方向是「监听 + 过滤」,出账方向(归集)的坑完全不同:资金在你自己手里,但每一笔都是写类智能合约交易,会消耗 Energy 和 Bandwidth,失败还会烧掉一部分 TRX。

前提:自建或托管节点、能拿到发送地址私钥(或签名服务)、TRON 主网(Nile/Shasta 测试网可先验证)。不适用:只做中心化交易所转账、直接把币收到单一热钱包、根本不需要归集的场景。

一条关键事实:广播成功 ≠ 上链成功

调用 /wallet/broadcasttransaction 返回 {"result": true, "txid": "..."},只代表交易进了节点内存池,不代表被打包、更不代表执行成功。业务上判定资金是否真的到热钱包,必须用 /walletsolidity/gettransactioninfobyid(固化块安全路径)查到明确 Receipt:ret.contractRet == SUCCESS

六类失败与排查

排查顺序(官方口径):停止重试 → 用 txid 查回执确认错误类型 → 重新模拟同一调用 → 查询发送地址当前 Energy/Bandwidth/TRX → 补资源或改参数 → 最后才重发。不要在条件未变化时反复点「发送」。

1. OUT_OF_ENERGY(链上 Failed,最常见)

  • 含义:执行所需 Energy 超过本笔可用上限。上限 = fee_limit / sunPerEnergy 与「质押 Energy + 可燃烧 TRX 换算 Energy」两者取较小值。
  • fee_limit 单位是 sun:1 TRX = 1,000,000 sun。把 fee_limit 写成 100(本意 100 TRX)实际只授权 100 sun = 0.0001 TRX,交易会立即 OUT_OF_ENERGY。主网上限 15,000 TRX(链参数 #47 getMaxFeeLimit),别硬编码,用 /wallet/getchainparameters 取当日值。
  • Energy 单价当前主网 0.0001 TRX/Energy(链参数 #11 getEnergyFee)。
  • USDT 合约受动态 Energy 模型影响:向已有 USDT 余额地址转账约 64,000 Energy;向零余额地址(首次 SSTORE 开槽 20,000 vs 更新 5,000)约 130,000 Energy。同一地址同一金额在不同时间消耗也会有小幅起伏,惩罚乘数每个维护周期(约 6 小时)重置。
  • 修复:用 /wallet/triggerconstantcontractenergy_used,或 /wallet/estimateenergyenergy_required(自建节点需开 vm.estimateEnergy=truevm.supportConstant=true,公共 RPC 不一定支持),乘缓冲系数后设 fee_limit

2. BANDWITH_ERROR(广播前就失败)

  • 普通交易主要消耗 Bandwidth;带宽不足时按 0.001 TRX/Bandwidth(链参数 #3)燃烧 TRX。注意它不表示 TVM 执行阶段的 Energy 不足,两者是不同阶段的失败。
  • 免费带宽每天 600。创建新账户 1 TRX 账户创建费 +(带宽不足时)0.1 TRX;通过智能合约激活新地址额外 25,000 Energy。

3. 广播返回 true 但迟迟未打包

  • 交易进了内存池,但没传播到出块 SR。单笔交易默认有效期约 60 秒,过期即作废。
  • 处理:在有效期内把已签名的完整交易对象提交到其他多个同步节点重传——TRON 按 txID 幂等去重,重复广播同一笔无害,还能提高传播概率。
  • 过期后必须基于最新区块和时间戳重建交易再签名,不要复用旧 txid / raw_data

4. TRANSACTION_EXPIRATION_ERROR

  • 默认 60 秒有效期,构建 → 本地签名 → 广播三步之间拖延太久就会命中。批量归集脚本一次签几十笔时最容易踩。
  • 公共 API 无法调大该窗口,只能在 60 秒内完成广播;自建节点可改 config.conftrx.expiration.timeInMilliseconds

5. SIGERROR

  • 签名验证未通过:私钥与发送方地址不匹配,或私钥 hex 格式错误。

6. SERVER_BUSY

  • 节点内存池满(默认约 2,000 笔未处理)。换节点或等待;自建节点可调 config.conf 内存池上限。

收款地址侧的前置条件

  • TRON 地址本地生成后,链上可能没有账户记录。归集前要确认地址「已激活」:查 TRONSCAN 看账户是否存在、TRX 余额、资源、代币余额四项,别把「USDT 余额为 0」等同于「未激活」。
  • 地址未激活时,归集要先激活(成本见上),或换用不依赖该地址发起的方案。
  • 只认官方 USDT 合约 TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t(decimals=6),避免假 USDT 地址上的余额被误归集。

资源策略三选

  • 质押 TRX(Stake 2.0):按质押份额拿每日 Energy 配额,24 小时内恢复,长期高频划算。
  • 租 Energy(如 JustLend Energy 租赁市场):按需付费,跑量波动时弹性好。注意确认委托对象就是发送地址——多账户钱包很容易租到另一个 TRON 地址上。
  • 直接燃烧 TRX:省事,但单价固定 0.0001 TRX/Energy,量大了成本最贵。

归集工程实践清单

  • 每次广播后先记 txid 与 fee_limit,再轮询 /walletsolidity/gettransactioninfobyid 判 SUCCESS;不要以 result: true 结单。
  • fee_limitgetchainparameters 动态取上限,按模拟结果设值,不要抄网上的固定 Energy 数字。
  • 归集失败只重新构建「新交易」,不复用旧 txid。
  • 同一笔归集做幂等(归集单号 + txid 唯一键),避免重试造成重复转账。
  • 先在 Nile/Shasta 测试网跑通失败与成功两类样例。

项目/文档地址

  • 广播与 RPC 错误:
  • 智能合约错误(OUT_OF_ENERGY / REVERT / OUT_OF_TIME):
  • 资源支付与能量分摊:
  • fee_limit 设定:
  • 获取能量与带宽:
  • FAQ(Energy 估算接口):
  • 关联站内 #6396 入账五道过滤。

数值口径与实测提醒

上面的 Energy 数值(64,000 / 130,000)、0.0001 TRX/Energy、0.001 TRX/Bandwidth,以及 60 秒有效期、2,000 笔内存池上限、15,000 TRX 的 fee_limit 上限,均为官方文档与链参数口径,随 SR 提案会变。上线前用 /wallet/getchainparameters 拉当日值,并在 Nile/Shasta 上跑一遍归集样例自行验证。

推荐文章

程序员茄子在线接单