Skip to main content
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.
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
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:

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

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

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

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

Instruções: Os Comandos Reais

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

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

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.

Transação v1: Orçamento de Cálculo no Cabeçalho

A Transação v1 (SIMD-0385, Agave 4.2) adiciona mais um campo à mensagem: transactionConfig.
Uma transação v1 carrega seu orçamento de cálculo aqui em vez de nas instruções do programa ComputeBudget, então o array instructions de uma transação v1 nunca contém uma entrada ComputeBudget111111111111111111111111111111. priorityFee é a taxa total em lamports para toda a transação, não micro-lamports por unidade de cálculo. Um campo null significa que o remetente não o definiu. Mensagens legadas e v0 não possuem transactionConfig, então sua presença identifica uma transação v1. Duas coisas para verificar em seu decodificador:
  • Extração de taxa de prioridade. Leia transactionConfig.priorityFee quando existir, e recorra à análise das instruções do ComputeBudget apenas para transações legadas e v0. Código que apenas analisa instruções lê cada transação v1 como pagando taxa de prioridade zero.
  • Versão Proto. yellowstone-grpc-proto 12.6.0 é a primeira versão que contém os campos v1, e helius-laserstream 0.8.4 (JavaScript), 0.6.3 (Rust) e 0.2.0 (Go) são as primeiras versões do SDK construídas sobre ela. Versões mais antigas descartam transactionConfig silenciosamente.
Veja Suporte à Transação v1 para a lista completa de alterações.

Como Tudo se Conecta: O Fluxo

Veja o que acontece a partir dos primeiros princípios:
  1. Construa a tabela de consulta: accountKeys lista todos os endereços que esta transação tocará
  2. Defina 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 de que precisa (via accounts → múltiplas posições accountKeys[index])
    • Os dados da instrução (codificados em data)
  4. Adicione autorização: signatures prova que as contas requeridas aprovaram esta transação
  5. Defina a expiração: recentBlockhash garante que esta transação não poderá ser reproduzida posteriormente

A execução: tudo dentro de 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/Fracasso
  • err: null = sucesso
  • err: {...} = falha com detalhes do erro
  • fee = lamports cobrados por esta transação
Mudanças de Saldo
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 de programa)
Uso de Cálculo
Mostra quanto do orçamento de cálculo foi usado (do valor solicitado).

Detalhes avançados de execução

Instruções Internas
Instruções internas são instruções adicionais que programas chamaram durante a execução. Elas não fazem parte da transação original, mas foram acionadas pelas instruções principais. Mensagens de Log
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 produziram. Mudanças de Saldo de Token
As mudanças de saldo de token mostram os estados antes/depois para as contas de token SPL, incluindo os valores legíveis por humanos com tratamento adequado de decimais.

Padrões práticos de decodificação

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

Exemplo completo: decodificador de troca Jupiter

Aqui está um exemplo completo que decodifica transações de troca Jupiter e extrai informações significativas:
Este exemplo mostra como combinar a decodificação de mensagens com a análise de meta para extrair informações relevantes de negócios de transações complexas de DeFi.

Principais pontos

  • 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 base58 legíveis
  • Consultas de chave de conta: Instruções referenciam contas por índice no array accountKeys
  • Acompanhamento de saldos: Compare preBalances e postBalances para ver o que mudou
  • Transação v1: Leia o orçamento de cálculo e a taxa de prioridade de transactionConfig quando presente; transações v1 não têm instruções de ComputeBudget
A chave para entender as transações Solana é reconhecer que elas são projetadas para eficiência: em vez de repetir endereços, elas usam tabelas de consulta e índices para minimizar o tamanho da transação, maximizando a densidade de informações.