Skip to main content
Wenn Sie Transaktionsdaten von Laserstream erhalten, gibt es zwei wichtige Dinge zu beachten:
  • Message → Was der Benutzer tun wollte (sein unterschriebener Vorschlag)
  • Meta → Was tatsächlich passiert ist (das Ausführungsergebnis)
Die Herausforderung: Rohdaten von Transaktionen kommen als binäre Byte-Arrays wie <Buffer 00 bf a0 e8...> anstatt als lesbare Adressen und Signaturen. Diese Anleitung zeigt Ihnen, wie Sie: Diese Binärdaten in ein menschenlesbares Format dekodieren, bedeutungsvolle Informationen extrahieren und die gesamte Transaktionsgeschichte vom Vorschlag bis zur Ausführung verstehen können.

Ein Livestream, keine Dekodierung

Führen Sie den minimalen Client unten aus. Die Filteroptionen lassen Abstimmungs- und fehlgeschlagene Transaktionen weg, und das accountInclude Array begrenzt die Ergebnisse auf Aktivitäten, die die Jupiter-Programm-ID berühren.
In Ihrer Konsole wird jetzt ein Wrapper angezeigt—filters, createdAt plus ein transaction Zweig, der zwei Kinder verbirgt:
  • transaction.transaction.transaction → die unterzeichnete Nachricht
  • transaction.transaction.meta → die Ausführungs-Meta
Alles, was wie Uint8Array aussieht, bleibt momentan opak. Wenn Sie das Skript mit der Dekodierungsfunktion ausführen, sehen Sie die tatsächliche verschachtelte Struktur mit lesbaren Adressen:

Dekodierung der Binärdaten

Warum dekodieren? Rohe Laserstream-Daten enthalten Signaturen, Kontoschlüssel und Hashes als binäre Uint8Array Objekte, die unleserlich sind. Sie müssen diese in Base58-Zeichenfolgen konvertieren, um die Transaktion zu verstehen. Die Lösung: Laserstream verwendet Yellowstone gRPC, das integrierte Dekodierungsfunktionen bietet. Anstatt separate Dekodierer für jeden Feldtyp zu schreiben, verwenden wir eine rekursive Funktion, die alle Binärdaten in ein menschenlesbares Format umwandelt.
Dieser Ansatz nutzt die integrierte Dekodierung, während die Binärfelder behandelt werden, die eine manuelle Konvertierung erfordern. Die Transaktionsstruktur ist bereits analysiert - Sie müssen nur die Binärfelder in ein lesbares Format konvertieren.

Verständnis der Transaktionsstruktur

Jetzt, da wir die dekodierten Daten sehen können, lassen Sie uns die beiden Hauptteile jeder Laserstream-Transaktionsaktualisierung untersuchen. Erinnern Sie sich an unser anfängliches Beispiel, dass jede Transaktion zwei wichtige Objekte enthält:
  • Message (Proposal)transaction.transaction.transaction → die signierte Nachricht (Vorschlag des Benutzers)
  • Meta (Execution)transaction.transaction.meta → die Ausführungs-Metadaten (Antwort des Validators)
Diese zweiteilige Struktur erzählt eine vollständige Geschichte: was der Benutzer angefordert hat im Vergleich zu dem, was tatsächlich passiert ist. Lassen Sie uns jeden Teil im Detail untersuchen.

Der Vorschlag: alles innerhalb der Nachricht

Der Benutzer erstellt eine Nachricht, die was, wer und bis wann spezifiziert. So dekodieren Sie jeden Teil:

Transaktionsheader

numRequiredSignatures teilt dem Validator mit, wie viele Signaturen zu überprüfen sind, während die zwei numReadonly* Werte Konten kennzeichnen, die die Laufzeit als schreibgeschützt behandeln kann, was eine parallele Ausführung ermöglicht.

Kontoschlüssel-Wörterbuch

accountKeys ist eine einfache Liste von öffentlichen Schlüsseln, die als Nachschlagetabelle dient. Jede spätere Zahl in der Transaktion - programIdIndex, jedes Element in einem Anweisungs-accounts Array - verweist nach Index auf diese Liste zurück, was mehr als ein Kilobyte pro Nachricht spart.

Schutz vor Wiedergabe

recentBlockhash läuft ab, sobald es aus den letzten 150 Block-Hashes herausgescrollt ist, ungefähr neunzig Sekunden im Mainnet.

Anweisungen: Die eigentlichen Befehle

Jede Anweisung enthält drei Hauptteile:
  • Program ID (programIdIndex): Verweist auf eine Adresse im accountKeys Array (z.B. Index 10 = ComputeBudget111111111111111111111111111111)
  • Accounts (accounts): Eine Base58-codierte Zeichenfolge, die angibt, welche Konto-Indizes diese Anweisung berührt
  • Data (data): Die tatsächlichen Anweisungsdaten, die als Base58 codiert sind
Aufgrund der convertBuffers Funktion erscheinen Konten als Base58, enthalten jedoch tatsächlich Konto-Indizes (z.B. "3vtmrQMafzDoG2CBz1iqgXPTnC" dekodiert zu Indizes [21, 19, 12, 17, 2, 6, 1, 22]) Dieses Design bedeutet, dass anstatt vollständige 32-Byte-Adressen zu wiederholen, jede Anweisung nur Positionen in der Nachschlagetabelle referenziert.

Signaturen: Nachweis der Autorisierung

signatures enthält die kryptografischen Signaturen, die belegen, dass die erforderlichen Konten diese Transaktion autorisiert haben. Die Anzahl der Signaturen muss mit header.numRequiredSignatures übereinstimmen.

Adresstabellen-Nachschläge

Wenn versioned true ist, erscheint addressTableLookups mit einer On-Chain-Tabelle und zwei Indizes-Listen. Nachschlagetabellen heben die harte Grenze der Adressanzahl auf Dutzende an, während das Paket unter der MTU von 1.232 Byte bleibt.

Transaktion v1: Rechenbudget im Header

Transaktion v1 (SIMD-0385, Agave 4.2) fügt der Nachricht ein weiteres Feld hinzu: transactionConfig.
Eine v1-Transaktion trägt hier ihr Rechenbudget anstatt in ComputeBudget-Programmanweisungen, so dass das instructions Array einer v1-Transaktion niemals einen ComputeBudget111111111111111111111111111111 Eintrag enthält. priorityFee ist die gesamte Gebühr in Lamports für die gesamte Transaktion, nicht in Mikro-Lamports pro Recheneinheit. Ein null Feld bedeutet, dass der Absender ihn nicht gesetzt hat. Legacy und v0 Nachrichten haben keinen transactionConfig, so dass seine Anwesenheit eine v1-Transaktion identifiziert. Zwei Dinge, die Sie in Ihrem Decoder überprüfen sollten:
  • Prioritätsgebührenextraktion. Lesen Sie transactionConfig.priorityFee, wenn es existiert, und durchforsten Sie ComputeBudget-Anweisungen nur für Legacy- und v0-Transaktionen. Code, der nur Anweisungen durchsucht, liest jede v1-Transaktion als zahlend keine Prioritätsgebühr.
  • Proto-Version. yellowstone-grpc-proto 12.6.0 ist die erste Version, die die v1-Felder enthält, und helius-laserstream 0.8.4 (JavaScript), 0.6.3 (Rust) und 0.2.0 (Go) sind die ersten SDK-Versionen, die darauf basieren. Ältere Versionen lassen transactionConfig stillschweigend fallen.
Siehe Transaktion v1 Unterstützung für die vollständige Liste der Änderungen.

So verbindet sich alles: Der Ablauf

Hier ist, was von den Grundlagen an passiert:
  1. Die Nachschlagetabelle erstellen: accountKeys listet alle Adressen auf, die diese Transaktion berühren wird
  2. Regeln festlegen: header legt fest, wie viele Signaturen erforderlich sind und welche Konten schreibgeschützt sind
  3. Befehle erstellen: Jeder instruction zeigt auf:
    • Ein Programm (über programIdIndexaccountKeys[index])
    • Die benötigten Konten (über accounts → mehrere accountKeys[index] Positionen)
    • Die Anweisungsdaten (im data kodiert)
  4. Autorisierung hinzufügen: signatures beweist, dass die erforderlichen Konten diese Transaktion genehmigt haben
  5. Ablauf festlegen: recentBlockhash stellt sicher, dass diese Transaktion später nicht erneut abgespielt werden kann

Die Ausführung: alles in Meta

Während die Nachricht zeigt, was der Benutzer tun wollte, zeigt die Meta, was tatsächlich passiert ist, als Validatoren die Transaktion ausführten.

Grundlegende Ausführungsinformationen

Erfolg/Misserfolg
  • err: null = Erfolg
  • err: {...} = Fehler mit Fehlerdetails
  • fee = Lamports, die für diese Transaktion berechnet wurden
Saldoänderungen
Saldenarrays entsprechen dem accountKeys Array nach Index:
  • Konto 0: 15000 Lamports verloren (Gebührenzahlung)
  • Konto 1: 1461600 Lamports gewonnen (neues Konto erstellt)
  • Konto 3: 2001231920 Lamports gewonnen (Programmkonto)
Rechenverbrauch
Zeigt, wie viel Rechenbudget vom angeforderten Betrag genutzt wurde.

Erweiterte Ausführungsdetails

Innere Anweisungen
Innere Anweisungen sind zusätzliche Anweisungen, die Programme während der Ausführung aufgerufen haben. Sie sind nicht Teil der ursprünglichen Transaktion, wurden jedoch durch die Hauptanweisungen ausgelöst. Protokollnachrichten
Protokollnachrichten bieten eine chronologische Nachverfolgung der Programmausführung und zeigen, welche Programme aufgerufen wurden und welche benutzerdefinierten Protokollnachrichten sie ausgegeben haben. Token-Saldoänderungen
Token-Saldoänderungen zeigen Vorher-/Nachher-Zustände für SPL-Token-Konten, einschließlich der lesbaren Beträge mit ordnungsgemäßer Dezimalhandhabung.

Praktische Dekodierungsmuster

Hier sind allgemeine Muster zum Extrahieren nützlicher Informationen aus dekodierten Transaktionen:

Komplettes Beispiel: Jupiter Swap Decoder

Hier ist ein komplettes Beispiel, das Jupiter Swap-Transaktionen dekodiert und bedeutungsvolle Informationen extrahiert:
Dieses Beispiel zeigt, wie man die Dekodierung von Nachrichten mit der Metaanalyse kombiniert, um geschäftsrelevante Informationen aus komplexen DeFi-Transaktionen zu extrahieren.

Wichtige Erkenntnisse

  • Zweiteilige Struktur: Jede Transaktion hat eine Nachricht (was angefordert wurde) und Meta (was tatsächlich passiert ist)
  • Binärdekodierung: Verwenden Sie bs58.encode(), um Binärfelder in lesbare Base58-Zeichenfolgen umzuwandeln
  • Kontoschlüssel-Nachschläge: Anweisungen verweisen auf Konten nach Index im accountKeys Array
  • Saldenverfolgung: Vergleichen Sie preBalances und postBalances, um zu sehen, was sich geändert hat
  • Transaktion v1: Lesen Sie das Rechenbudget und die Prioritätsgebühr von transactionConfig, wenn es vorhanden ist; v1 Transaktionen haben keine ComputeBudget Anweisungen
Der Schlüssel zum Verständnis von Solana-Transaktionen besteht darin zu erkennen, dass sie auf Effizienz ausgelegt sind: anstatt Adressen zu wiederholen, verwenden sie Nachschlagetabellen und Indizes, um die Transaktionsgröße zu minimieren und gleichzeitig eine maximale Informationsdichte zu gewährleisten.