Scan to Download Gate App
qrCode
More Download Options
Don't remind me again today

Exchange wallet system development — integrating Solana blockchain

In our previous article, we completed the exchange’s risk control system. In this article, we will discuss integrating Solana into the exchange wallet. Solana’s account model, log storage, and confirmation mechanism differ significantly from Ethereum-based chains. Simply applying Ethereum routines can easily lead to pitfalls. Here, we outline the overall approach to recording Solana.

Understanding the Unique Aspects of Solana

Solana Account Model

Solana uses a program and data separation model. Programs are shareable, while program data is stored separately in PDA (Program Derived Address) accounts. Since programs are shared, Token Mint accounts are used to distinguish different tokens. Token Mint accounts store global token metadata such as mint authority, total supply, and decimals.
Each token has a unique Mint account address as an identifier. For example, USD Coin (USDC) on the Solana mainnet has the Mint address EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.

Solana has two sets of Token programs: SPL Token and SPL Token-2022. Each SPL Token has an associated ATA (Associated Token Account) to hold user balances. Token transfers involve calling their respective programs to transfer tokens between ATAs.

Log Limitations on Solana

On Ethereum, token transfers are tracked by parsing historical transfer logs. Solana’s execution logs are not permanently retained by default; they are not part of the ledger state and lack log Bloom filters. Logs may also be truncated during execution.
Therefore, we cannot rely on log scanning for deposit reconciliation. Instead, we must use getBlock or getSignaturesForAddress to parse instructions.

Confirmation and Reorganization in Solana

Solana produces blocks approximately every 400ms. After about 12 seconds (32 confirmations), a block reaches ‘finalized’ status. For low-latency requirements, trusting only finalized blocks is sufficient.
For higher real-time needs, consider potential chain reorganizations, which are rare but possible. Unlike Ethereum, Solana’s consensus does not rely on parentBlockHash to form a chain structure, so fork detection cannot be based solely on parentBlockHash and blockHash mismatches.
To detect reorgs locally, record the blockhash for each slot. If the blockhash for the same slot changes, it indicates a rollback.

Understanding these differences, we can proceed with implementation. First, consider the necessary database modifications:

Database Table Design

Since Solana has two token types, we add a token_type field to the tokens table to distinguish between SPL Token and SPL Token-2022.

Although Solana addresses differ from Ethereum, they can be derived using BIP32/BIP44 with different derivation paths. The existing wallets table can be reused. To support ATA address mapping and chain scanning, add the following tables:

Table Name Key Fields Description
solana_slots slot, block_hash, status, parent_slot Stores redundant slot info for fork detection and rollback triggers
solana_transactions tx_hash, slot, to_addr, token_mint, amount, type Records deposit/withdrawal transactions; tx_hash is unique for tracking signatures
solana_token_accounts wallet_id, wallet_address, token_mint, ata_address Maps user ATA addresses; enables internal account lookup during scanning

Details:

  • solana_slots records confirmed/finalized/skipped slots; scanner uses status to decide whether to store or rollback.
  • solana_transactions stores amounts in lamports or token units, with a ‘type’ field for deposit/withdrawal, and sensitive operations require risk control signatures.
  • solana_token_accounts links wallet_id and wallet_address with token_mint and ata_address, ensuring ATA uniqueness (wallet_address + token_mint). This is core for scanning logic.

Refer to db_gateway/database.md for detailed table definitions.

Processing User Deposits

Deposit processing involves continuously scanning on-chain data, typically via two methods:

  1. Signature scanning: getSignaturesForAddress
  2. Block scanning: getBlock

Method 1: Signature scanning involves calling getSignaturesForAddress with the user’s ATA address or program ID, passing parameters like before, until, limit. This retrieves signatures related to the address, which can then be fetched via getTransaction for detailed info. Suitable for small account sets.

Method 2: Block scanning involves fetching the latest slot, then calling getBlock for each slot to get transaction details, signatures, and account info. This is more suitable for large account sets.

Note: Due to high transaction throughput and TPS on Solana, parsing and filtering may lag behind block production. Using message queues (Kafka/RabbitMQ) to push potential deposit events for downstream processing can improve efficiency. Hot data can be cached in Redis to prevent queue buildup. For many addresses, sharding by ATA addresses and multiple consumers can enhance throughput.

Alternatively, third-party RPC providers offer indexer services with webhook and account monitoring, which can handle large-scale data parsing.

Block Scanning Workflow

We use method two, with core code in scan/solana-scan modules (blockScanner.ts and txParser.ts). The main steps:

  1. Initial sync / historical data fill (performInitialSync):
  • Start from the last scanned slot, scan up to the latest slot.
  • Check every 100 slots for new slots, dynamically updating target.
  • Use ‘confirmed’ commitment for a balance between speed and certainty.
  1. Continuous scanning (scanNewSlots):
  • Keep polling for new slots.
  • Re-validate recent confirmed slots to detect reorgs.
  1. Block parsing (txParser.parseBlock):
  • Call getBlock with commitment ‘confirmed’ and encoding ‘jsonParsed’.
  • Iterate through each transaction’s instructions and inner instructions.
  • Only process successful transactions (tx.meta.err === null).
  1. Instruction parsing (txParser.parseInstruction):
  • SOL transfers: Match System Program transfer instructions, check if destination address is monitored.
  • SPL Token transfers: Match Token Program or Token-2022 Program transfer/transferChecked instructions; if destination ATA matches, map to wallet address and token mint via database.

Reorg handling:

  • Continuously fetch finalizedSlot.
  • If slot ≤ finalizedSlot, mark as finalized.
  • For confirmed slots, check if blockhash has changed to detect reorgs.

Sample core code snippets:

// blockScanner.ts - scan a single 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 - parse transfer instructions for (const tx of block.transactions) { if (tx.meta?.err) continue; // skip failed tx const instructions = [ …tx.transaction.message.instructions, …(tx.meta.innerInstructions ?? []).flatMap(i => i.instructions), ]; for (const ix of instructions) { // SOL transfer if (ix.programId === SYSTEM_PROGRAM_ID && ix.parsed?.type === ‘transfer’) { if (monitoredAddresses.has(ix.parsed.info.destination)) { // process deposit } } // SPL Token transfer 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)) { // process deposit } } } } }

Deposit Processing Summary:

  • After detecting deposit transactions, verify via risk control signatures.
  • Record deposit details into the funds flow table (‘credits’).

Withdrawal Process

Solana withdrawals are similar to EVM chains but differ in transaction construction:

  1. Token types: SPL Token and SPL Token-2022 have different program IDs; need to distinguish during transaction creation.
  2. Transactions consist of signatures (ed25519 signatures) and message (header, account keys, recent blockhash, instructions).
  • RecentBlockhash is used instead of nonce, valid for ~150 blocks (~1 minute).
  • Each withdrawal requires fetching the latest recentBlockhash from the chain before constructing the transaction.
  • If manual review is needed, fetch recentBlockhash, construct transaction, and sign again.

Withdrawal workflow:

  • Build transfer instructions (SOL or token) based on token type.
  • Create transaction message with appropriate fee payer and recentBlockhash.
  • Sign transaction with the hot wallet’s signer.
  • Encode transaction (Base64) for network submission.

Sample code for signing:

// SOL transfer instruction const instruction = getTransferSolInstruction({ source: hotWalletSigner, destination: solanaAddress.to, amount: BigInt(amount), }); // Token transfer instruction const instruction = getTransferInstruction({ source: sourceAta, destination: destAta, authority: hotWalletSigner, amount: BigInt(amount), });

// Build transaction message const transactionMessage = pipe( createTransactionMessage({ version: 0 }), tx => setTransactionMessageFeePayerSigner(hotWalletSigner, tx), tx => setTransactionMessageLifetime({ blockhash, lastValidBlockHeight }), tx => appendTransactionMessageInstruction(instruction), );

// Sign transaction const signedTx = await signTransactionMessageWithSigners(transactionMessage); // Encode for sending const signedTransaction = getBase64EncodedWireTransaction(signedTx);

Wallet Module: walletBusinessService.ts:405-754
Signer Module: solanaSigner.ts:29-122
Test Scripts: requestWithdrawOnSolana.ts

Optimization notes:

  • Pre-check ATA existence before withdrawal; create ATA if missing (additional fee).
  • Use computeUnitPrice to prioritize transactions during network congestion.

Summary

Integrating Solana into the exchange does not fundamentally change the architecture but requires adapting to its unique account model, transaction structure, and consensus confirmation mechanism.
Pre-establish and maintain ATA-to-wallet mappings for token transfers, monitor blockhash changes to detect reorgs, and dynamically update transaction statuses from confirmed to finalized.
During withdrawals, fetch the latest blockhash, distinguish token types, and construct appropriate transactions accordingly.

SOL3.86%
ETH4.18%
USDC-0.02%
View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • Comment
  • Repost
  • Share
Comment
0/400
No comments
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate App
Community
English
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)