Compte TRON non activé : ce que cela bloque et comment y remédier
Une adresse n'est qu'une paire de clés tant que le réseau n'a pas de compte pour elle. Tout le « compte TRON non activé » tient là-dedans, et c'est la réponse qui surprend : l'adresse est bien formée, le portefeuille l'affiche, le QR code se scanne, et la chaîne n'en a jamais entendu parler. Pour un service de paiements, cet état se présente à deux endroits différents — le portefeuille qui envoie et l'adresse qu'on paie — et un seul des deux est un refus.
Ce qu'est réellement l'activation
Une adresse TRON est dérivée d'une paire de clés hors ligne. Rien n'est enregistré nulle part au moment où on la fabrique, et le réseau n'en apprend l'existence que lorsqu'une transaction crée un enregistrement de compte pour elle. Notre propre vérification est littérale là-dessus : nous demandons à un nœud le compte derrière l'adresse, et une réponse vide, voilà ce que veut dire activated: false. Pas « nous n'avons pas réussi à le trouver », pas « il a l'air neuf » — il n'y a pas de compte là.
La façon ordinaire dont un compte vient à l'existence est un transfert de TRX entrant : envoyez des TRX à l'adresse et le réseau crée le compte pendant qu'il exécute ce transfert. Créer un compte n'est pas gratuit. Ce que cela coûte est un paramètre de la chaîne plutôt qu'un chiffre qui mérite d'être imprimé sur une page : lisez-le donc sur le réseau plutôt que quelque part qui pourrait avoir un an de retard. Ce qui compte ici, c'est que c'est une transaction ordinaire, et qu'ensuite l'adresse est un compte comme un autre.
Ce que cela bloque : le portefeuille qui va envoyer
L'énergie est déléguée à un compte, et il n'y a rien à quoi déléguer tant que le compte n'existe pas. Tout le refus tient là-dedans. Une commande en mode A ou en mode C nommant un tel portefeuille comme from revient en 422 invalid_address avec details.reason à inactive_wallet, accompagné de details.index — quel transfert du lot — et de details.address. C'est l'un des quatre échecs qu'un service de paiements rencontre encore et encore, et l'un des deux qu'une commande refuse d'emblée au lieu de les tarifer.
Trois propriétés de ce refus comptent avant d'écrire le gestionnaire.
Il ne consomme pas l'Idempotency-Key. Aucun refus de cette famille ne la consomme : la requête est bien formée, et nous ne pouvons simplement pas la prendre sur ces adresses-là. Une fois le portefeuille existant, la même clé avec un corps identique octet pour octet est un rejeu valide et non un conflit : rien n'a donc à être redérivé chez vous.
Il ne sort pas d'un cache. L'état d'une adresse est mis en cache chez nous, mais l'activation est relue à côté du cache avant qu'une commande soit refusée pour elle — un portefeuille activé il y a quelques secondes est accepté tout de suite, et non une fois qu'une entrée a expiré. Réessayez immédiatement ; il n'y a aucun délai à laisser passer.
Il n'existe pas en mode B. Là, la transaction arrive déjà signée par le propriétaire de l'adresse émettrice : le compte est donc présent par construction, et la vérification poserait une question qui s'est répondue toute seule.
Non activée n'est pas mal formée
Deux réponses portent le même code d'erreur et veulent dire des choses opposées. 400 invalid_address, sans aucun details.reason, parle de la forme de la chaîne de caractères : ce n'est pas une adresse TRON, et rien de ce qui se passe sur la chaîne n'en fera une. 422 invalid_address avec un details.reason parle de l'état d'une adresse parfaitement valable, qu'une seule transaction corrige. Un pipeline qui les traite pareil rejoue la première indéfiniment et abandonne la seconde : c'est donc sur la présence de details.reason qu'il vaut la peine de brancher, plutôt que sur le code d'erreur, que les deux partagent.
Ce que cela coûte : l'adresse qu'on paie
Du côté réception, rien n'est refusé ; un destinataire non activé est un prix. Un transfert vers une adresse sans solde USDT doit ouvrir le compte de jeton du destinataire en plus de déplacer les jetons, ce qui prend environ 131,000 unités d'énergie contre environ 65,000 pour une adresse qui détient déjà des USDT (pourquoi les deux diffèrent). Au tarif du protocole, cela fait 13.1 TRX de combustion au lieu de 6.5 TRX, et dans une campagne de paiements vers de nouveaux clients la plus grande partie du lot peut être dans cet état à la fois — ce que coûte la location de cette énergie bouge avec le marché, la combustion non.
Ici les deux questions se séparent, et les noms invitent à les confondre : soyons donc exacts. activated demande si le réseau a un compte pour l'adresse. holds_usdt demande si son solde USDT est supérieur à zéro. Le prix suit la seconde : kind vaut double dès que l'adresse ne détient aucun USDT, activée ou non. L'avertissement inactive_recipient suit la première. Ainsi une adresse activée il y a des années mais qui n'a simplement jamais été payée en USDT ne porte aucun avertissement et reste tarifée double ; et une adresse jamais activée peut malgré tout détenir des USDT — un solde TRC-20 vit dans le stockage propre du contrat du jeton, donc un transfert entrant crée un solde et non un compte — ce qui la tarife single et déclenche en même temps l'avertissement inactive_recipient. Les deux ressemblent à une seule question jusqu'au jour où une adresse y répond différemment.
Vérifier une adresse, et en vérifier cinq cents
Pour une seule adresse, GET /v1/address-check répond aux deux questions et à deux autres en un appel : activated, holds_usdt, blacklisted, is_contract, et l'expected_kind qui en découle.
Pour un lot, POST /v1/estimate prend jusqu'à 500 transferts et ne refuse rien — tout ce qu'il remarque revient en warnings sur l'élément, y compris des constats qui arrêteraient une commande. Les destinataires sont toujours examinés. L'expéditeur ne l'est que si vous en nommez un : from est facultatif sur un élément d'estimation et ne change aucun prix, et la seule chose qu'il achète, c'est la réponse. inactive_sender y est le même constat qu'une commande signale par un 422, mais il arrive alors que rien n'est en jeu.
POST /v1/estimate
{"transfers":[{"from":"TN3W…","to":"TMu1…"}]}
# avertissements qu'un élément peut porter : blacklisted, inactive_recipient,
# contract_recipient, inactive_sender, blacklisted_sender
Ni l'un ni l'autre ne réserve ou ne débite quoi que ce soit : une liste peut donc être passée au crible aussi souvent que les limites de débit le permettent. Ce que ni l'un ni l'autre ne répète, c'est la commande elle-même, qui dépense de l'argent réel dans tous les modes.
Le remède
Envoyez des TRX à l'adresse, laissez cette transaction se confirmer, et rejouez la commande avec la même clé et les mêmes octets. Il n'y a pas d'étape d'inscription, pas de demande à déposer et pas de second appel chez nous. La seule règle d'ordre qui mérite d'être écrite, c'est que le portefeuille doit exister avant la commande, pas avant le transfert : l'énergie est livrée à un compte, il faut donc que le compte soit là pour la recevoir.
La même exigence vaut pour un portefeuille que vous voulez garder approvisionné. Une règle Auto-refill est refusée sur une adresse non activée avec ce même mot de raison, et sur une adresse de contrat avec contract_wallet — la consommation d'énergie d'un contrat fonctionne autrement, et la règle n'a rien à dimensionner. Activez d'abord, réglez la règle ensuite.