- Mensaje → Lo que el usuario quería hacer (su propuesta firmada)
- Metadatos → Lo que realmente ocurrió (el resultado de la ejecución)
<Buffer 00 bf a0 e8...>, en lugar de direcciones y firmas legibles.
Esta guía te muestra cómo: Decodificar esos datos binarios a un formato legible, extraer información relevante y comprender la historia completa de la transacción, desde la propuesta hasta la ejecución.
Un stream en vivo, sin decodificación
Ejecuta el cliente mínimo que aparece a continuación. Los indicadores del filtro descartan las transacciones de voto y las fallidas, mientras que el arregloaccountInclude limita los resultados a la actividad que interactúa con el identificador del programa Jupiter.
filters, createdAt— junto con una rama transaction que oculta dos elementos secundarios:
transaction.transaction.transaction→ el mensaje firmadotransaction.transaction.meta→ los metadatos de ejecución
Uint8Array permanece opaco.
Cuando ejecutes el script con la función de decodificación, verás la estructura anidada real con direcciones legibles:
Decodificación de los datos binarios
¿Por qué decodificar? Los datos sin procesar de Laserstream contienen firmas, claves de cuentas y hashes como objetos binariosUint8Array ilegibles. Debes convertirlos en cadenas base58 para comprender la transacción.
La solución: Laserstream usa Yellowstone gRPC, que proporciona utilidades de decodificación integradas. En lugar de escribir decodificadores independientes para cada tipo de campo, usamos una sola función recursiva que convierte todos los datos binarios a un formato legible.
Descripción de la estructura de la transacción
Ahora que podemos ver los datos decodificados, exploremos las dos partes principales de cada actualización de transacción de Laserstream. Recuerda que, en nuestro ejemplo inicial, cada transacción contiene dos objetos clave:- Mensaje (propuesta) →
transaction.transaction.transaction→ el mensaje firmado (la propuesta del usuario) - Metadatos (ejecución) →
transaction.transaction.meta→ los metadatos de ejecución (la respuesta del validador)
La propuesta: todo lo que contiene message
El usuario crea un mensaje que especifica qué, quién y hasta cuándo. Así puedes decodificar cada parte:Encabezado de la transacción
numRequiredSignatures indica al validador cuántas firmas debe verificar, mientras que los dos valores numReadonly* marcan las cuentas que el entorno de ejecución puede tratar como de solo lectura, lo que permite la ejecución en paralelo.
Diccionario de claves de cuentas
accountKeys es una lista simple de claves públicas que funciona como tabla de consulta. Cada entero posterior de la transacción —programIdIndex y cada elemento del arreglo accounts de una instrucción— apunta a esta lista mediante un índice, lo que ahorra más de un kilobyte por mensaje.
Protección contra repetición
recentBlockhash vence cuando sale de los últimos 150 hashes de bloque, aproximadamente noventa segundos en mainnet.
Instrucciones: los comandos reales
- Identificador del programa (
programIdIndex): Apunta a una dirección del arregloaccountKeys(p. ej., índice 10 =ComputeBudget111111111111111111111111111111) - Cuentas (
accounts): Una cadena codificada en base58 que representa los índices de las cuentas con las que interactúa esta instrucción - Datos (
data): Los datos reales de la instrucción codificados en base58
convertBuffers, las cuentas aparecen en base58, pero en realidad contienen índices de cuentas (p. ej., "3vtmrQMafzDoG2CBz1iqgXPTnC" se decodifica como los índices [21, 19, 12, 17, 2, 6, 1, 22])
Gracias a este diseño, en lugar de repetir direcciones completas de 32 bytes, cada instrucción solo hace referencia a posiciones de la tabla de consulta.
Firmas: prueba de autorización
signatures contiene las firmas criptográficas que demuestran que las cuentas requeridas autorizaron esta transacción. La cantidad de firmas debe coincidir con header.numRequiredSignatures.
Consultas de tablas de direcciones
versioned es true, addressTableLookups aparece con una tabla en cadena y dos listas de índices. Las tablas de consulta elevan a decenas el límite estricto de direcciones y mantienen el paquete por debajo de la MTU de 1232 bytes.
Transacción v1: presupuesto de cómputo en el encabezado
La transacción v1 (SIMD-0385, Agave 4.2) agrega un campo más al mensaje:transactionConfig.
instructions de una transacción v1 nunca contiene una entrada ComputeBudget111111111111111111111111111111. priorityFee es la comisión total en lamports de toda la transacción, no microlamports por unidad de cómputo. Un campo null significa que el remitente no lo configuró. Los mensajes heredados y v0 no tienen transactionConfig, por lo que su presencia identifica una transacción v1.
Debes comprobar dos aspectos en tu decodificador:
- Extracción de la comisión de prioridad. Lee
transactionConfig.priorityFeecuando exista y recurre al análisis de las instrucciones de ComputeBudget solo para transacciones heredadas y v0. El código que únicamente analiza instrucciones interpreta que todas las transacciones v1 pagan una comisión de prioridad de cero. - Versión de Proto.
yellowstone-grpc-proto12.6.0 es la primera versión que incluye los campos de v1, ehelius-laserstream0.8.4 (JavaScript), 0.6.3 (Rust) y 0.2.0 (Go) son las primeras versiones del SDK creadas sobre ella. Las versiones anteriores descartantransactionConfigde forma silenciosa.
Cómo se conecta todo: el flujo
Esto es lo que ocurre desde los principios básicos:- Crear la tabla de consulta:
accountKeysenumera todas las direcciones con las que interactuará esta transacción - Definir las reglas:
headerespecifica cuántas firmas se requieren y qué cuentas son de solo lectura - Crear los comandos: Cada
instructionapunta a:- Un programa (mediante
programIdIndex→accountKeys[index]) - Las cuentas que necesita (mediante
accounts→ varias posiciones deaccountKeys[index]) - Los datos de la instrucción (codificados en
data)
- Un programa (mediante
- Agregar la autorización:
signaturesdemuestra que las cuentas requeridas aprobaron esta transacción - Definir el vencimiento:
recentBlockhashgarantiza que esta transacción no pueda repetirse más adelante
La ejecución: todo lo que contiene meta
Mientras que el mensaje muestra lo que el usuario quería hacer, los metadatos muestran lo que realmente ocurrió cuando los validadores ejecutaron la transacción.Información básica de la ejecución
Éxito/Errorerr: null= éxitoerr: {...}= error con detallesfee= lamports cobrados por esta transacción
accountKeys por índice:
- Cuenta 0: Perdió 15000 lamports (pago de la comisión)
- Cuenta 1: Ganó 1461600 lamports (se creó una cuenta nueva)
- Cuenta 3: Ganó 2001231920 lamports (cuenta del programa)
Detalles avanzados de la ejecución
Instrucciones internasPatrones prácticos de decodificación
Estos son algunos patrones comunes para extraer información útil de las transacciones decodificadas:Ejemplo completo: decodificador de swaps de Jupiter
Este es un ejemplo completo que decodifica transacciones de swaps de Jupiter y extrae información relevante:Puntos clave
- Estructura de dos partes: Cada transacción tiene un mensaje (lo que se solicitó) y metadatos (lo que realmente ocurrió)
- Decodificación binaria: Usa
bs58.encode()para convertir campos binarios en cadenas base58 legibles - Consultas de claves de cuentas: Las instrucciones hacen referencia a las cuentas mediante su índice en el arreglo
accountKeys - Seguimiento de saldos: Compara
preBalancesypostBalancespara ver qué cambió - Transacción v1: Lee el presupuesto de cómputo y la comisión de prioridad de
transactionConfigcuando esté presente; las transacciones v1 no tienen instrucciones de ComputeBudget