Transacciones que no llegan
Una transacción que simula limpiamente aún puede no confirmarse nunca: los líderes la omiten durante la congestión, los blockhashes expiran o las pujas de tarifas pierden la subasta de prioridad. El triaje de producción separa el fallo de ejecución del fallo de inclusión antes de que cambies la lógica del programa.
Receta
Tarjeta de receta de referencia rápida, lista para copiar y pegar.
import {
createSolanaRpc,
createSolanaRpcSubscriptions,
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
signTransactionMessageWithSigners,
getSignatureFromTransaction,
} from "@solana/kit";
import {
getSetComputeUnitLimitInstruction,
getSetComputeUnitPriceInstruction,
} from "@solana-program/compute-budget";
// 1. Blockhash fresco 2. Instrucción de presupuesto de cómputo 3. enviar + suscribirseCuándo usar esto:
- Los usuarios ven "pendiente" indefinidamente en la billetera
- La tasa de error es baja pero el volumen de soporte para txs atascadas es alto
- Pico post-despliegue en firmas no confirmadas
- Bots compitiendo durante la acuñación de NFT o el lanzamiento de tokens
- Necesitas SLOs para la latencia de llegada de transacciones
Ejemplo de trabajo
import { createSolanaRpc, createSolanaRpcSubscriptions } from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
const subs = createSolanaRpcSubscriptions(process.env.WS_URL!);
async function sendWithLandingWatch(wireBase64: string, signature: string) {
await rpc.sendTransaction(wireBase64, {
encoding: "base64",
skipPreflight: false,
maxRetries: 0,
preflightCommitment: "confirmed",
}).send();
const started = Date.now();
const sub = await subs.signatureNotifications(signature, { commitment: "confirmed" });
for await (const note of sub) {
if (note.value.err) throw new Error(`Error en la cadena: ${JSON.stringify(note.value.err)}`);
return { landedMs: Date.now() - started, slot: note.context.slot };
}
throw new Error("La suscripción finalizó sin confirmación");
}Lo que esto demuestra:
maxRetries: 0para que tu aplicación controle la política de reintentos con blockhashes frescossignatureNotificationsde WebSocket en lugar de sondeargetSignatureStatuses- Medición de la latencia de llegada para paneles de SLO
- Tratamiento de errores
erren cadena por separado de nunca llegar
Inmersión Profunda
Cómo funciona
- Gulf Stream reenvía transacciones a los próximos líderes; no hay un orden garantizado global del mempool.
- Blockhash reciente establece la vida útil de la transacción (aproximadamente 60-90 segundos de validez). Las txs expiradas se descartan silenciosamente.
- Tarifa de prioridad (
set_compute_unit_price) puja micro-lamports por CU; las pujas más altas tienden a llegar primero bajo carga. - Límite de unidades de cómputo limita la ejecución; una subestimación de la CU puede causar ineficiencia en la programación.
- Tablas de búsqueda de direcciones (ALT) reducen el tamaño de las txs para que quepan más txs por bloque.
Modos de fallo de llegada
| Señal | Causa probable | Mitigación |
|---|---|---|
| Sin firma en el explorador | RPC nunca reenvió | Proveedor secundario, comprueba los límites de tasa 429 |
| Firma existente, sin confirmación | Descartada / superada en la puja | Aumenta la tarifa de prioridad, agrupa vía Jito |
BlockhashNotFound al reenviar | Vida útil obsoleta | Obtén un nuevo blockhash, vuelve a firmar |
| Confirmado y luego revertido | Bifurcación rara; generalmente lectura de compromiso incorrecta | Espera confirmed o finalized |
Notas de TypeScript
// Obtener tarifas de priorización recientes para tus cuentas de programa
const fees = await rpc.getRecentPrioritizationFees({
lockedWritableAccounts: [programId],
}).send();
const p75 = fees.value.map((f) => f.prioritizationFee).sort((a, b) => a - b)[
Math.floor(fees.value.length * 0.75)
] ?? 1;Errores comunes
- Reenvíos idénticos - Reenviar los mismos bytes firmados después de la expiración desperdicia tarifas. Solución: Reconstruye el mensaje con un nuevo blockhash y vuelve a firmar.
- Precio de CU predeterminado de cero durante las acuñaciones - Tus txs pierden cada subasta. Solución: Oráculo de tarifas dinámico de
getRecentPrioritizationFees. - Confiar en
processedpara la experiencia de usuario - Las bifurcaciones pueden deshacerprocessed. Solución: Muestra el éxito enconfirmed; liquida el dinero enfinalizedsi es necesario. - Transacciones enormes sin ALT - Excede el tamaño del paquete; RPC puede aceptar pero los líderes la descartan. Solución: Compila con tablas de búsqueda; divide las instrucciones.
- Tormentas de reintentos de RPC - Cliente y RPC reintentan; confusión de nonces y límites de tasa. Solución: Claves de idempotencia a nivel de aplicación;
maxRetries: 0al enviar. - Ignorar la congestión por hora del día - Los picos de Mainnet difieren de devnet. Solución: Rastrea la llegada p95 por hora; escala automáticamente el multiplicador de tarifas.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
| Bundles de Jito | Debe llegar atómicamente con propina | Transferencias simples de bajo valor |
| RPC privado / SWQoS | Línea de base de dApp de producción | Solo desarrollo local |
| UX de compromiso inferior | Paneles de solo lectura | Confirmación de pago |
| Servicio de cola de transacciones | Lote de backend con política de tarifas | Billeteras completamente del lado del cliente |
Preguntas frecuentes
¿Es una transacción descartada un error del programa?
Normalmente no. Los fallos de inclusión son problemas económicos y de programación de red, a menos que la simulación también falle.
¿Cuánto tiempo es válido un blockhash?
Aproximadamente 60-90 segundos (150 slots). Siempre obtén uno nuevo antes de reenviar.
¿Debo usar `skipPreflight: true` para llegar más rápido?
Solo en rutas de servidor confiables con simulación separada. Omitir el preflight aumenta la tasa de fallos en cadena.
¿Qué tarifa de prioridad debo usar?
Comienza en el p75 de getRecentPrioritizationFees para tus cuentas escribibles; ajusta según las métricas de llegada.
¿Por qué devnet llega instantáneamente pero mainnet no?
Devnet tiene baja contención. Los mercados de tarifas de Mainnet y la programación de líderes dominan.
¿Ayudan las ALT a la llegada?
Reducen el tamaño de las txs, mejorando la propagación y el ajuste en los bloques, especialmente para rutas DeFi complejas.
¿Cómo detecto descartes silenciosos?
Tiempo de espera en signatureNotifications; compara con getSignatureStatuses en un RPC secundario.
¿Puedo reenviar la misma firma?
Sí, los envíos duplicados de los mismos bytes son idempotentes hasta la expiración. Después de la expiración, necesitas una nueva firma.
¿Qué es SWQoS?
QoS ponderada por stake da a los proveedores de RPC con conexión en stake un mejor reenvío a los líderes, común en niveles de pago.
¿Cuándo debo usar bundles de Jito?
Acuñaciones de alta contención, arbitraje o cualquier flujo donde la llegada atómica justifique una propina.
Relacionado
- Depuración de transacciones fallidas - simulación y errores de programa
- Interrupciones de RPC e Infraestructura - conmutación por error del proveedor
- Antes/Después: Haciendo que las transacciones lleguen de manera confiable - estudio de caso
Versiones de Stack: 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.