Tous les articles
9 min de lecture

Historique des transactions TON : lt, hash et nœud d'archive

Comment fonctionne l'historique des transactions TON : logical time (lt), hash et pointeur de dernière transaction. Lecture via MCP et rôle du nœud d'archive.

historique transactions TONlogical timelt hash TONget_transactionsnœud d'archive TONMCP TON

Vous travaillez de nuit et, à 3 h 47, un message tombe : un utilisateur a payé sa commande en TON, et le bot ne l'a pas comptabilisée. Vous ouvrez l'explorateur — l'argent est bien là, le virement entrant est visible. Le problème n'est donc pas dans la blockchain, mais dans la façon dont votre service lit l'historique des transactions. Et c'est là que vous découvrez que, sur TON, cet historique n'est pas du tout organisé comme vous en avez l'habitude avec Ethereum.

Le monitoring des paiements sur TON paraît trompeusement simple : on interroge le solde et on réagit au changement. En pratique, tout s'écroule. Deux paiements du même montant dans la même minute — y en a-t-il eu un ou deux ? L'utilisateur a payé en USDT, et le solde GRAM n'a pas bougé d'un poil. Il vous faut un relevé sur six mois, et le liteserver ne renvoie que les dernières transactions sans un mot sur le reste.

Si vous branchez un agent IA sur TON ou si vous écrivez un backend qui rapproche des paiements, entrons dans le concret : ce qu'est l'historique des transactions TON, à quoi sert la paire lt + hash, pourquoi la profondeur bute sur le nœud d'archive et comment lire tout cela depuis un agent via MCP.

Ce qu'est l'historique des transactions dans TON et pourquoi un agent en a besoin

Dans TON, chaque compte — wallet, contrat, jetton-wallet — possède sa propre chaîne de transactions. Chaque transaction est le résultat du traitement d'un message entrant : réception de GRAM, transfert de jeton, appel d'une méthode de contrat. Pour un agent (Claude, Cursor, n'importe quel client MCP) qui encaisse des paiements ou rapproche des comptes, l'historique est la seule source fiable : le solde dit « combien maintenant », l'historique dit « quoi exactement et quand, de qui et pour quel montant ».

Les tâches très concrètes pour lesquelles un agent a besoin de l'historique :

  • confirmer la bonne réception d'un paiement (« vérifie si un virement est arrivé sur cette adresse dans la dernière heure ») ;
  • constituer un relevé de wallet ;
  • retrouver une transaction précise à partir de son identifiant ;
  • rapprocher les jetons entrants (USDT par exemple) du montant attendu.

Le problème, c'est que « donne-moi les transactions n° 100 à 120 » ne marche pas sur TON. Il n'existe pas ici de numérotation globale et continue des blocs qui permettrait de « donner la transaction numéro N ». L'ordre est établi autrement — par le temps logique.

Le temps logique (lt) : pourquoi TON n'a pas de numérotation de blocs classique

Sur Ethereum, tout est simple : il y a le bloc n° 19 000 000, et dedans les transactions dans l'ordre. Un compteur global unique, monotone croissant, et tout le monde se réfère au même numéro.

TON est un système multi-thread : masterchain, workchains, shards qui se scindent et fusionnent selon la charge. Il n'existe tout simplement pas ici de « numéro de bloc » unique permettant d'ordonner linéairement tous les événements du réseau. À la place, TON utilise le temps logique (lt, logical time) — un compteur monotone croissant avec lequel le réseau ordonne les événements, les messages et les transactions. Il garantit ceci : si l'événement A a influencé l'événement B, alors lt(A) < lt(B).

D'où une conséquence pratique : la position d'une transaction est définie non par un numéro de bloc, mais par la paire (lt, hash). Le lt se charge de l'ordre (qui vient avant), le hash de l'identification univoque de la transaction. Pris séparément, ils sont inutiles pour une requête ciblée : des objets différents peuvent avoir des lt voisins, et le hash seul ne dit pas au liteserver par où commencer la lecture.

Hash de transaction et paire lt + hash : le pointeur de dernière transaction d'un compte

Le hash d'une transaction est son empreinte cryptographique ; sur TON, il arrive en base64 ou en hex. Le lt dit « quand dans l'ordre », le hash « laquelle exactement », parce que, sur une même tranche de lt, une ambiguïté reste théoriquement possible et que, sans le hash, le liteserver ne renverra pas la transaction. À retenir fermement : la paire (lt, hash) est indissociable. Le lt seul ou le hash seul ne suffisent pas.

Où trouver le point de départ ? L'état du compte conserve un pointeur vers la toute dernière transaction — last_transaction_id, c'est-à-dire cette même paire lt + hash (champs last_trans_lt / last_trans_hash). C'est la « tête » de la liste, celle par laquelle commence le parcours de l'historique vers l'arrière.

Dans l'outillage MCP de TONNode, c'est get_account_state qui s'en charge — il renvoie le statut du compte, ses flags et le pointeur vers la dernière transaction.

Comment get_transactions parcourt l'historique : lt + hash et la chaîne prev_trans

Voici le point clé, celui qui rend possible le feuilletage de l'historique. Chaque transaction contient, en plus de ses propres lt et hash, deux champs : prev_trans_lt et prev_trans_hash — la référence vers la transaction précédente du même compte. Les transactions forment donc une liste simplement chaînée qui remonte le temps ; la tête, c'est le last_transaction_id issu de l'état.

get_transactions accepte :

  • account — l'adresse du compte ;
  • les lt et hash de départ — le point à partir duquel lire ;
  • count — la taille du lot.

Il renvoie un lot de transactions en allant vers l'arrière depuis le point indiqué. La logique du parcours page par page :

  1. Prendre last_trans_lt / last_trans_hash depuis get_account_state — c'est la tête.
  2. Appeler get_transactions(account, lt, hash, count) — récupérer un lot.
  3. Sur la dernière transaction du lot, relever prev_trans_lt et prev_trans_hash.
  4. Rappeler get_transactions avec cette paire — c'est la page suivante.
  5. Répéter jusqu'à ce que prev_trans_lt tombe à 0 (le début de vie du compte) ou jusqu'à atteindre la profondeur voulue.

Voilà comment fonctionne la pagination sur TON : non pas « page 2 », mais « continue à partir de cette paire (lt, hash) ».

Blocs récents contre historique profond : à quoi sert un nœud d'archive

C'est là que la plupart des développeurs trébuchent. Un liteserver ordinaire renvoie les blocs récents et l'historique récent d'un compte : il conserve une fenêtre d'état limitée — un certain nombre de blocs récents — et, à mesure que le réseau grossit, les anciennes données en sont évacuées. Remontez la chaîne prev_trans suffisamment loin et, à un moment, le liteserver cessera tout simplement de renvoyer des transactions : elles ne sont physiquement plus dans sa fenêtre. Le paiement de tout à l'heure, vous le verrez ; la transaction d'il y a six mois, non.

Ce n'est pas un bug, c'est un choix de conception : garder le parcours complet de chaque compte depuis le bloc de genèse coûte cher. L'historique profond est conservé par un nœud d'archive — un nœud qui ne purge pas les anciens blocs et garde l'état complet du réseau sur toute son histoire.

Conclusion pratique :

  • détection de paiement, relevé récent, « est-ce arrivé dans la dernière heure » — un liteserver ordinaire suffit ;
  • audit complet d'un wallet depuis le tout premier jour — il faut un nœud d'archive.

Autre source de douleur : les liteservers publics de la config globale de TON. Ils sont partagés et limités : sous charge, ils répondent souvent not ready ou partent en timeout ADNL, et l'historique profond n'y est pas non plus. Si vous êtes justement tombé sur un not ready, c'est le symptôme d'un liteserver partagé surchargé — l'analyse se trouve dans la note pourquoi le liteserver répond not ready et comment le réparer.

Soyons honnêtes sur TONNode : le nœud d'archive est dans la roadmap et en cours de synchronisation — je ne vous promets pas la profondeur d'archive comme fonctionnalité déjà disponible. Aujourd'hui, il s'agit de lire l'historique récent via un endpoint maîtrisé, sans la loterie des liteservers publics. Pour l'archive complète depuis le bloc de genèse, attendez une annonce dédiée.

Comment lire l'historique TON avec un agent IA via MCP

TONNode est un serveur MCP hébergé pour TON, exactement 16 outils, et get_transactions fait partie du bloc lecture. MCP (Model Context Protocol) est le standard par lequel l'agent appelle les outils lui-même, sans que vous ayez à assembler des requêtes ADNL à la main. Pas besoin d'installer de SDK, de monter un liteserver ni de décortiquer du TL-B — l'agent appelle l'outil directement.

Connexion locale gratuite

L'ensemble complet des outils de lecture, config publique, paquet @tonnode/mcp — open source, MIT :

{
  "mcpServers": {
    "ton": {
      "command": "npx",
      "args": ["-y", "@tonnode/mcp"]
    }
  }
}

Le paquet fonctionne sur le protocole ADNL natif de TON, sans couche HTTP intermédiaire.

Endpoint hébergé avec votre propre clé

Pour un débit garanti, prenez l'endpoint hébergé avec votre propre clé :

{
  "mcpServers": {
    "ton": {
      "type": "http",
      "url": "https://mcp.tonnode.io/mcp",
      "headers": { "Authorization": "Bearer tn_live_…" }
    }
  }
}

Pour lire l'historique, vous utiliserez :

  • get_account_state — le point de départ du parcours (last_trans_lt / last_trans_hash), le statut et les flags du compte ;
  • get_transactions — les transactions elles-mêmes, page par page, par paire (lt, hash) ;
  • parse_address — conversion hors ligne des adresses entre EQ / UQ / raw ;
  • get_jetton_info et get_jetton_balance — pour l'historique des jetons.

Pratique : détecter un paiement et parcourir l'historique page par page

Montons un scénario type — « le paiement est-il arrivé, et pour quel montant ».

Le prompt pour l'agent

Via l'outil ton, prends get_account_state pour EQC…myshop, puis get_transactions à partir de ses last_trans_lt / last_trans_hash, count 20. Trouve un virement entrant de 5 GRAM sur les 10 dernières minutes. Convertis les adresses des expéditeurs en EQ via parse_address avant de comparer. S'il faut remonter plus loin dans l'historique, prends les prev_trans_lt / prev_trans_hash de la dernière transaction et répète get_transactions.

L'agent ira chercher lui-même la tête de la chaîne, feuillettera le lot et vérifiera les montants.

Pseudo-code du parcours paginé

Le même parcours en pseudo-code (les appels sont les outils MCP que l'agent déclenche) :

state = get_account_state(account)
lt    = state.last_trans_lt
hash  = state.last_trans_hash

while lt != 0:
    batch = get_transactions(account, lt, hash, count=20)
    for tx in batch:
        if matches_expected_payment(tx):   # message entrant avec le montant/commentaire attendu
            return tx
    last = batch[-1]
    lt   = last.prev_trans_lt
    hash = last.prev_trans_hash   # sans hash, impossible de demander la page suivante

Les détails qui économisent des heures de debug

  • Dans les champs des transactions, les adresses arrivent sous forme raw (0:abcd…). Avant de comparer l'expéditeur ou le destinataire à l'adresse EQ… dont vous avez l'habitude, ramenez les deux au même format via parse_address — sinon la comparaison de chaînes échouera alors qu'il s'agit de la même adresse. Plus de détails sur les formats : EQ, UQ et raw dans les adresses TON.
  • Les paiements en USDT ne sont pas visibles sur la chaîne du wallet GRAM. Les jetons vivent sur un jetton-wallet distinct, dont l'adresse se calcule on-chain (get_jetton_balance le fait pour vous et renvoie le solde courant). Pour l'historique des jetons, parcourez les transactions du jetton-wallet, pas celles du wallet principal.
  • Reconvertissez les montants raw via les decimals. Les montants de jetons arrivent en unités minimales : pour transformer 5000000 en 5 USDT lisibles par un humain, prenez decimals depuis get_jetton_info — pour l'USDT c'est 6, pour la plupart des jetons 9. Une confusion, et vous vous trompez d'un facteur 1000. L'analyse complète : comment détecter un paiement USDT entrant sur TON.
  • Dédupliquez par (lt, hash), pas par montant. Deux paiements identiques ne se distinguent que par la paire lt + hash — c'est précisément la clé d'idempotence.

Si, au-delà de l'historique, l'agent doit interroger l'état courant d'un contrat, c'est run_get_method qui s'en charge — comment appeler des get-méthodes sans SDK, c'est montré dans la lecture de TON sans SDK via une get-méthode.

En bref

  • Sur TON, il n'y a pas de numéros de blocs comme sur Ethereum — la position d'une transaction est donnée par la paire (lt, hash) : lt pour l'ordre, hash pour l'identification, et les deux sont obligatoires ensemble.
  • Les transactions d'un compte forment une liste chaînée via prev_trans_lt / prev_trans_hash ; vous feuilletez vers l'arrière en partant du last_transaction_id fourni par get_account_state.
  • Un liteserver ordinaire renvoie l'historique récent ; la profondeur complète, c'est l'affaire d'un nœud d'archive.
  • Pour les jetons, n'oubliez ni les decimals de get_jetton_info (USDT = 6) ni les adresses raw, qu'il faut faire passer par parse_address.

Pas envie de vous battre avec des liteservers publics qui répondent not ready ? get_transactions est disponible d'emblée sur le plan gratuit Hobby — 60 requêtes/min, pour toujours, sans carte bancaire, la clé est délivrée dès la connexion.

Branchez la lecture de l'historique TON en une minute → tonnode.io/dashboard?plan=hobby

Et pour ce que sait faire un agent sur TON au-delà de la lecture de l'historique — les 16 outils sur la page des outils TONNode.

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.