TRON 账户未激活:它挡住什么,又怎么解开
在网络给一个地址建起账户之前,它只是一对密钥。“TRON 账户未激活”说的就是这件事,而这个答案会让人意外:地址格式完全正确,钱包能显示它,二维码扫得出来,而链从来没听说过它。对一个付款团队来说,这个状态会在两个不同的地方冒出来——发钱的那个钱包,和被付钱的那个地址——而其中只有一个是拒绝。
激活到底是什么
TRON 地址是离线从一对密钥推出来的。造它的时候没有向任何地方登记过什么,网络只有在某笔交易为它建起一条账户记录时才知道它的存在。我们自己的检查在这一点上非常字面:我们向节点要这个地址背后的账户,一个空答复就是 activated: false 的含义。不是“我们没找到它”,也不是“它看着挺新”——那里根本没有账户。
账户诞生的寻常途径是一笔转入的 TRX:往这个地址转 TRX,网络在办这笔转账的同时把账户建起来。建账户不是免费的。它花多少是一个链上参数,不是值得印在页面上的一个数,所以请从网络上读它,而不是从任何可能已经过时一年的地方读。这里要紧的是:它就是一笔寻常交易,办完之后这个地址就和别的账户一样了。
它挡住什么:那个要发钱的钱包
能量是代理给一个账户的,而账户根本不存在时,就没有东西可以代理过去。这就是那个拒绝的全部。mode A 或 mode C 的订单把这样一个钱包写成 from,拿回的是 422 invalid_address,details.reason 为 inactive_wallet,同时带着 details.index——是这一批里的哪一笔——和 details.address。它是一个付款团队反反复复碰到的四种失败之一,也是订单会当场拒掉、而不是给它定价的那两种之一。
动手写处理逻辑之前,这个拒绝有三条性质要紧。
它不消耗 Idempotency-Key。这一族里的拒绝都不消耗:请求本身是好的,只是在那些地址上我们接不下来。等钱包存在之后,同一个键配上逐字节相同的请求体,是一次有效的重放而不是冲突,所以您那边什么都不用重新推导。
它不是从缓存里答出来的。地址状态在我们这里是有缓存的,但在因此拒掉一个订单之前,激活状态会绕过缓存重读一遍——几秒钟前刚激活的钱包会被立刻接受,而不是要等某条缓存到期。请立刻重试;这里没有什么需要等过去的东西。
在 mode B 里它根本不存在。那里的交易到达时已经由转出地址的所有者签过名,所以账户按构造就在那里,这项检查会变成一个自己已经答完了的问题。
未激活不等于格式错误
有两种答复带着同一个错误码,意思却正好相反。400 invalid_address,完全没有 details.reason,说的是这串字符的形状:它不是一个 TRON 地址,而链上发生什么都不会让它变成一个。422 invalid_address 带着一个 details.reason,说的是一个完全合格的地址的状态,一笔交易就能改掉。一条流水线要是把两者一视同仁,就会对前者永远重试,对后者干脆放弃,所以值得拿来分支的是 details.reason 在不在,而不是那个两者共用的错误码。
它要花什么:那个被付钱的地址
在收款这一侧什么都不会被拒;一个未激活的收款方是一个价格。转给 USDT 余额为零的地址,除了搬动代币,还得把收款方的代币账户开出来,那要大约 131,000 单位能量,而已经持有 USDT 的地址大约要 65,000(这两个数为什么不同)。按协议价算,那就是烧掉 13.1 TRX 而不是 6.5 TRX,而在一轮给新客户的付款里,整批里的大部分可能同时处在这个状态——租下那份能量要花多少钱跟着市场动,燃烧不动。
到这里两个问题分了岔,而这两个名字又特别容易被混成一个,所以请说得精确些。activated 问的是网络有没有这个地址的账户。holds_usdt 问的是它的 USDT 余额是不是大于零。价格跟着第二个走:只要地址不持有 USDT,不管激活没激活,kind 都是 double。而 inactive_recipient 这个警告跟着第一个走。于是,一个多年前就激活、只是从来没被人用 USDT 付过钱的地址,不带任何警告,却照样按 double 定价;而一个从来没有激活过的地址仍然可以持有 USDT——TRC-20 余额住在代币合约自己的存储里,所以一笔转入建起的是余额而不是账户——它会被定价成 single,同时带上 inactive_recipient 警告。这两个问题看上去是一个,直到某个地址给出了两个不同的答案。
查一个地址,和查五百个
查单个地址,GET /v1/address-check 一次调用就把这两个问题连同另外两个一起答了:activated、holds_usdt、blacklisted、is_contract,以及由它们推出的 expected_kind。
查一整批,POST /v1/estimate 收最多 500 笔转账,而且什么都不拒——它注意到的一切都作为该笔的 warnings 回来,包括那些会让订单停下的发现。收款方总是会被查。转出方只有在您写了的时候才被查:估价项里的 from 是可选的,它不改动任何价格,买到的唯一东西就是那个答案。那里的 inactive_sender,和订单以 422 报出来的是同一个发现,只不过它到达的时候什么都还没押上去。
POST /v1/estimate
{"transfers":[{"from":"TN3W…","to":"TMu1…"}]}
# 一笔可以带的 warnings:blacklisted、inactive_recipient、
# contract_recipient、inactive_sender、blacklisted_sender
这两个调用都不预留、不扣费,所以一份名单可以在限额允许的范围内随便筛。它们俩都没有排练的,是订单本身——那在每一种模式里花的都是真钱。
解法
往这个地址转一点 TRX,等那笔交易确认,然后用同一个键、同一串字节把订单重放一遍。没有注册这一步,不用申请,也不用再给我们打一个电话。唯一值得写下来的先后规则是:钱包必须在下单之前存在,而不是在转账之前——能量是交付给一个账户的,所以账户得在那里接。
对一个您想让它一直保持有能量的钱包,同样的要求成立。一条 Auto-refill 规则在未激活的地址上会被拒,原因词一样;在合约地址上则是 contract_wallet——合约的能量消耗是另一套机制,规则没有东西可以拿来定量。先激活,再设规则。