Cómo fluye una transacción
Una transacción de Solana viaja desde tu cliente a través de la red hasta un líder productor de bloques, se ejecuta atómicamente en la cadena y recibe votos hasta alcanzar el nivel de compromiso elegido. Comprender esta ruta explica la latencia, los fallos y la estrategia de reintentos.
Receta
Tarjeta de referencia rápida - lista para copiar y pegar.
Cliente construye → Firma → sendTransaction → Líder recibe (Gulf Stream)
→ Ordenamiento PoH → Ejecución Sealevel → Propagación de bloque (Turbine)
→ Votos del validador (TowerBFT) → Sondeo de confirmación RPC
import { createSolanaRpc, createSolanaRpcSubscriptions } from "@solana/kit";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const rpcSubs = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
// Después de que sendTransaction devuelva una firma:
const sub = await rpcSubs.signatureNotifications(signature, { commitment: "confirmed" });Cuándo usar esto:
- Para depurar por qué una transacción fue descartada en lugar de rechazada
- Para elegir el nivel de compromiso adecuado para tu UX
- Para comprender el tiempo de reintento en relación con la expiración del blockhash
- Para explicar a los usuarios la diferencia entre "procesado" en subsegundos y "finalizado" en ~12s
- Para diseñar la resubmisión de transacciones idempotentes
Ejemplo de trabajo
import {
createSolanaRpc,
createSolanaRpcSubscriptions,
createKeyPairSignerFromBytes,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signTransactionMessageWithSigners,
getSignatureFromTransaction,
pipe,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
import { readFileSync } from "node:fs";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const rpcSubs = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
const secret = Uint8Array.from(
JSON.parse(readFileSync(process.env.HOME + "/.config/solana/id.json", "utf8")),
);
const signer = await createKeyPairSignerFromBytes(secret);
// 1. Obtener blockhash reciente (vida útil de la transacción)
const { value: blockhash } = await rpc.getLatestBlockhash().send();
// 2. Construir y firmar
const message = pipe(
createTransactionMessage({ version: 0 }),
(m) => setTransactionMessageFeePayer(signer.address, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(blockhash, m),
(m) =>
appendTransactionMessageInstruction(
getTransferSolInstruction({
source: signer.address,
destination: signer.address, // demo de autotransferencia
amount: 1n,
}),
m,
),
);
const signed = await signTransactionMessageWithSigners(message, [signer]);
const signature = getSignatureFromTransaction(signed);
// 3. Enviar
await rpc.sendTransaction(signed, { encoding: "base64" }).send();
console.log("Enviado:", signature);
// 4. Esperar confirmación
const notifications = await rpcSubs.signatureNotifications(signature, {
commitment: "confirmed",
});
for await (const note of notifications) {
if (note.value.err) {
console.error("Falló en la cadena:", note.value.err);
} else {
console.log("Confirmado en el slot:", note.context.slot);
}
break;
}Lo que esto demuestra:
- El blockhash vincula la transacción a un estado reciente del ledger (validez de ~60-90 segundos)
sendTransactionla transmite al RPC, que la reenvía a través de Gulf StreamsignatureNotificationsde WebSocket es más eficiente que el sondeo degetSignatureStatusesconfirmedsignifica que una supermayoría votó sobre el bloque;finalizedes más fuerte
Inmersión Profunda
Cómo funciona
- Construir - el cliente ensambla instrucciones, cuentas y un blockhash reciente
- Firmar - los firmantes requeridos producen firmas Ed25519
- Enviar - el nodo RPC la recibe y la reenvía al líder actual/próximo
- Programar - el líder ordena las transacciones usando las marcas de Proof of History
- Ejecutar - Sealevel ejecuta instrucciones en paralelo donde los bloqueos de cuentas lo permiten
- Confirmar - los cambios de la transacción se escriben en la base de datos de cuentas
- Votar - los validadores votan sobre el bloque; TowerBFT acumula bloqueos
- Confirmar - el RPC informa el estado en el nivel de compromiso solicitado
Niveles de compromiso
| Nivel | Latencia | Seguridad | Caso de uso |
|---|---|---|---|
processed | ~400 ms | Puede ser descartada/reordenada | Solo pistas de UI |
confirmed | ~1-2 s | Supermayoría votó | La mayoría de las acciones de dapp |
finalized | ~12-13 s | Enraizada, sin retroceso | Liquidaciones de alto valor |
Modos de fallo
- Descartada - nunca entró en un bloque (salto del líder, tarifa de baja prioridad, blockhash expirado)
- Rechazada - entró pero el programa devolvió un error (fondos insuficientes, violación de restricciones)
- Fallo de simulación - la prevalidación del RPC detectó el error antes del envío
Trampas
- Tratar
processedcomo final - las bifurcaciones pueden reordenar a este nivel. Solución: esperarconfirmedofinalizedantes de una UX irreversible. - No refrescar el blockhash al reintentar - las transacciones expiradas se descartan silenciosamente. Solución: obtener un nuevo blockhash y volver a firmar en cada reintento.
- Ignorar errores de prevalidación -
skipPreflight: trueoculta los fallos de simulación. Solución: simular primero en desarrollo; decodificar errores antes de reintentar. - Sondear en un bucle ajustado - sobrecarga el RPC y aún así se pierde confirmaciones rápidas. Solución: usar suscripciones de firma WebSocket.
- Reenvío de la misma firma - válido e idempotente, pero inútil si el blockhash expiró. Solución: solo reenviar la misma firma dentro de la ventana del blockhash.
- Salto del líder durante la congestión - las transacciones se ponen en cola por más tiempo. Solución: agregar tarifas de prioridad a través del Programa de Presupuesto de Cómputo.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
| Suscripción de firma WebSocket | UX de confirmación en tiempo real | Scripts CLI de un solo uso |
Sondeo getSignatureStatuses | Scripts simples | Aplicaciones de alto rendimiento |
skipPreflight: true | Transacciones conocidas como buenas, latencia crítica | Desarrollo y depuración |
| Nonce duradero | Sesiones de firma de larga duración | Transacciones de usuario normales |
Preguntas frecuentes
¿Qué sucede inmediatamente después de sendTransaction?
El RPC reenvía tu transacción a los líderes a través de Gulf Stream. Todavía no está confirmada, solo enviada.
¿Cuánto tiempo es válido un blockhash?
Aproximadamente 60-90 segundos (alrededor de 150 slots). Después de la expiración, la transacción es rechazada incluso si se reenvía.
¿Cuál es la diferencia entre descartada y rechazada?
Descartada: nunca ejecutada en un bloque. Rechazada: ejecutada pero el programa devolvió un error; está en la cadena con un estado de fallo.
¿Por qué la finalidad tarda ~12 segundos?
TowerBFT requiere suficientes votos de validadores a través de múltiples slots para enraizar un bloque. Esto toma aproximadamente 32 slots.
¿Puedo acelerar la confirmación?
Las tarifas de prioridad aumentan la prioridad de programación. Los bundles de Jito ayudan durante períodos de alta actividad de MEV. No puedes saltarte el proceso de votación.
¿Qué es la simulación de prevalidación?
El RPC ejecuta tu transacción contra el estado actual antes de transmitirla. Detecta la mayoría de los errores de forma temprana.
¿Es sendTransaction síncrono?
Devuelve una firma inmediatamente. La confirmación es asíncrona; sondea o suscríbete por separado.
¿Qué es Gulf Stream?
Un protocolo que reenvía transacciones directamente a los próximos líderes de bloques en lugar de usar un mempool tradicional.
¿Se ejecutan todas las instrucciones de una transacción atómicamente?
Sí. Si alguna instrucción falla, toda la transacción se revierte (a menos que un programa capture y maneje errores de CPI).
¿Puede una transacción tener éxito parcialmente?
No. Todas las instrucciones tienen éxito o ninguno de los cambios de estado se confirma.
¿Qué compromiso debe usar una interfaz de pago?
confirmed para la mayoría de los pagos. finalized para acciones de alto valor o irreversibles.
¿Cómo rastreo una transacción en un explorador?
Usa https://explorer.solana.com/tx/<SIGNATURE>?cluster=devnet e inspecciona los logs, CU y el desglose de instrucciones.
Relacionado
- Conceptos básicos de Solana - ejemplo de seguimiento de extremo a extremo
- Slots, Bloques y Épocas - contexto temporal
- Keypairs y Direcciones - paso de firma
Versiones de la pila: Esta página fue escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, y LiteSVM 0.6.x.