Escanea para descargar la aplicación Gate
qrCode
Más opciones de descarga
No volver a recordar hoy

Desarrollo del sistema de billetera de la plataforma de intercambio — Integración con la cadena Solana

La última vez completamos el sistema de control de riesgos de la bolsa, en esta ocasión conectamos la wallet de la bolsa a la cadena Solana. El modelo de cuentas, el almacenamiento de logs y el mecanismo de confirmación de Solana son muy diferentes de las cadenas basadas en Ethereum. Si se sigue usando el esquema de Ethereum, es muy fácil cometer errores. A continuación, repasamos la lógica general para entender Solana.

Comprendiendo la singularidad de Solana

Modelo de cuentas de Solana

Solana utiliza un modelo donde programa y datos están separados; los programas son compartidos, mientras que los datos del programa se almacenan en cuentas PDA (Program Derived Address) de forma independiente. Debido a que los programas son compartidos, se necesita Token Mint para distinguir diferentes tokens. La cuenta Token Mint almacena metadatos globales del token, como permiso de acuñación (mint_authority), suministro total (supply), decimales (decimals), etc.
Cada token tiene una dirección única de cuenta Mint que lo identifica, por ejemplo, USDC en la red principal de Solana tiene la dirección de Mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.

En Solana existen dos programas de Token: SPL Token y SPL Token-2022. Cada SPL Token tiene una ATA (Associated Token Account) independiente para guardar el saldo del usuario. Cuando se realiza una transferencia de tokens, en realidad se llama a su programa respectivo para transferir entre cuentas ATA.

Restricciones en los logs de Solana

En Ethereum, se obtiene información de transferencias analizando los logs históricos. En Solana, los logs de ejecución no se conservan permanentemente por defecto, no pertenecen al estado del libro mayor (no hay un filtro Bloom para logs), y pueden ser truncados durante la ejecución.
Por lo tanto, no podemos hacer conciliación de depósitos solo escaneando logs, sino que debemos usar getBlock o getSignaturesForAddress para analizar instrucciones.

Confirmación y reorganización en Solana

El tiempo de bloque en Solana es de aproximadamente 400ms. Con 32 confirmaciones (unos 12 segundos), se alcanza el estado finalizado (finalized). Si la precisión en tiempo real no es crítica, basta confiar en los bloques finalizados.
Para mayor precisión, hay que considerar posibles reorganizaciones de bloques, aunque son raras. La diferencia es que el consenso de Solana no depende del parentBlockHash para formar la cadena, por lo que no se puede detectar bifurcaciones comparando parentBlockHash y blockHash en la base de datos como en Ethereum. ¿Cómo detectar si un bloque ha sido reorganizado?
Al escanear localmente, debemos registrar el blockhash de cada slot. Si el blockhash de un mismo slot cambia, indica que hubo un rollback.

Entendiendo las diferencias de Solana, ahora podemos comenzar con las modificaciones en la base de datos:

Diseño de tablas en la base de datos

Dado que Solana tiene dos tipos de tokens, en la tabla tokens añadiremos un campo token_type para distinguir entre SPL Token y SPL Token-2022.

Aunque las direcciones de Solana difieren de Ethereum, ambas pueden derivarse usando BIP32 y BIP44, solo que con diferentes rutas de derivación. Por ello, podemos mantener la tabla wallets existente, pero para soportar el mapeo ATA y el seguimiento de bloques en Solana, añadiremos estas tres tablas:

Nombre de la tabla Campos clave Descripción
solana_slots slot, block_hash, status, parent_slot Información redundante de slots, para detectar bifurcaciones y gestionar reversiones
solana_transactions tx_hash, slot, to_addr, token_mint, amount, type Detalles de depósitos y retiros, tx_hash único para seguimiento de doble firma
solana_token_accounts wallet_id, wallet_address, token_mint, ata_address Registro del mapeo ATA-usuario, para que el módulo de escaneo pueda consultar por ata_address

Detalles:

  • solana_slots registra estados como confirmed, finalized, skipped; el escáner decide si guardar o revertir según el estado.
  • solana_transactions almacena en lamports o unidades mínimas del token, con un campo type para distinguir depósitos, retiros, etc. La escritura sensible requiere firma de control de riesgos.
  • solana_token_accounts mantiene la relación con wallets/usuarios, garantizando la unicidad de ATA (wallet_address + token_mint), que es clave para la lógica de escaneo.

Para más detalles, consultar db_gateway/database.md

Procesamiento de depósitos de usuarios

Para gestionar depósitos, se deben escanear continuamente los datos en la cadena Solana, con dos métodos principales:

  1. Escaneo de firmas: getSignaturesForAddress
  2. Escaneo de bloques: getBlock

Método 1: Escanear firmas del address. Se llama a getSignaturesForAddress con la dirección (que puede ser la ATA generada para el usuario o el programID). Se pasa como parámetros before, until, limit para obtener firmas incrementales. Luego, se obtiene la transacción con getTransaction usando la firma.
Este método funciona bien con pocos usuarios o cuentas, pero si hay muchas, es mejor usar el método de escaneo de bloques, que es el que implementamos aquí.

Método 2: Escanear bloques. Se obtiene el slot más reciente, se llama a getBlock para obtener detalles completos, incluyendo transacciones y cuentas, filtrando por instrucciones y cuentas relevantes.

Nota: Debido al alto volumen de transacciones y TPS en Solana, en producción puede que el análisis y filtrado no puedan seguir el ritmo de los bloques. En ese caso, se recomienda usar colas de mensajes (Kafka, RabbitMQ) para filtrar las transferencias de tokens y detectar potenciales eventos de depósito, enviándolos a los consumidores para su procesamiento y almacenamiento. Para acelerar el filtrado, algunos datos calientes se almacenan en Redis. Si hay muchos usuarios, se puede dividir por ATA para mejorar la eficiencia con múltiples consumidores.

Otra opción es usar servicios de indexadores de terceros, que ofrecen Webhook, monitoreo de cuentas y filtrado avanzado, soportando cargas de datos elevadas.

Proceso de escaneo de bloques

Utilizamos el método 2, con código en los módulos scan/solana-scan (blockScanner.ts y txParser.ts). El flujo principal:

1. Sincronización inicial y recuperación histórica (performInitialSync)

  • Desde el último slot escaneado, se recorre hasta el más reciente.
  • Cada 100 slots, se verifica si hay nuevos slots.
  • Se obtiene el bloque con confirmación “confirmed” para balance entre rapidez y precisión.

2. Escaneo en tiempo real (scanNewSlots)

  • Se comprueba continuamente si hay nuevos slots.
  • Se revalida el slot confirmado más reciente para detectar reversiones.

3. Análisis de bloques (txParser.parseBlock)

  • Se llama a getBlock con commitment “confirmed” y encoding “jsonParsed”.
  • Se recorren las transacciones, instrucciones y metadatos internos.
  • Solo se procesan transacciones exitosas (tx.meta.err === null).

4. Análisis de instrucciones (txParser.parseInstruction)

  • Transferencias SOL: coinciden con System Program y tipo ‘transfer’, verificando si la dirección destino está en la lista monitoreada.
  • Transferencias SPL Token: coinciden con Token Program o Token-2022, verificando si la dirección destino es una ATA, y mapeando a la wallet y token mint en la base de datos.

Gestión de reversiones:
El programa obtiene continuamente el slot finalizado (finalizedSlot). Cuando un slot es menor o igual, se marca como finalizado. Para los que aún están en confirmed, se comprueba si el blockhash ha cambiado para detectar reversiones.

Ejemplo de código clave:

// blockScanner.ts - Escaneo de un slot
async scanSingleSlot(slot: number) {
  const block = await solanaClient.getBlock(slot);
  if (!block) {
    await insertSlot({ slot, status: 'skipped' });
    return;
  }
  const finalizedSlot = await getCachedFinalizedSlot();
  const status = slot <= finalizedSlot ? 'finalized' : 'confirmed';
  await processBlock(slot, block, status);
}

// txParser.ts - Parseo de instrucciones
for (const tx of block.transactions) {
  if (tx.meta?.err) continue; // Saltar transacciones fallidas
  const instructions = [
    ...tx.transaction.message.instructions,
    ...(tx.meta.innerInstructions ?? []).flatMap(i => i.instructions)
  ];
  for (const ix of instructions) {
    // Transferencia SOL
    if (ix.programId === SYSTEM_PROGRAM_ID && ix.parsed?.type === 'transfer') {
      if (monitoredAddresses.has(ix.parsed.info.destination)) {
        // Procesar depósito
      }
    }
    // Transferencia SPL Token
    if (ix.programId === TOKEN_PROGRAM_ID || ix.programId === TOKEN_2022_PROGRAM_ID) {
      if (ix.parsed?.type === 'transfer' || ix.parsed?.type === 'transferChecked') {
        const ataAddress = ix.parsed.info.destination;
        const walletAddress = ataToWalletMap.get(ataAddress);
        if (walletAddress && monitoredAddresses.has(walletAddress)) {
          // Procesar depósito
        }
      }
    }
  }
}

Al detectar depósitos, se mantiene la seguridad con DB Gateway y firma doble del control de riesgos, y se registra en la tabla de fondos.

Retiro

El proceso de retiro en Solana es similar al de EVM, pero con diferencias en la construcción de transacciones:

  1. Hay dos tipos de tokens: SPL-Token y SPL-Token 2022, con diferentes programID. Al construir la transacción, se debe distinguir entre ellos.
  2. La transacción consta de dos partes: firmas (signatures) y mensaje (message). El message incluye header, accountKeys, recentBlockhash, instructions. El hash del mensaje se firma con las firmas.
    No hay nonce en Solana; en su lugar, se usa recentBlockhash, válido por unos 150 bloques (~1 minuto). Cada vez que se realiza un retiro, se obtiene un recentBlockhash actualizado en tiempo real. Si el retiro requiere revisión manual, se vuelve a obtener el recentBlockhash, se construye la transacción y se firma nuevamente.

Proceso de retiro

El flujo general:

![diagrama de flujo de retiro]

El código clave para firmar y enviar la transacción:

// Construcción de instrucciones
const instruction = getTransferSolInstruction({
  source: hotWalletSigner,
  destination: solanaAddressTo,
  amount: BigInt(amount)
});

// Para token
const instruction = getTransferInstruction({
  source: sourceAta,
  destination: destAta,
  authority: hotWalletSigner,
  amount: BigInt(amount)
});

// Construcción y firma del mensaje
const transactionMessage = pipe(
  createTransactionMessage({ version: 0 }),
  tx => setTransactionMessageFeePayerSigner(hotWalletSigner, tx),
  tx => setTransactionMessageLifetime({ blockhash, lastValidBlockHeight }),
  tx => appendTransactionMessageInstruction(instruction, tx)
);

// Firmar
const signedTx = await signTransactionMessageWithSigners(transactionMessage);

// Codificación para envío
const signedTransaction = getBase64EncodedWireTransaction(signedTx);

Luego, se envía usando @solana/web3.js:

const solanaRpc = chainConfigManager.getSolanaRpc();
const txSignature = await solanaRpc.sendTransaction(signedTransaction, ...);

Los archivos relevantes son:

  • Wallet: walletBusinessService.ts (405-754)
  • Signer: solanaSigner.ts (29-122)
  • Scripts de prueba: requestWithdrawOnSolana.ts

Optimización pendiente:

  • Verificación previa de ATA: asegurar que la cuenta ATA exista antes del retiro, o crearla si no.
  • Prioridad en tarifas: en congestión, ajustar computeUnitPrice para priorizar.

Resumen

Integrar Solana en la bolsa no requiere cambios en la arquitectura general, solo adaptar su modelo de cuentas, estructura de transacciones y mecanismo de confirmación.
Para depósitos, mantener un mapeo ATA-wallet, monitorizar cambios en blockhash para detectar reorganizaciones, y actualizar dinámicamente el estado del bloque (confirmed → finalized).
Para retiros, obtener el último recentBlockhash en tiempo real, distinguir entre tokens SPL y Token-2022, y construir transacciones específicas para cada caso.

SOL3.87%
ETH4.72%
USDC-0.01%
Ver originales
Esta página puede contener contenido de terceros, que se proporciona únicamente con fines informativos (sin garantías ni declaraciones) y no debe considerarse como un respaldo por parte de Gate a las opiniones expresadas ni como asesoramiento financiero o profesional. Consulte el Descargo de responsabilidad para obtener más detalles.
  • Recompensa
  • Comentar
  • Republicar
  • Compartir
Comentar
0/400
Sin comentarios
  • Anclado
Opera con criptomonedas en cualquier momento y lugar
qrCode
Escanee para descargar la aplicación Gate
Comunidad
Español
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)