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

# Decodificação e Análise de Dados de Transação

> Aprenda como decodificar e analisar dados de transações do Laserstream para melhor compreensão das transações Solana.

**Ao receber dados de transação do Laserstream, há duas coisas importantes a observar:**

* **Message** → O que o usuário queria fazer (sua proposta assinada)
* **Meta** → O que realmente aconteceu (o resultado da execução)

**O desafio:** Dados brutos de transação vêm como arrays de bytes binários como `<Buffer 00 bf a0 e8...>` em vez de endereços e assinaturas legíveis.

**Este guia mostra como:** Decodificar esses dados binários em formato legível, extrair informações significativas e entender toda a história da transação desde a proposta até a execução.

***

## Uma transmissão ao vivo, sem decodificação

Execute o cliente mínimo abaixo. As flags de filtro descartam votos e transações falhas, e o array `accountInclude` limita os resultados a atividades que tocam o ID do programa Jupiter.

```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);
```

Seu console agora mostra um wrapper—`filters`, `createdAt` mais um branch `transaction` que oculta duas crianças:

* `transaction.transaction.transaction` → a **mensagem** assinada
* `transaction.transaction.meta` → o **meta** da execução

```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
}
```

Tudo que parece `Uint8Array` permanece opaco por enquanto.

Quando você executa o script com a função de decodificação, verá a estrutura aninhada real com endereços legíveis:

```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"
}
```

***

## Decodificando os dados binários

**Por que decodificar?** Dados brutos do Laserstream contêm assinaturas, chaves de conta e hashes como objetos binários `Uint8Array` que são ilegíveis. Você precisa convertê-los para strings base58 para entender a transação.

**A solução:** O Laserstream usa Yellowstone gRPC, que fornece utilitários de decodificação integrados. Em vez de escrever decodificadores separados para cada tipo de campo, usamos uma função recursiva que converte todos os dados binários em formato legível.

```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();
```

Esta abordagem aproveita a decodificação integrada enquanto lida com os campos binários que precisam de conversão manual. A estrutura da transação já está analisada - você só precisa converter os campos binários para um formato legível.

***

## Entendendo a estrutura da transação

Agora que podemos ver os dados decodificados, vamos explorar as duas partes principais de cada atualização de transação do Laserstream. Lembre-se do nosso exemplo inicial de que cada transação contém dois objetos chave:

* **Message (Proposta)** → `transaction.transaction.transaction` → a mensagem assinada (proposta do usuário)
* **Meta (Execução)** → `transaction.transaction.meta` → os metadados da execução (resposta do validador)

Esta estrutura em duas partes conta uma história completa: o que o usuário solicitou versus o que realmente aconteceu. Vamos examinar cada parte em detalhe.

***

## A proposta: tudo dentro da mensagem

O usuário cria uma mensagem que especifica *o que*, *quem* e *até quando*. Veja como decodificar cada parte:

### Cabeçalho da Transação

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

`numRequiredSignatures` diz ao validador quantas assinaturas verificar, enquanto os dois valores `numReadonly*` rotulam contas que o runtime pode tratar como somente leitura, permitindo execução paralela.

### Dicionário de Chaves de Conta

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

`accountKeys` é uma lista simples de chaves públicas que atua como uma tabela de consulta. Cada inteiro posterior na transação - `programIdIndex`, cada elemento em um array `accounts` de uma instrução - aponta de volta para esta lista por índice, salvando mais de um quilobyte por mensagem.

### Proteção Contra Repetição

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

`recentBlockhash` expira uma vez que sai dos últimos 150 hashes de bloco, aproximadamente noventa segundos no mainnet.

### Instruções: Os Comandos Reais

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

Cada instrução contém três partes principais:

* **ID do Programa** (`programIdIndex`): Aponta para um endereço no array `accountKeys` (por exemplo, índice 10 = `ComputeBudget111111111111111111111111111111`)
* **Contas** (`accounts`): Uma string codificada em base58 representando quais índices de conta essa instrução toca
* **Dados** (`data`): Os dados reais da instrução codificados como base58

Devido à função `convertBuffers`, contas aparecem como base58, mas na verdade contêm índices de conta (por exemplo, `"3vtmrQMafzDoG2CBz1iqgXPTnC"` decodifica para índices \[21, 19, 12, 17, 2, 6, 1, 22])

Este design significa que em vez de repetir endereços de 32 bytes completos, cada instrução apenas referencia posições na tabela de consulta.

### Assinaturas: Prova de Autorização

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

`signatures` contém as assinaturas criptográficas provando que as contas necessárias autorizaram esta transação. O número de assinaturas deve coincidir com `header.numRequiredSignatures`.

### Consultas de Tabela de Endereços

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

Se `versioned` é `true`, `addressTableLookups` aparece com uma tabela em cadeia e duas listas de índices. Tabelas de consulta elevam a limitação no número de endereços para dezenas, mantendo o pacote abaixo do MTU de 1.232 bytes.

### Como Tudo se Conecta: O Fluxo

Aqui está o que acontece desde os primeiros princípios:

1. **Construa a tabela de consulta**: `accountKeys` lista todos os endereços que esta transação tocará
2. **Estabeleça as regras**: `header` especifica quantas assinaturas são necessárias e quais contas são somente leitura
3. **Crie os comandos**: Cada `instruction` aponta para:
   * Um programa (via `programIdIndex` → `accountKeys[index]`)
   * As contas que necessita (via `accounts` → múltiplas posições `accountKeys[index]`)
   * Os dados da instrução (codificados em `data`)
4. **Adicione autorização**: `signatures` comprova que as contas necessárias aprovaram esta transação
5. **Defina a expiração**: `recentBlockhash` garante que esta transação não possa ser repetida posteriormente

***

## A execução: tudo dentro do meta

Enquanto a mensagem mostra o que o usuário queria fazer, o meta mostra o que realmente aconteceu quando os validadores executaram a transação.

### Informações básicas de execução

**Sucesso/Falha**

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

* `err: null` = sucesso
* `err: {...}` = falha com detalhes de erro
* `fee` = lamports cobrados por esta transação

**Mudanças de Saldo**

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

Os arrays de saldo correspondem ao array `accountKeys` por índice:

* Conta 0: Perdeu 15000 lamports (pagamento de taxa)
* Conta 1: Ganhou 1461600 lamports (nova conta criada)
* Conta 3: Ganhou 2001231920 lamports (conta do programa)

**Uso de Computação**

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

Mostra quanto do orçamento de computação foi usado (do total solicitado).

### Detalhes avançados de execução

**Instruções Internas**

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

Instruções internas são instruções adicionais que programas chamaram durante a execução. Não fazem parte da transação original, mas foram acionadas pelas instruções principais.

**Mensagens de Log**

```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"
  ]
}
```

Mensagens de log fornecem um rastreamento cronológico da execução do programa, mostrando quais programas foram chamados e qualquer mensagem de log personalizada que eles emitiram.

**Mudanças de Saldo de Token**

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

As mudanças de saldo de token mostram os estados antes/depois para contas de token SPL, incluindo as quantidades legíveis com o tratamento decimal adequado.

***

## Padrões práticos de decodificação

Aqui estão padrões comuns para extrair informações úteis de transações decodificadas:

```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;
}
```

***

## Exemplo completo: decodificador de troca Jupiter

Aqui está um exemplo completo que decodifica transações de troca Jupiter e extrai informações significativas:

```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);
```

Este exemplo mostra como combinar decodificação de mensagem com análise de meta para extrair informações relevantes para negócios de transações DeFi complexas.

***

## Pontos principais

* **Estrutura de duas partes**: Cada transação tem uma **mensagem** (o que foi solicitado) e **meta** (o que realmente aconteceu)
* **Decodificação binária**: Use `bs58.encode()` para converter campos binários em strings legíveis base58
* **Consultas de chave de conta**: Instruções referenciam contas por índice no array `accountKeys`
* **Rastreamento de saldo**: Compare `preBalances` e `postBalances` para ver o que mudou

A chave para entender transações Solana é reconhecer que elas são projetadas para eficiência: em vez de repetir endereços, usam tabelas de consulta e índices para minimizar o tamanho da transação enquanto maximizam a densidade da informação.
