Preparación para producción

Construir localmente y probar en devnet son excelentes formas de comenzar con los pagos en Solana. Sin embargo, cuando estés listo para desplegar en mainnet, debes conocer los matices de mainnet. Devnet perdona los errores. Mainnet no. Esta guía cubre las diferencias que importan para garantizar que tus usuarios tengan una experiencia fluida.

DevnetMainnet
SOL gratis desde faucetsAdquiere SOL real para las comisiones
Baja competencia por espacio de bloqueLas comisiones de prioridad importan
Las transacciones se confirman fácilmenteLa configuración de transacciones es crítica
El RPC público está bienSe requiere RPC de producción
Keypairs y mints de devnetClaves y mints de tokens diferentes—actualiza tu configuración

Infraestructura RPC

Los endpoints públicos (api.mainnet-beta.solana.com) tienen límites de tasa sin SLA. Están bien para desarrollo pero fallarán en flujos de pago de producción—como intentar ejecutar un procesador de pagos a través de una API compartida sin garantía de tiempo de actividad.

Nunca uses RPC público para producción

Usa un proveedor de RPC privado para acceso confiable y de baja latencia.

Al elegir un proveedor de RPC, busca:

  • Confiabilidad: SLAs con garantías de tiempo de actividad (99.9%+)
  • Latencia: Proximidad geográfica a tus usuarios
  • Características: Funciones de confirmación de transacciones, indexación, APIs de comisiones de prioridad

Para una lista completa de proveedores de RPC, consulta la guía de proveedores de infraestructura RPC.

Configuración redundante de RPC

Como cualquier proveedor de servicios de red, los proveedores de RPC pueden experimentar tiempo de inactividad o períodos de rendimiento degradado. Para garantizar que tu aplicación sea resiliente, debes configurar tu aplicación para usar múltiples proveedores de RPC.

Solana Kit proporciona una biblioteca para personalizar transportes RPC que te permite construir tu propio cliente RPC redundante. Aquí hay un ejemplo de cómo podrías usarlo para construir un cliente RPC redundante:

import { RpcTransport } from "@solana/rpc-spec";
import { RpcResponse } from "@solana/rpc-spec-types";
import { createHttpTransport } from "@solana/rpc-transport-http";
// Create a transport for each RPC server
const transports = [
createHttpTransport({ url: "https://mainnet-beta.my-server-1.com" }),
createHttpTransport({ url: "https://mainnet-beta.my-server-2.com" }),
createHttpTransport({ url: "https://mainnet-beta.my-server-3.com" })
];
// Create a wrapper transport that distributes requests to them
let nextTransport = 0;
async function roundRobinTransport<TResponse>(
...args: Parameters<RpcTransport>
): Promise<RpcResponse<TResponse>> {
const transport = transports[nextTransport];
nextTransport = (nextTransport + 1) % transports.length;
return await transport(...args);
}

Si prefieres no construir tus propias herramientas de enrutamiento, puedes aprovechar un servicio de terceros como Iron Forge para gestionar el enrutamiento por ti.

Aterrizaje de transacciones

En Devnet, las transacciones aterrizan con bastante facilidad. En Mainnet, estás compitiendo por espacio de bloque. Para aumentar las posibilidades de que tu transacción sea incluida en un bloque, debes asegurarte de haber ensamblado correctamente tu transacción. Esto significa:

  • incluir un blockhash reciente antes de enviar la transacción
  • incluir una instrucción de tarifa de prioridad en la transacción con una tarifa de prioridad competitiva
  • incluir una instrucción de límite de unidades de cómputo en la transacción con un límite de unidades de cómputo basado en las unidades de cómputo estimadas requeridas para la transacción

Además, debes considerar otras herramientas como Jito Bundles para aumentar las posibilidades de que tu transacción sea incluida en un bloque. Exploremos estas herramientas con más detalle.

Configuración de envío de transacciones

Al enviar transacciones en Mainnet, configura estos parámetros para tasas de aterrizaje óptimas:

Gestión de blockhash:

  • Obtén con confirmed commitment
  • Almacena el lastValidBlockHeight devuelto por getLatestBlockhash—esto te indica cuándo expira tu transacción
  • Los blockhashes expiran después de ~150 bloques (~60-90 segundos)

Opciones de envío:

  • maxRetries: 0 — Desactiva los reintentos automáticos de RPC. Gestiona los reintentos tú mismo para poder actualizar el blockhash cuando sea necesario.
  • skipPreflight: true — Omite la simulación antes de enviar. Usa esto cuando ya hayas validado la transacción y quieras la menor latencia. Mantenlo en false durante el desarrollo para detectar errores temprano.
import { createSolanaRpc } from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
// 1. Get blockhash with confirmed commitment
const { value: latestBlockhash } = await rpc
.getLatestBlockhash({ commitment: "confirmed" })
.send();
// 2. Build and sign your transaction with the blockhash
// ... (transaction building code)
// 3. Send with production settings
const signature = await rpc
.sendTransaction(encodedTransaction, {
encoding: "base64",
maxRetries: 0n, // Handle retries yourself
skipPreflight: true, // Skip simulation for speed (use false during dev)
preflightCommitment: "confirmed"
})
.send();
// 4. Track expiration using lastValidBlockHeight
const { lastValidBlockHeight } = latestBlockhash;
// Stop retrying when current block height exceeds lastValidBlockHeight

Usa tarifas de prioridad

Cada transacción de Solana requiere una tarifa de transacción, pagada en SOL. Las tarifas de transacción se dividen en dos partes: la tarifa base y la tarifa de prioridad. La tarifa base compensa a los validadores por procesar la transacción. La tarifa de prioridad es una tarifa opcional para aumentar la probabilidad de que el líder actual procese tu transacción. Piensa en ello como envío exprés: pagas más por una entrega más rápida y confiable.

Cómo funcionan las tarifas:

Total fee = Base fee (5,000 lamports per signature) + Priority fee
Priority fee = Compute units x Price per unit (micro-lamports per compute unit)

Costos reales:

  • Transferencia simple de USDC: ~$0.001-0.005 durante condiciones normales
  • Durante congestión: ~$0.01-0.05
  • Congestión máxima: puede aumentar más

Implementación de ejemplo:

El paquete @solana-program/compute-budget proporciona una función auxiliar para actualizar o agregar fácilmente la instrucción de precio de unidad de cómputo (en micro-lamports) a una transacción.

import { updateOrAppendSetComputeUnitPriceInstruction } from "@solana-program/compute-budget";
const tx = pipe(
createTransactionMessage({ version: 0 }),
(m) => setTransactionMessageFeePayerSigner(payer, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
(m) => appendTransactionMessageInstructions([myInstructions], m),
(m) => updateOrAppendSetComputeUnitPriceInstruction(1000n as MicroLamports, m)
);

Obtener estimaciones de tarifas: la mayoría de los proveedores de RPC ofrecen APIs de tarifas de prioridad:

Para conocer la mecánica completa de las tarifas, consulta Tarifas de transacción y nuestra guía: Cómo agregar tarifas de prioridad a una transacción.

Optimiza las unidades de cómputo

El cómputo en Solana es efectivamente una medida de la cantidad de trabajo que está realizando el programa. Hay un límite en la cantidad de cómputo que se puede usar en una transacción (actualmente 1.4 millones de unidades de cómputo), y un límite en la cantidad de cómputo que se puede usar por cuenta por bloque (actualmente 100 millones de unidades de cómputo).

Cuando envías una transacción, necesitas estimar la cantidad de cómputo que se utilizará y establecer el límite de unidades de cómputo en consecuencia; esto es efectivamente una solicitud de cuánta capacidad total debe reservarse para tu transacción. En la práctica, esto significa que estimar correctamente las unidades de cómputo requeridas para tu transacción es fundamental para que tu transacción se incluya en un bloque (e importante para gestionar tus tarifas de prioridad).

La API JSON RPC de Solana dispone de un método simulatetransaction que puede utilizarse para estimar las unidades de cómputo necesarias para una transacción, lo que incluye una estimación de las unidades de cómputo que se consumirán. @solana/kit proporciona funciones auxiliares que estiman los límites de recursos de una transacción y los configuran en el mensaje en un solo paso (usando el método simulatetransaction internamente). Para transacciones de versión 1, estos helpers también estiman el límite de tamaño de datos de las cuentas cargadas.

import {
estimateResourceLimitsFactory,
estimateAndSetResourceLimitsFactory
} from "@solana/kit";
const estimateResourceLimits = estimateResourceLimitsFactory({ rpc });
const estimateAndSetResourceLimits = estimateAndSetResourceLimitsFactory(
estimateResourceLimits
);
const txWithResourceLimits = await estimateAndSetResourceLimits(tx);

Si construyes y envías transacciones con un cliente plugin de kit, normalmente no necesitas este paso — el cliente añade instrucciones de presupuesto de cómputo por ti al enviar (.sendTransaction()). El flujo manual anterior es para cuando ensamblas transacciones directamente con @solana/kit.

En producción, si repites el mismo tipo de transacción múltiples veces, debería considerar almacenar en caché la estimación de cómputo para ese tipo de transacción y así evitar la sobrecarga de estimar las unidades de cómputo en cada ocasión.

Bundles de Jito

Los bundles de Jito son una herramienta para gestionar la ejecución atómica de múltiples transacciones. Esto se logra enviando múltiples transacciones a la red de Jito junto con una propina. Las propinas pueden utilizarse para incentivar a la red de Jito a incluir tus transacciones en un bloque.

Recursos:

Estrategias de reintento

Las transacciones pueden fallar por muchas razones. A diferencia de las APIs de pago tradicionales que devuelven éxito o fallo de inmediato, las transacciones en blockchain requieren un seguimiento de confirmación.

Conceptos clave:

  • Expiración del blockhash: Las transacciones son válidas durante ~150 bloques (~60-90 segundos)
  • Idempotencia: La misma transacción firmada siempre produce la misma firma — reenviarla es seguro
  • Retroceso exponencial: Evita saturar la red con reintentos rápidos
import {
createSolanaRpc,
createSolanaRpcSubscriptions,
sendAndConfirmTransactionFactory,
isSolanaError,
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED
} from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.RPC_WSS_URL!);
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc,
rpcSubscriptions
});
// Send with automatic confirmation tracking and block height monitoring
try {
await sendAndConfirmTransaction(signedTransaction, {
commitment: "confirmed",
// Optional: abort after 75 seconds
abortSignal: AbortSignal.timeout(75_000)
});
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {
// Blockhash expired—rebuild transaction with fresh blockhash and retry
rebuildAndRetryTransaction(); // implement your own logic for rebuilding and retrying the transaction
}
throw e;
}

El sendAndConfirmTransactionFactory de @solana/kit gestiona automáticamente el sondeo de confirmaciones y el seguimiento de la altura de bloque. Lanza SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED cuando el blockhash de la transacción expira, lo que indica que debes reconstruir la transacción con un blockhash nuevo.

Recursos adicionales

Niveles de confirmación

Solana ofrece tres niveles de confirmación. En términos de finanzas tradicionales:

NivelDefinición en SolanaEquivalente tradicionalCaso de uso
processedEn un bloque, aún sin votosAutorización pendienteActualizaciones de UI en tiempo real
confirmedSupermayoría votadaFondos compensadosLa mayoría de los pagos
finalizedConsolidado, irreversibleFondos liquidadosAlto valor, cumplimiento normativo

Cuándo usar cada uno:

  • Actualizaciones de UI: Mostrar processed para retroalimentación inmediata ("Pago enviado")
  • Acreditar cuenta del usuario: Esperar confirmed (seguro para la mayoría de las transacciones)
  • Enviar bienes físicos: Esperar finalized
  • Retiros de grandes montos: Esperar finalized
  • Cumplimiento/auditoría: Registrar siempre el estado finalized

Para más información sobre cómo verificar el estado de las transacciones, consulta Interactuando con Solana.

Manejo de errores

Solana Kit proporciona errores tipados a través de isSolanaError(). Usa códigos de error específicos en lugar de comparación de cadenas de texto:

import {
isSolanaError,
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,
SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE,
SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND,
SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS
} from "@solana/kit";
function handlePaymentError(error: unknown): {
message: string;
retryable: boolean;
} {
// Blockhash expired—rebuild and retry
if (
isSolanaError(error, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED) ||
isSolanaError(error, SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND)
) {
return { message: "Transaction expired—rebuilding", retryable: true };
}
// Insufficient SOL for fees
if (
isSolanaError(
error,
SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE
)
) {
return { message: "Not enough SOL for fees", retryable: false };
}
// Insufficient token balance
if (
isSolanaError(error, SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS)
) {
return { message: "Insufficient balance", retryable: false };
}
// Unknown error
console.error("Payment error:", error);
return { message: "Payment failed—please retry", retryable: true };
}

Códigos de error comunes:

Código de errorCausaSolución
BLOCK_HEIGHT_EXCEEDEDBlockhash expiradoReconstruir con un blockhash nuevo
BLOCKHASH_NOT_FOUNDBlockhash no encontradoReconstruir con un blockhash nuevo
INSUFFICIENT_FUNDS_FOR_FEESOL insuficienteFinanciar el pagador de comisiones o usar abstracción de comisiones
INSUFFICIENT_FUNDSTokens insuficientesEl usuario necesita más saldo
ACCOUNT_NOT_FOUNDtoken account faltanteCrear ATA en la transacción

Transacciones sin gas

Los usuarios esperan pagar en stablecoins, no tener que adquirir SOL para cubrir las tarifas de red. Las transacciones sin gas resuelven esto, de manera similar a cómo los usuarios de Venmo no piensan en las tarifas ACH. Consulta Abstracción de tarifas para una implementación completa.

Seguridad

Gestión de claves

  • Nunca expongas claves privadas en el código del frontend. Utiliza firma en el backend, carteras de hardware, carteras multifirma o servicios de gestión de claves.
  • Separa las carteras activas de las frías. La cartera activa para operaciones, la fría para el tesoro.
  • Haz copias de seguridad de todas las claves de producción. Guarda copias cifradas en múltiples ubicaciones seguras. Perder una clave significa perder el acceso de forma permanente.
  • Usa claves distintas para devnet y mainnet. Las claves de devnet no deben ser las mismas que las de mainnet. Utiliza configuración basada en entornos para garantizar que se carguen las claves correctas en cada red.

Infraestructura de firma

Para la firma en backend en producción, utiliza Keychain, una biblioteca de firma unificada que abstrae múltiples backends de gestión de claves a través de una única interfaz: Memory, Vault, Privy, Turnkey, AWS KMS, Fireblocks, GCP KMS, CDP, Para, Dfns, Crossmint, Openfort y Utila. Esto te permite desarrollar localmente con claves en memoria y luego cambiar a backends de nivel productivo sin modificar el código de la aplicación.

¿No sabes qué backend se adapta mejor a tus necesidades? Consulta Cómo elegir un backend. Keychain está disponible tanto para Rust como para TypeScript.

Seguridad RPC

Trata los endpoints RPC como claves API—no los expongas en el código frontend donde puedan ser extraídos y abusados. Usa un proxy de backend o variables de entorno que no estén incluidas en el código del cliente.

Monitoreo

Rastrea estas métricas en producción:

MétricaPor qué
Tasa de éxito de transaccionesDetectar problemas temprano
Latencia de confirmaciónMonitorear salud de la red
Gasto en tarifas prioritariasGestión de costos
Tasa de errores RPCSalud del proveedor

Configura alertas para:

  • Transferencias por encima del umbral desde la tesorería
  • Picos en la tasa de transacciones fallidas
  • Patrones inusuales de destinatarios
  • Incrementos en la tasa de errores RPC

Para monitoreo de transacciones en tiempo real a escala, consulta nuestra Guía de Indexación.

Verificar Direcciones

Cada token y programa tiene exactamente una dirección correcta en mainnet. Los tokens falsificados que imitan USDC u otras stablecoins son comunes—tendrán el mismo nombre y símbolo pero un mint diferente. Tu aplicación debe codificar o incluir en una lista de permitidos las direcciones de mint (según tus requisitos), nunca aceptarlas dinámicamente de fuentes no confiables.

Configuración basada en el entorno: Devnet y Mainnet suelen utilizar mints de tokens completamente diferentes. Configure su aplicación para cargar las direcciones correctas según el entorno—no codifique direcciones de mainnet y olvide cambiarlas durante las pruebas, o peor aún, envíe direcciones de devnet a producción.

Algunos mints de stablecoins comunes son:

TokenEmisorDirección del Mint
USDCCircleEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDTTetherEs9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
PYUSDPayPal2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo
USDGPaxos2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH

Las direcciones de los programas también son importantes. Enviar instrucciones al programa incorrecto fallará—o peor, resultará en una pérdida irreversible de fondos. Las direcciones del Token Program son:

ProgramaDirección
Token ProgramTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Token-2022 ProgramTokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Incluir en la lista de permitidos las direcciones correctas protege contra tokens falsificados, pero no protege contra el envío al tipo de cuenta incorrecto. Una dirección de destinatario puede ser una wallet, un token account, un mint o un programa, y cada uno requiere un manejo diferente. SOL nativo enviado a cualquier cosa que no sea una wallet queda fuera del control del remitente — perdido definitivamente en el caso de un mint o programa, recuperable solo por el propietario de la cuenta en el caso de un token account — sin que haya una transacción fallida que advierta al usuario.

Valida los destinatarios antes de enviar

Clasifica cada dirección de destinatario antes de firmar una transferencia. Consulta Verificar Dirección para ver el árbol de decisión completo y el código de referencia que distingue entre wallets, token accounts, mints y programas.

Lista de verificación previa al lanzamiento

  • SOL de Mainnet adquirido para comisiones y rent
  • RPC de producción configurado (no endpoint público)
  • Endpoint RPC de respaldo configurado
  • Comisiones de prioridad implementadas con precios dinámicos
  • La lógica de reintentos gestiona la expiración del blockhash
  • Nivel de confirmación adecuado para el caso de uso
  • Todos los errores comunes gestionados correctamente
  • Gasless configurado (si aplica)
  • Direcciones de tokens de Mainnet verificadas (no mints de devnet)
  • Validación de dirección de destinatario antes de enviar (wallet vs token account vs mint vs programa)
  • Todas las claves respaldadas de forma segura
  • Gestión de claves revisada (sin claves en el frontend)
  • Monitoreo de transacciones y alertas activos
  • Pruebas de carga realizadas al volumen esperado

Despliegue de programas

Si estás desplegando un programa personalizado de Solana como parte de tu infraestructura de pagos, hay consideraciones adicionales a tener en cuenta.

Antes del despliegue

  • Versión de Solana CLI: Asegúrate de estar usando la última versión de la Solana CLI.
  • keypair del programa: Tu programa tendrá una dirección diferente en mainnet que en devnet (a menos que estés reutilizando el mismo keypair). Actualiza todas las referencias en la configuración de tu aplicación. Guarda el keypair de tu programa en un lugar seguro (ten en cuenta que ejecutar cargo clean probablemente eliminará tu keypair del programa).
  • Inicializar cuentas: Si tu programa requiere cuentas de administrador, PDAs u otras cuentas de estado, asegúrate de que estas se creen en mainnet antes de que los usuarios interactúen con tu aplicación. Lo mismo aplica para cualquier Associated Token Accounts (ATAs) que tu programa necesite.

Proceso de despliegue

  • Cuentas de búfer: Los programas de gran tamaño se despliegan a través de cuentas de búfer. El comando solana program deploy gestiona esto automáticamente, pero ten en cuenta que el despliegue no es atómico: si se interrumpe, es posible que necesites recuperar o cerrar las cuentas de búfer. Consulta Deploying Programs.
  • Autoridad de actualización: Decide si tu programa debe ser actualizable tras el lanzamiento. Para garantizar la inmutabilidad, revoca la autoridad de actualización después del despliegue. Para mayor flexibilidad, protege adecuadamente la clave de autoridad de actualización.
  • Rent: Asegúrate de que tu billetera de despliegue tenga suficiente SOL para cubrir los mínimos de exención de rent para todos los program accounts.
  • Verificación: Verifica tu programa para asegurarte de que el programa ejecutable que despliegas en la red de Solana coincida con el código fuente en tu repositorio

Para obtener una guía completa sobre el despliegue de programas, consulta Deploying Programs.

Is this page helpful?