- Message → Ce que l’utilisateur voulait faire (sa proposition signée)
- Meta → Ce qui s’est réellement passé (le résultat de l’exécution)
<Buffer 00 bf a0 e8...> au lieu d’adresses et de signatures lisibles.
Ce guide vous montre comment : Décoder ces données binaires en un format lisible, extraire des informations significatives, et comprendre toute l’histoire de la transaction de la proposition à l’exécution.
Un flux en direct, sans décodage
Exécutez le client minimal ci-dessous. Les indicateurs de filtre suppriment les transactions de vote et échouées, et le tableauaccountInclude limite les résultats aux activités qui touchent l’ID de programme Jupiter.
filters, createdAt plus une branche transaction qui cache deux enfants :
transaction.transaction.transaction→ le message signétransaction.transaction.meta→ le meta d’exécution
Uint8Array reste opaque pour le moment.
Lorsque vous exécutez le script avec la fonction de décodage, vous verrez la structure réellement imbriquée avec les adresses lisibles :
Décodage des données binaires
Pourquoi décoder ? Les données brutes de Laserstream contiennent des signatures, des clés de compte et des hachages sous forme d’objets binairesUint8Array qui sont illisibles. Vous devez les convertir en chaînes base58 pour comprendre la transaction.
La solution : Laserstream utilise Yellowstone gRPC, qui fournit des utilitaires de décodage intégrés. Au lieu d’écrire des décodeurs séparés pour chaque type de champ, nous utilisons une fonction récursive qui convertit toutes les données binaires en format lisible.
Comprendre la structure de la transaction
Maintenant que nous pouvons voir les données décodées, explorons les deux parties principales de chaque mise à jour de transaction Laserstream. Rappelez-vous de notre exemple initial que chaque transaction contient deux objets clés :- Message (Proposition) →
transaction.transaction.transaction→ le message signé (proposition de l’utilisateur) - Meta (Exécution) →
transaction.transaction.meta→ les métadonnées d’exécution (réponse du validateur)
La proposition : tout ce qui est à l’intérieur du message
L’utilisateur crée un message qui spécifie quoi, qui et jusqu’à quand. Voici comment décoder chaque partie :En-tête de Transaction
numRequiredSignatures indique au validateur combien de signatures vérifier, tandis que les deux valeurs numReadonly* étiquettent les comptes que le runtime peut traiter comme en lecture seule, permettant une exécution parallèle.
Dictionnaire des Clés de Compte
accountKeys est une liste simple de clés publiques qui agit comme une table de recherche. Chaque entier ultérieur dans la transaction - programIdIndex, chaque élément dans le tableau accounts d’une instruction - renvoie à cette liste par index, économisant plus d’un kilooctet par message.
Protection Contre les Répétitions
recentBlockhash expire une fois qu’il défile hors des 150 derniers hachages de blocs, soit environ quatre-vingt-dix secondes sur le mainnet.
Instructions : Les Commandes Réelles
- ID de Programme (
programIdIndex) : Pointe vers une adresse dans le tableauaccountKeys(par exemple, index 10 =ComputeBudget111111111111111111111111111111) - Comptes (
accounts) : Une chaîne encodée en base58 représentant quels indexes de compte cette instruction touche - Données (
data) : Les données réelles de l’instruction encodées en base58
convertBuffers, les comptes apparaissent en base58 mais contiennent en réalité des indices de compte (par exemple, "3vtmrQMafzDoG2CBz1iqgXPTnC" décode en indices [21, 19, 12, 17, 2, 6, 1, 22])
Ce design signifie qu’au lieu de répéter des adresses complètes de 32 octets, chaque instruction fait juste référence à des positions dans la table de recherche.
Signatures : Preuve d’Autorisation
signatures contient les signatures cryptographiques prouvant que les comptes requis ont autorisé cette transaction. Le nombre de signatures doit correspondre à header.numRequiredSignatures.
Recherches de Tables d’Adresses
versioned est true, addressTableLookups apparaît avec une table en chaîne et deux listes d’index. Les tables de recherche lèvent la limite de l’adresse à des dizaines tout en gardant le paquet sous le MTU de 1 232 octets.
Transaction v1 : Budget de Calcul dans l’En-tête
La Transaction v1 (SIMD-0385, Agave 4.2) ajoute un champ supplémentaire au message :transactionConfig.
instructions d’une transaction v1 ne contient jamais d’entrée ComputeBudget111111111111111111111111111111. priorityFee est la totalité des frais en lamports pour toute la transaction, non des micro-lamports par unité de calcul. Un champ null signifie que l’expéditeur ne l’a pas défini. Les messages Legac et v0 n’ont pas de transactionConfig, donc sa présence identifie une transaction v1.
Deux choses à vérifier dans votre décodeur :
- Extraction des frais de priorité. Lire
transactionConfig.priorityFeequand il existe, et se rabattre sur l’analyse des instructions de ComputeBudget uniquement pour les transactions legacy et v0. Le code qui ne scanne que les instructions lit chaque transaction v1 comme ne payant aucun frais de priorité. - Version proto.
yellowstone-grpc-proto12.6.0 est la première version qui transporte les champs v1, ethelius-laserstream0.8.4 (JavaScript), 0.6.3 (Rust), et 0.2.0 (Go) sont les premières versions de SDK construites dessus. Les versions plus anciennes abandonnenttransactionConfigsilencieusement.
Comment Tout se Connecte : Le Flux
Voici ce qui se passe depuis les premiers principes :- Construire la table de recherche :
accountKeysliste toutes les adresses que cette transaction va toucher - Définir les règles :
headerspécifie combien de signatures sont requises et quels comptes sont en lecture seule - Créer les commandes : Chaque
instructionpointe vers :- Un programme (via
programIdIndex→accountKeys[index]) - Les comptes dont elle a besoin (via
accounts→ plusieurs positionsaccountKeys[index]) - Les données de l’instruction (encodées dans
data)
- Un programme (via
- Ajouter l’autorisation :
signaturesprouve que les comptes requis ont approuvé cette transaction - Définir l’expiration :
recentBlockhashassure que cette transaction ne peut pas être rejouée plus tard
L’exécution : tout ce qui est à l’intérieur du meta
Tandis que le message montre ce que l’utilisateur voulait faire, le meta montre ce qui s’est réellement passé lorsque les validateurs ont exécuté la transaction.Informations de base sur l’exécution
Succès/Échecerr: null= succèserr: {...}= échec avec détails sur l’erreurfee= lamports facturés pour cette transaction
accountKeys par index :
- Compte 0 : Perd 15 000 lamports (paiement de frais)
- Compte 1 : Gagne 1 461 600 lamports (nouveau compte créé)
- Compte 3 : Gagne 2 001 231 920 lamports (compte programme)
Détails avancés de l’exécution
Instructions InternesModèles pratiques de décodage
Voici des modèles courants pour extraire des informations utiles des transactions décodées :Exemple complet : décodeur d’échange Jupiter
Voici un exemple complet qui décode les transactions d’échange Jupiter et extrait des informations significatives :Points clés
- Structure en deux parties : Chaque transaction a un message (ce qui a été demandé) et un meta (ce qui s’est réellement passé)
- Décodage binaire : Utilisez
bs58.encode()pour convertir les champs binaires en chaînes base58 lisibles - Recherche de clés de compte : Les instructions référencent les comptes par index dans le tableau
accountKeys - Suivi des balances : Comparez
preBalancesetpostBalancespour voir ce qui a changé - Transaction v1 : Lisez le budget de calcul et les frais de priorité dans
transactionConfiglorsqu’il est présent ; les transactions v1 n’ont pas d’instructions ComputeBudget