> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Dekodierung und Analyse von Transaktionsdaten

> Erlernen Sie, wie Sie Transaktionsdaten von Laserstream dekodieren und analysieren, um ein besseres Verständnis von Solana-Transaktionen zu erhalten.

**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.

```ts [expandable] theme={"system"}
import { subscribe, CommitmentLevel, SubscribeUpdate, LaserstreamConfig } from 'helius-laserstream';

async function runTransactionSubscription() {
  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const request = {
    transactions: {
      "Jupiter-transactions": {
        vote: false,
        failed: false,
        accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4']
      }
    },
    commitment: CommitmentLevel.PROCESSED,
    accounts: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    (u: SubscribeUpdate) => console.log('💸 Transaction update', u),
    console.error
  );

  console.log(`✅ stream id → ${stream.id}`);
  process.on('SIGINT', () => { stream.cancel(); process.exit(0); });
}
runTransactionSubscription().catch(console.error);
```

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**

```json theme={"system"}
{
 filters: [ 'Jupiter-transactions' ],
  account: undefined,
  transaction: {
    transaction: {
      signature: <Buffer 00 bf a0 e8 9f cc 84 0c a4 83 e3 97 cd b7 57 e2 2b bc 1d ca 8c a6 1b ce b5 57 d7 47 5e ec 1f 46 ae b2 2d 6a 12 cb 88 48 1d 07 bf f6 b2 d3 a8 0b c9 04 ... 14 more bytes>,
      transaction: [Object],
      meta: [Object],
      index: '1177'
    },
    slot: '351704819'
  },
  transactionStatus: undefined,
  block: undefined,
  blockMeta: undefined,
  entry: undefined,
  ping: undefined,
  pong: undefined,
  createdAt: 2025-07-07T10:58:44.403Z
}
```

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:

```json [expandable] theme={"system"}
{
  "filters": ["Jupiter-transactions"],
  "account": undefined,
  "transaction": {
    "transaction": {
      "signature": "5u62i53R1Hdc4thm6DQTNWNkyypuJJSaXSMwwQDxNqKMaAw62H1Xa3Md7QDhYjoPk5dCPg18fwz83kUR6TrMviTx",
      "transaction": {
        "message": {
          "header": {
            "numRequiredSignatures": 1,
            "numReadonlySignedAccounts": 0,
            "numReadonlyUnsignedAccounts": 8
          },
          "accountKeys": [
            "AF9KFSWQeKVxd3kVvFvysWXmATHyYzrN8zN8GtXn4qTF",
            "G9VzXwhDPQ8KRbQAJN6TyGf2gWukYDAvmnXJhPZFev4f",
            "ES9qPxWQVMRZkobJ9yr3U6XSrXzGNLJdSe6p6fS7b82T",
            "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
            "ComputeBudget111111111111111111111111111111",
            "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
            "So11111111111111111111111111111111111111112",
            "11111111111111111111111111111111",
            "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
          ],
          "recentBlockhash": "8sGjRxJHLJVWqpHt5UdN8qxtLLdgcnLKBpFj9Qrn5PNF",
          "instructions": [
            {
              "programIdIndex": 4,
              "accounts": [],
              "data": "3bjaAzoXPjbY"
            },
            {
              "programIdIndex": 3,
              "accounts": [0, 1, 2, 5, 6, 7, 8],
              "data": "2L1xoA2KEqBgWfGt3fwFJK8k4FPJRJzYHRgH4R3xC8A7"
            }
          ]
        },
        "signatures": [
          "5u62i53R1Hdc4thm6DQTNWNkyypuJJSaXSMwwQDxNqKMaAw62H1Xa3Md7QDhYjoPk5dCPg18fwz83kUR6TrMviTx"
        ]
      },
      "meta": {
        "err": null,
        "fee": 12500,
        "preBalances": [1075517572, 0, 207594496815, 0, 0, 0, 0, 0, 0],
        "postBalances": [1075502572, 1461600, 207594496815, 2001231920, 2039280, 0, 0, 0, 0],
        "innerInstructions": [
          {
            "index": 1,
            "instructions": [
              {
                "programIdIndex": 5,
                "accounts": [1, 2, 0],
                "data": "3Bxs4h24hBtQy9rw"
              }
            ]
          }
        ],
        "logMessages": [
          "Program ComputeBudget111111111111111111111111111111 invoke [1]",
          "Program ComputeBudget111111111111111111111111111111 success",
          "Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [1]",
          "Program log: Instruction: Swap",
          "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [2]",
          "Program log: Create",
          "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]",
          "Program log: Instruction: GetAccountDataSize",
          "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1569 of 242833 compute units",
          "Program return: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA pQAAAAAAAAA=",
          "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success",
          "Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success"
        ],
        "preTokenBalances": [],
        "postTokenBalances": [],
        "computeUnitsConsumed": 182564
      },
      "index": "1177"
    },
    "slot": "351709933"
  },
  "transactionStatus": undefined,
  "block": undefined,
  "blockMeta": undefined,
  "entry": undefined,
  "ping": undefined,
  "pong": undefined,
  "createdAt": "2025-01-14T10:58:44.403Z"
}
```

***

## 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.

```ts [expandable] theme={"system"}
import bs58 from 'bs58';
import { subscribe, CommitmentLevel, SubscribeUpdate, LaserstreamConfig } from 'helius-laserstream';

// Recursive function to convert all Buffer/Uint8Array fields to base58
function convertBuffers(obj: any): any {
  if (!obj) return obj;
  if (Buffer.isBuffer(obj) || obj instanceof Uint8Array) {
    return bs58.encode(obj);
  }
  if (Array.isArray(obj)) {
    return obj.map(item => convertBuffers(item));
  }
  if (typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj).map(([key, value]) => [key, convertBuffers(value)])
    );
  }
  return obj;
}

async function runTransactionSubscription() {
  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const request = {
    transactions: {
      "Jupiter-transactions": {
        vote: false,
        failed: false,
        accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4']
      }
    },
    commitment: CommitmentLevel.PROCESSED,
    accounts: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    (update: SubscribeUpdate) => {
      if (update.transaction) {
        // Convert all binary fields to human-readable format
        const decodedTransaction = convertBuffers(update.transaction);
        console.log('💸 Decoded transaction:', JSON.stringify(decodedTransaction, null, 2));
        
        // Or process specific fields
        processTransaction(update.transaction);
      }
    },
    console.error
  );

  console.log(`✅ stream id → ${stream.id}`);
  process.on('SIGINT', () => { stream.cancel(); process.exit(0); });
}

function processTransaction(txUpdate: any) {
  const tx = txUpdate.transaction;
  const meta = tx.meta;
  
  console.log('Transaction Details:');
  console.log('- Signature:', bs58.encode(tx.signature));
  console.log('- Slot:', txUpdate.slot);
  console.log('- Success:', meta.err === null);
  console.log('- Fee:', meta.fee, 'lamports');
  console.log('- Compute Units:', meta.computeUnitsConsumed);
  
  // Account keys are already available in the message
  const message = tx.transaction.message;
  if (message.accountKeys) {
    console.log('- Account Keys:');
    message.accountKeys.forEach((key: Uint8Array, index: number) => {
      console.log(`  ${index}: ${bs58.encode(key)}`);
    });
  }
  
  // Log messages are already UTF-8 strings
  if (meta.logMessages && meta.logMessages.length > 0) {
    console.log('- Log Messages:');
    meta.logMessages.forEach((log: string) => {
      console.log(`  ${log}`);
    });
  }
}

runTransactionSubscription();
```

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

```json theme={"system"}
{
  "header": {
    "numRequiredSignatures": 1,
    "numReadonlySignedAccounts": 0,
    "numReadonlyUnsignedAccounts": 5
  }
}
```

`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

```json theme={"system"}
{
  "accountKeys": [
    "7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5",
    "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "So11111111111111111111111111111111111111112",
    "11111111111111111111111111111111"
  ]
}
```

`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

```json theme={"system"}
{
  "recentBlockhash": "8sGjRxJHLJVWqpHt5UdN8qxtLLdgcnLKBpFj9Qrn5PNF"
}
```

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

### Anweisungen: Die eigentlichen Befehle

```json theme={"system"}
{
  "instructions": [
    {
      "programIdIndex": 10,
      "data": "HnkkG7"
    },
    {
      "programIdIndex": 15,
      "accounts": "3vtmrQMafzDoG2CBz1iqgXPTnC",
      "data": "5jRcjdixRUDKQKUEt6oHJ747HCB3vWb5y"
    }
  ]
}
```

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

```json theme={"system"}
{
  "signatures": [
    "5u62i53R1Hdc4thm6DQTNWNkyypuJJSaXSMwwQDxNqKMaAw62H1Xa3Md7QDhYjoPk5dCPg18fwz83kUR6TrMviTx"
  ]
}
```

`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

```json theme={"system"}
{
  "addressTableLookups": [
    {
      "accountKey": "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1",
      "writableIndexes": [0, 1],
      "readonlyIndexes": [2, 3, 4]
    }
  ],
  "versioned": true
}
```

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](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md), Agave 4.2) fügt der Nachricht ein weiteres Feld hinzu: `transactionConfig`.

```json theme={"system"}
{
  "transactionConfig": {
    "computeUnitLimit": 200000,
    "heapSize": null,
    "loadedAccountsDataSizeLimit": 200000,
    "priorityFee": 50000
  },
  "versioned": true
}
```

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](/docs/de/rpc/transaction-v1) 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 `programIdIndex` → `accountKeys[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**

```json theme={"system"}
{
  "err": null,
  "fee": 12500
}
```

* `err: null` = Erfolg
* `err: {...}` = Fehler mit Fehlerdetails
* `fee` = Lamports, die für diese Transaktion berechnet wurden

**Saldoänderungen**

```json theme={"system"}
{
  "preBalances": [1075517572, 0, 207594496815, 0, 0, 0, 0, 0, 0],
  "postBalances": [1075502572, 1461600, 207594496815, 2001231920, 2039280, 0, 0, 0, 0]
}
```

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**

```json theme={"system"}
{
  "computeUnitsConsumed": 182564
}
```

Zeigt, wie viel Rechenbudget vom angeforderten Betrag genutzt wurde.

### Erweiterte Ausführungsdetails

**Innere Anweisungen**

```json theme={"system"}
{
  "innerInstructions": [
    {
      "index": 1,
      "instructions": [
        {
          "programIdIndex": 5,
          "accounts": [1, 2, 0],
          "data": "3Bxs4h24hBtQy9rw"
        }
      ]
    }
  ]
}
```

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**

```json theme={"system"}
{
  "logMessages": [
    "Program ComputeBudget111111111111111111111111111111 invoke [1]",
    "Program ComputeBudget111111111111111111111111111111 success",
    "Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 invoke [1]",
    "Program log: Instruction: Swap",
    "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [2]",
    "Program log: Create",
    "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]",
    "Program log: Instruction: GetAccountDataSize",
    "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1569 of 242833 compute units",
    "Program return: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA pQAAAAAAAAA=",
    "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success",
    "Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 success"
  ]
}
```

Protokollnachrichten bieten eine chronologische Nachverfolgung der Programmausführung und zeigen, welche Programme aufgerufen wurden und welche benutzerdefinierten Protokollnachrichten sie ausgegeben haben.

**Token-Saldoänderungen**

```json theme={"system"}
{
  "preTokenBalances": [],
  "postTokenBalances": [
    {
      "accountIndex": 1,
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "owner": "7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5",
      "uiTokenAmount": {
        "amount": "1000000",
        "decimals": 6,
        "uiAmount": 1.0,
        "uiAmountString": "1"
      }
    }
  ]
}
```

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:

```typescript theme={"system"}
// Transaction Success
function isTransactionSuccessful(meta: any): boolean {
  return meta.err === null;
}

function getTransactionFee(meta: any): number {
  return meta.fee;
}

function getComputeUnitsUsed(meta: any): number {
  return meta.computeUnitsConsumed;
}

// Balance Changes
function getBalanceChanges(meta: any, accountKeys: string[]): Array<{account: string, change: number}> {
  const changes = [];
  
  for (let i = 0; i < meta.preBalances.length; i++) {
    const change = meta.postBalances[i] - meta.preBalances[i];
    if (change !== 0) {
      changes.push({
        account: accountKeys[i],
        change: change
      });
    }
  }
  
  return changes;
}

// Program Calls
function getInvokedPrograms(meta: any, accountKeys: string[]): string[] {
  const programs = new Set<string>();
  
  meta.logMessages.forEach((log: string) => {
    const match = log.match(/Program ([1-9A-HJ-NP-Za-km-z]{32,}) invoke/);
    if (match) {
      programs.add(match[1]);
    }
  });
  
  return Array.from(programs);
}

// Token Transfers
function getTokenTransfers(meta: any): Array<{mint: string, from: string, to: string, amount: number}> {
  const transfers = [];
  
  // Compare pre and post token balances
  const preBalances = new Map();
  const postBalances = new Map();
  
  meta.preTokenBalances.forEach((balance: any) => {
    preBalances.set(balance.accountIndex, balance);
  });
  
  meta.postTokenBalances.forEach((balance: any) => {
    postBalances.set(balance.accountIndex, balance);
  });
  
  // Find changes
  for (const [accountIndex, postBalance] of postBalances) {
    const preBalance = preBalances.get(accountIndex);
    const preAmount = preBalance ? parseInt(preBalance.uiTokenAmount.amount) : 0;
    const postAmount = parseInt(postBalance.uiTokenAmount.amount);
    
    if (preAmount !== postAmount) {
      transfers.push({
        mint: postBalance.mint,
        account: postBalance.owner,
        change: postAmount - preAmount,
        decimals: postBalance.uiTokenAmount.decimals
      });
    }
  }
  
  return transfers;
}
```

***

## Komplettes Beispiel: Jupiter Swap Decoder

Hier ist ein komplettes Beispiel, das Jupiter Swap-Transaktionen dekodiert und bedeutungsvolle Informationen extrahiert:

```typescript [expandable] theme={"system"}
import bs58 from 'bs58';
import { subscribe, CommitmentLevel, SubscribeUpdate, LaserstreamConfig } from 'helius-laserstream';

interface SwapInfo {
  signature: string;
  slot: number;
  user: string;
  inputMint: string;
  outputMint: string;
  inputAmount: number;
  outputAmount: number;
  fee: number;
  success: boolean;
}

function convertBuffers(obj: any): any {
  if (!obj) return obj;
  if (Buffer.isBuffer(obj) || obj instanceof Uint8Array) {
    return bs58.encode(obj);
  }
  if (Array.isArray(obj)) {
    return obj.map(item => convertBuffers(item));
  }
  if (typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj).map(([key, value]) => [key, convertBuffers(value)])
    );
  }
  return obj;
}

function decodeJupiterSwap(txUpdate: any): SwapInfo | null {
  const tx = txUpdate.transaction;
  const meta = tx.meta;
  const message = tx.transaction.message;
  
  // Convert binary fields to readable format
  const signature = bs58.encode(tx.signature);
  const accountKeys = message.accountKeys.map((key: any) => bs58.encode(key));
  
  // Check if this is a Jupiter transaction
  const jupiterProgram = "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4";
  if (!accountKeys.includes(jupiterProgram)) {
    return null;
  }
  
  // Extract user (first account is typically the fee payer/user)
  const user = accountKeys[0];
  
  // Get token balance changes
  const tokenChanges = getTokenTransfers(meta);
  
  // Find input (negative change) and output (positive change)
  const inputChange = tokenChanges.find(change => change.change < 0);
  const outputChange = tokenChanges.find(change => change.change > 0);
  
  if (!inputChange || !outputChange) {
    return null;
  }
  
  return {
    signature,
    slot: parseInt(txUpdate.slot),
    user,
    inputMint: inputChange.mint,
    outputMint: outputChange.mint,
    inputAmount: Math.abs(inputChange.change),
    outputAmount: outputChange.change,
    fee: meta.fee,
    success: meta.err === null
  };
}

function getTokenTransfers(meta: any): Array<{mint: string, change: number}> {
  const transfers = [];
  
  const preBalances = new Map();
  const postBalances = new Map();
  
  meta.preTokenBalances.forEach((balance: any) => {
    preBalances.set(balance.accountIndex, balance);
  });
  
  meta.postTokenBalances.forEach((balance: any) => {
    postBalances.set(balance.accountIndex, balance);
  });
  
  for (const [accountIndex, postBalance] of postBalances) {
    const preBalance = preBalances.get(accountIndex);
    const preAmount = preBalance ? parseInt(preBalance.uiTokenAmount.amount) : 0;
    const postAmount = parseInt(postBalance.uiTokenAmount.amount);
    
    if (preAmount !== postAmount) {
      transfers.push({
        mint: postBalance.mint,
        change: postAmount - preAmount
      });
    }
  }
  
  return transfers;
}

async function runJupiterSwapMonitor() {
  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const request = {
    transactions: {
      "Jupiter-swaps": {
        vote: false,
        failed: false,
        accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4']
      }
    },
    commitment: CommitmentLevel.PROCESSED,
    accounts: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    (update: SubscribeUpdate) => {
      if (update.transaction) {
        const swapInfo = decodeJupiterSwap(update.transaction);
        if (swapInfo) {
          console.log('🔄 Jupiter Swap:');
          console.log(`  User: ${swapInfo.user}`);
          console.log(`  Input: ${swapInfo.inputAmount} of ${swapInfo.inputMint}`);
          console.log(`  Output: ${swapInfo.outputAmount} of ${swapInfo.outputMint}`);
          console.log(`  Fee: ${swapInfo.fee} lamports`);
          console.log(`  Success: ${swapInfo.success}`);
          console.log(`  Signature: ${swapInfo.signature}`);
          console.log('---');
        }
      }
    },
    console.error
  );

  console.log(`✅ Jupiter swap monitor started (id: ${stream.id})`);
  process.on('SIGINT', () => { stream.cancel(); process.exit(0); });
}

runJupiterSwapMonitor().catch(console.error);
```

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.
