Solde USDT sur un wallet TON en un seul appel
Lire le solde USDT d'un wallet TON en un seul appel get_jetton_balance : le jetton-wallet est calculé on-chain, aucun indexeur nécessaire.
Le wallet de l'agent est ouvert, get_balance a bien renvoyé quelques GRAM — mais l'USDT affiche zéro. Alors que vous savez pertinemment que des stablecoins sont arrivés sur cette adresse. Ça vous parle ? Le wallet a l'air vide alors qu'il contient 500 USDT. Ce n'est ni un bug ni de l'argent disparu — vous avez simplement interrogé le mauvais contrat. Et c'est exactement là que déraille la plupart des premières intégrations d'un agent IA avec TON : l'agent lit la mauvaise adresse.
Pourquoi le solde USDT n'est pas le solde de votre wallet TON
Sur Ethereum, vous êtes habitué à ce qu'un token ERC-20 « se trouve sur une adresse » : le contrat du token tient une table address → balance, et pour connaître un solde, vous interrogez ce contrat unique. Sur TON, le modèle est différent — et c'est la première source de confusion.
La monnaie native GRAM (ex-Toncoin ; le réseau, lui, s'appelle toujours TON) se trouve directement sur le smart contract de votre wallet — get_balance la lit en une seule consultation de l'état du compte. L'USDT, en revanche, est un jetton — le standard TON pour les tokens fongibles. Et le solde d'un jetton n'est pas stocké sur votre wallet principal, mais sur un petit contrat séparé : le jetton-wallet.
Une analogie : votre wallet TON principal, c'est vous en tant que personne. Le jetton-wallet USDT, c'est un compte séparé ouvert à votre nom par une banque précise (le contrat maître USDT). Demander « combien j'ai de dollars » à la personne elle-même n'a aucun sens — l'argent est sur le compte, pas dans la poche. Chaque paire « propriétaire + jetton » a son propre jetton-wallet : une adresse pour USDT, une autre pour NOT, une troisième pour n'importe quel autre jetton.
La bonne nouvelle : l'adresse de ce jetton-wallet n'a rien d'aléatoire. Elle se déduit de manière déterministe de deux éléments :
- l'adresse du propriétaire (votre wallet TON habituel,
EQ…/UQ…) ; - l'adresse du contrat maître du jetton (jetton master — pour USDT, c'est un contrat unique et fixe).
La mauvaise nouvelle : pour calculer honnêtement cette adresse, il faut aller interroger le contrat maître, appeler sa méthode get, puis lire l'état du jetton-wallet. À la main, cela fait plusieurs étapes — et ce sont précisément elles qui font trébucher les intégrations.
get_jetton_balance : un seul appel au lieu d'un indexeur
D'habitude, on règle ce problème de deux façons — et les deux sont pénibles :
- Votre propre indexeur. Monter un nœud, indexer les transferts de jettons dans une base, la maintenir à jour. Cher et fragile pour un seul nombre — et les données sont toujours légèrement en retard sur la chaîne.
- Une API HTTP publique. Vous butez vite sur les limites : sans clé, c'est de l'ordre d'une requête par seconde, et au-delà vous récoltez un bon vieux
HTTP 429 Too Many Requests. Sans compter que vous dépendez de l'indexation d'un tiers et de sa profondeur.
L'outil get_jetton_balance de TONNode élimine les deux problèmes. Vous lui passez :
- l'adresse du propriétaire — le wallet TON habituel de l'utilisateur (
EQ…/UQ…) ; - l'identifiant du jetton — USDT, par exemple.
Ensuite, le serveur fait tout le travail lui-même, on-chain :
- il appelle la méthode get du contrat maître du jetton, qui renvoie, à partir de l'adresse du propriétaire, l'adresse de son jetton-wallet (la fameuse dérivation déterministe) ;
- il lit le solde sur ce jetton-wallet et vous le renvoie.
Aucun indexeur tiers, aucune base, aucune désynchronisation — rien qu'une lecture directe de l'état du réseau via les méthodes get des contrats.
TONNode est un serveur MCP hébergé pour TON. MCP (Model Context Protocol) est le standard par lequel les agents IA (Claude, Cursor, ChatGPT/Codex et n'importe quel client MCP) appellent des outils externes. Autrement dit, get_jetton_balance n'est pas une ligne dans votre code : c'est un outil que l'agent déclenche de lui-même quand l'utilisateur pose une question sur un solde. Sous le capot, le paquet @tonnode/mcp (open source, MIT) parle le protocole natif ADNL de TON, sans couches HTTP intermédiaires — l'agent dialogue directement avec le réseau, et non via une énième passerelle REST.
Exemple : le prompt envoyé à l'agent et ce qui revient
La connexion est locale, sans clé et sans carte bancaire. Ajoutez ceci à la config de votre client MCP (Claude Desktop, Cursor, n'importe quel client compatible MCP) :
{
"mcpServers": {
"ton": {
"command": "npx",
"args": ["-y", "@tonnode/mcp"]
}
}
}
Ensuite, il suffit d'un simple prompt en langage naturel :
Combien d'USDT y a-t-il sur le wallet
UQAbc…xyz? Renvoie la valeur lisible par un humain.
L'agent choisira lui-même l'outil get_jetton_balance, lui passera l'adresse du propriétaire et le jetton USDT, et le serveur renverra le solde — non pas dans les « dollars » auxquels vous êtes habitué, mais en unités raw (les plus petites unités indivisibles du jetton). En plus du solde, la réponse contient l'adresse du jetton-wallet que le serveur a calculée on-chain — pratique si vous voulez ensuite suivre les transferts de ce contrat précis. Le solde lui-même, dans notre exemple, ressemble à 12500000.
Mais 12500000 ne fait pas 12,5 millions d'USDT. C'est ici que commence le second piège, celui où l'on se trompe le plus facilement.
Unités raw et decimals : pourquoi USDT a 6 et non 9
Les blockchains ne manipulent pas de fractions. Tous les montants sont stockés en unités raw entières, et la valeur « humaine » s'obtient en divisant par 10^decimals, où decimals est une propriété du jetton concerné. C'est comme les centimes et les euros : à bas niveau, tout est compté en centimes, et decimals indique où placer la virgule.
La subtilité clé sur TON : le nombre de decimals varie d'un jetton à l'autre.
- Pour le GRAM natif et pour la plupart des jettons TON,
decimals = 9. - Mais pour l'USDT sur TON,
decimals = 6.
Une même valeur raw désigne donc des montants radicalement différents. Reprenons notre exemple correctement :
raw = 12500000
decimals = 6 // spécifiquement pour USDT
human = 12500000 / 10^6 = 12500000 / 1_000_000 = 12.5 USDT
12,5 USDT, donc, et non 12,5 millions. Si, par réflexe, vous aviez divisé par 10^9 (comme pour un jetton ordinaire), vous auriez obtenu 0.0125 — une erreur d'un facteur mille. Un même nombre, trois ordres de grandeur d'écart. C'est exactement comme cela que l'argent se perd dans les intégrations naïves : un diviseur 10^9 codé en dur pour tout et n'importe quoi. D'où la règle : ne codez jamais les decimals en dur — lisez-les dans les métadonnées du jetton lui-même.
get_jetton_info : où récupérer decimals et les métadonnées du jetton
Pour ne pas avoir à deviner, il existe un outil jumeau : get_jetton_info. Il lit le contrat maître du jetton et en restitue les métadonnées :
- le nom (name) ;
- le symbole (symbol) ;
- decimals — le fameux nombre dont dépend le diviseur ;
- l'émission totale (total supply).
Le scénario fiable pour un agent tient en deux appels : si le jetton est inconnu, d'abord get_jetton_info pour obtenir decimals, puis get_jetton_balance pour récupérer le solde raw, et seulement ensuite la division raw / 10^decimals pour l'affichage.
1) get_jetton_info(USDT) -> decimals = 6
2) get_jetton_balance(owner, USDT) -> raw = 12500000
3) human = 12500000 / 10^6 -> 12.5 USDT
On peut formuler le prompt pour que l'agent enchaîne ces étapes tout seul :
Récupère les decimals d'USDT via get_jetton_info, puis le solde du wallet
UQAbc…xyzvia get_jetton_balance, et convertis le raw en nombre lisible par un humain.
Cet ordre est obligatoire dès que vous travaillez avec des jettons arbitraires, et pas seulement avec USDT : pour un token inconnu, vous ne savez pas d'avance s'il a 6 ou 9 decimals. Pour USDT, decimals = 6 est une constante, mais prendre l'habitude de lire la valeur via get_jetton_info vous sauvera dès le premier jetton non standard. Le même réflexe est à la base de la détection des paiements USDT entrants sur TON — les decimals y sont critiques pour ne pas confondre 1 USDT avec une poussière microscopique.
parse_address : vérifier l'adresse hors ligne avant la requête
Autre cause fréquente d'un solde « à zéro » : une adresse de propriétaire mal formée. Les utilisateurs envoient des adresses dans des formats variés : EQ… (bounceable), UQ… (non-bounceable), raw (0:…). Avant d'interroger un solde, il est utile de normaliser l'adresse via parse_address — il fonctionne hors ligne (sans le moindre appel réseau), accepte une adresse dans n'importe lequel de ces formats, la convertit dans les trois (EQ/UQ/raw) et vous dit si elle est valide tout court.
C'est peu coûteux, instantané, et cela élimine toute une classe d'erreurs du type « solde à 0 parce que l'adresse n'est pas la bonne » — surtout quand l'adresse vient d'une source peu fiable, d'une saisie utilisateur ou d'un chat.
Comment se connecter : gratuitement en local ou en hébergé
Trois outils — parse_address, get_jetton_info, get_jetton_balance — donnent une réponse complète et honnête à la question « combien d'USDT y a-t-il sur ce wallet », sans le moindre indexeur tiers.
Gratuitement en local
Le paquet @tonnode/mcp est open source (MIT), publié sur npm et GitHub. La même config publique que dans l'exemple ci-dessus (npx -y @tonnode/mcp) donne accès au jeu complet d'outils de lecture, dont get_jetton_balance, get_jetton_info et parse_address — aucune clé séparée n'est nécessaire.
Vous redémarrez le client — et l'agent lit déjà les soldes de jettons via ADNL. Pourquoi un agent a besoin d'un serveur MCP dédié plutôt que d'une passerelle publique bridée, c'est détaillé dans la note sur MCP pour les agents IA sur TON. Quant à savoir où s'arrête la config publique gratuite, et pourquoi, c'est dans l'analyse des limites des liteservers publics TON.
Hébergé — quand il vous faut du débit
Quand le débit local ne suffit plus (bots, backends, charge de production), vous basculez sur l'endpoint hébergé avec votre propre clé. Le jeu d'outils est identique partout — les 16, bloc de lecture compris ; les plans ne diffèrent que par le débit garanti :
- Hobby — gratuit pour toujours, 60 requêtes/min ;
- Pro — $29/mois, 300 requêtes/min ;
- Scale — $199/mois, 1200 requêtes/min.
La connexion hébergée ne diffère que sur un point — vous vous adressez à un endpoint commun avec votre propre clé :
{
"mcpServers": {
"ton": {
"type": "http",
"url": "https://mcp.tonnode.io/mcp",
"headers": { "Authorization": "Bearer tn_live_…" }
}
}
}
Si vous construisez un dashboard, un bot de vérification de soldes ou un agent qui interroge fréquemment des adresses, prenez une clé : vous éviterez de buter sur les limites des API publiques et de récolter un 429 au pire moment.
En bref
- L'USDT sur TON est un jetton : son solde vit sur un jetton-wallet séparé, pas sur l'adresse principale.
get_jetton_balancecalcule lui-même l'adresse du jetton-wallet on-chain et en lit le solde — ni indexeur ni base maison.- Le solde arrive en unités raw ; pour l'affichage, divisez par
10^decimals. - USDT a
decimals = 6(diviseur1_000_000), la plupart des jettons ont9. Ne codez pas en dur — lisez la valeur viaget_jetton_info. - Tout le parcours se teste gratuitement :
npx -y @tonnode/mcp, config publique, jeu complet de lecture.
La clé Hobby gratuite — 60 requêtes/min, sans carte bancaire — est délivrée dès la connexion : obtenir une clé. Elle suffit largement à dérouler parse_address → get_jetton_info → get_jetton_balance sur un vrai wallet et à vérifier que 12500000 raw font exactement 12,5 USDT — et non 12,5 millions ni 0,0125.
Donnez à votre agent l'accès à TON
16 outils MCP : lecture, swaps non-custodial, cross-chain et wallets. Forfait gratuit — 60 req/min, sans carte.