Transações que Não Pousam
Uma transação que simula limpo ainda pode nunca confirmar: leaders a pulam durante congestionamento, blockhashes expiram ou lances de fee perdem o leilão de prioridade. Triagem em produção separa falha de execução de falha de inclusão antes de mudar lógica de programa.
Receita
Card de receita de referência rápida - pronto para copiar e colar.
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. ix compute budget 3. send + subscribeQuando usar:
- Usuários veem "pendente" indefinidamente na wallet
- Taxa de erro é baixa mas volume de suporte para txs presas é alto
- Pico pós-deploy de signatures não confirmadas
- Bots competindo durante mint NFT ou launch de token
- Você precisa de SLOs para latência de pouso de transação
Exemplo Funcional
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(`On-chain err: ${JSON.stringify(note.value.err)}`);
return { landedMs: Date.now() - started, slot: note.context.slot };
}
throw new Error("Subscription ended without confirmation");
}O que isso demonstra:
maxRetries: 0para seu app controlar política de retry com blockhashes frescos- WebSocket
signatureNotificationsem vez de pollinggetSignatureStatuses - Medir latência de pouso para dashboards SLO
- Tratar
erron-chain separado de nunca pousar
Aprofundamento
Como Funciona
- Gulf Stream encaminha transações para leaders futuros; não há ordenação global garantida de mempool.
- Recent blockhash define lifetime da transação (~60-90 segundos de validade). Txs expiradas são descartadas silenciosamente.
- Priority fee (
set_compute_unit_price) oferece micro-lamports por CU; lances maiores tendem a pousar primeiro sob carga. - Limite de compute units limita execução; CU sub-ofertado pode causar ineficiência de scheduling.
- Address Lookup Tables (ALTs) reduzem tamanho da tx para mais txs caberem por bloco.
Modos de Falha de Pouso
| Sinal | Causa provável | Mitigação |
|---|---|---|
| Sem signature no explorer | RPC nunca encaminhou | Provedor secundário, verifique limites 429 |
| Signature existe, sem confirmação | Descartada / superada | Aumente priority fee, bundle via Jito |
BlockhashNotFound no resend | Lifetime obsoleto | Busque novo blockhash, re-assine |
| Confirmada depois revertida | Fork raro; geralmente leitura de commitment errada | Espere confirmed ou finalized |
Notas TypeScript
// Busque priority fees recentes para suas contas 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;Erros Comuns
- Resubmits idênticos - Rebroadcast dos mesmos bytes assinados após expiração desperdiça fees. Correção: Reconstrua mensagem com novo blockhash e re-assine.
- Preço de CU padrão zero durante mints - Suas txs perdem todo leilão. Correção: Oráculo de fee dinâmico de
getRecentPrioritizationFees. - Confiar em
processedpara UX - Forks podem desfazerprocessed. Correção: Mostre sucesso emconfirmed; liquide dinheiro emfinalizedse necessário. - Transações enormes sem ALTs - Excede tamanho do pacote; RPC pode aceitar mas leaders descartam. Correção: Compile com lookup tables; divida instructions.
- Tempestades de retry RPC - Client e RPC ambos fazem retry; confusão de nonce e rate limits. Correção: Chaves de idempotência no app;
maxRetries: 0no send. - Ignorar congestionamento por horário - Picos mainnet diferem de devnet. Correção: Rastreie p95 de pouso por hora; escale multiplicador de fee automaticamente.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Bundles Jito | Deve pousar atomicamente com tip | Transferências simples de baixo valor |
| RPC privado / SWQoS | Baseline dApp em produção | Apenas desenvolvimento local |
| UX de commitment menor | Dashboards read-only | Confirmação de pagamento |
| Serviço de fila de transação | Batching backend com política de fee | Wallets totalmente client-side |
FAQs
Tx descartada é bug de programa?
Geralmente não. Falhas de inclusão são problemas econômicos e de scheduling de rede a menos que simulação também falhe.
Quanto tempo um blockhash é válido?
Cerca de 60-90 segundos (150 slots). Sempre busque um fresco antes de resubmit.
Devo usar `skipPreflight: true` para pousar mais rápido?
Apenas em caminhos server confiáveis com simulação separada. Pular preflight aumenta taxa de falha on-chain.
Qual priority fee devo usar?
Comece no p75 de getRecentPrioritizationFees para suas contas writable; ajuste com métricas de pouso.
Por que devnet pousa instantaneamente mas mainnet não?
Devnet tem baixa contenção. Mercados de fee e scheduling de leader dominam na mainnet.
ALTs ajudam no pouso?
Reduzem tamanho da tx, melhorando propagação e encaixe em blocos - especialmente para rotas DeFi complexas.
Como detectar drops silenciosos?
Timeout em signatureNotifications; compare com getSignatureStatuses em segundo RPC.
Posso resubmeter a mesma signature?
Sim - envios duplicados de wire bytes idênticos são idempotentes até expirar. Após expiração precisa de nova signature.
O que é SWQoS?
QoS ponderado por stake dá a provedores RPC com conexão staked melhor encaminhamento para leaders - comum em tiers pagos.
Quando usar bundles Jito?
Mints de alta contenção, arbitragem ou qualquer fluxo onde pouso atômico justifica tip.
Relacionado
- Depurando Transações que Falharam - simulação e erros de programa
- Outages de RPC e Infraestrutura - failover de provedor
- Antes/Depois: Fazendo Transações Pousarem com Confiabilidade - estudo de caso
Versões do stack: Esta página foi 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 e LiteSVM 0.6.x.