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.
| Devnet | Mainnet |
|---|---|
| SOL gratis desde faucets | Adquiere SOL real para las comisiones |
| Baja competencia por espacio de bloque | Las comisiones de prioridad importan |
| Las transacciones se confirman fácilmente | La configuración de transacciones es crítica |
| El RPC público está bien | Se requiere RPC de producción |
| Keypairs y mints de devnet | Claves 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 serverconst 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 themlet 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
confirmedcommitment - Almacena el
lastValidBlockHeightdevuelto porgetLatestBlockhash—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 enfalsedurante el desarrollo para detectar errores temprano.
import { createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc(process.env.RPC_URL!);// 1. Get blockhash with confirmed commitmentconst { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();// 2. Build and sign your transaction with the blockhash// ... (transaction building code)// 3. Send with production settingsconst signature = await rpc.sendTransaction(encodedTransaction, {encoding: "base64",maxRetries: 0n, // Handle retries yourselfskipPreflight: true, // Skip simulation for speed (use false during dev)preflightCommitment: "confirmed"}).send();// 4. Track expiration using lastValidBlockHeightconst { 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 feePriority 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 monitoringtry {await sendAndConfirmTransaction(signedTransaction, {commitment: "confirmed",// Optional: abort after 75 secondsabortSignal: AbortSignal.timeout(75_000)});} catch (e) {if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {// Blockhash expired—rebuild transaction with fresh blockhash and retryrebuildAndRetryTransaction(); // 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
- Guía: Confirmación y expiración de transacciones
- Helius: Cómo confirmar transacciones en Solana
- QuickNode: Estrategias para optimizar transacciones en Solana
Niveles de confirmación
Solana ofrece tres niveles de confirmación. En términos de finanzas tradicionales:
| Nivel | Definición en Solana | Equivalente tradicional | Caso de uso |
|---|---|---|---|
processed | En un bloque, aún sin votos | Autorización pendiente | Actualizaciones de UI en tiempo real |
confirmed | Supermayoría votada | Fondos compensados | La mayoría de los pagos |
finalized | Consolidado, irreversible | Fondos liquidados | Alto valor, cumplimiento normativo |
Cuándo usar cada uno:
- Actualizaciones de UI: Mostrar
processedpara 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 retryif (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 feesif (isSolanaError(error,SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE)) {return { message: "Not enough SOL for fees", retryable: false };}// Insufficient token balanceif (isSolanaError(error, SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS)) {return { message: "Insufficient balance", retryable: false };}// Unknown errorconsole.error("Payment error:", error);return { message: "Payment failed—please retry", retryable: true };}
Códigos de error comunes:
| Código de error | Causa | Solución |
|---|---|---|
BLOCK_HEIGHT_EXCEEDED | Blockhash expirado | Reconstruir con un blockhash nuevo |
BLOCKHASH_NOT_FOUND | Blockhash no encontrado | Reconstruir con un blockhash nuevo |
INSUFFICIENT_FUNDS_FOR_FEE | SOL insuficiente | Financiar el pagador de comisiones o usar abstracción de comisiones |
INSUFFICIENT_FUNDS | Tokens insuficientes | El usuario necesita más saldo |
ACCOUNT_NOT_FOUND | token account faltante | Crear 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.
- QuickNode: Mejores Prácticas de Seguridad de Endpoints
- Helius: Protege tus Claves API de Solana: Mejores Prácticas de Seguridad
Monitoreo
Rastrea estas métricas en producción:
| Métrica | Por qué |
|---|---|
| Tasa de éxito de transacciones | Detectar problemas temprano |
| Latencia de confirmación | Monitorear salud de la red |
| Gasto en tarifas prioritarias | Gestión de costos |
| Tasa de errores RPC | Salud 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:
| Token | Emisor | Dirección del Mint |
|---|---|---|
| USDC | Circle | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Tether | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| PYUSD | PayPal | 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo |
| USDG | Paxos | 2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH |
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:
| Programa | Dirección |
|---|---|
| Token Program | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA |
| Token-2022 Program | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
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 cleanprobablemente 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 deploygestiona 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?