Na nossa última publicação, complementámos o sistema de controlo de riscos da bolsa. Nesta, vamos abordar a integração da carteira da bolsa com a cadeia Solana. O modelo de contas, armazenamento de logs e mecanismo de confirmação do Solana diferem bastante das cadeias baseadas em Ethereum. Se utilizarmos a abordagem do Ethereum, é fácil cometer erros. A seguir, faremos uma análise geral do raciocínio para lidar com o Solana.
Compreender o Solana de forma única
Modelo de contas do Solana
O Solana utiliza um modelo de separação entre programas e dados, onde os programas podem ser reutilizados, enquanto os dados do programa são armazenados em contas PDA (Program Derived Address) específicas. Como os programas são comuns, é necessário um Token Mint para distinguir diferentes tokens. A conta Token Mint armazena metadados globais do token, como autoridade de emissão (mint_authority), suprimento total (supply), número de casas decimais (decimals), etc.
Cada token possui um endereço de conta Mint único, por exemplo, o USD Coin (USDC) na rede principal do Solana tem o endereço de Mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.
No Solana, existem duas versões de programas SPL Token: uma é SPL Token e outra é SPL Token-2022. Cada SPL Token tem uma ATA (Associated Token Account) independente que armazena o saldo do utilizador. Quando ocorre uma transferência de token, na verdade, chama-se o programa correspondente para transferir entre contas ATA.
Limitações do Log do Solana
No Ethereum, obtém-se as transferências de tokens analisando os logs históricos. No entanto, os logs do Solana não são preservados permanentemente por padrão, e não pertencem ao estado do livro-razão (não há um filtro Bloom para logs). Além disso, podem ser truncados durante a execução.
Por isso, não podemos simplesmente escanear logs para reconciliar depósitos, mas sim usar funções como getBlock ou getSignaturesForAddress para interpretar as instruções.
Confirmação e reorganização do Solana
O tempo de bloco do Solana é de aproximadamente 400ms. Após 32 confirmações (cerca de 12 segundos), o bloco atinge o estado finalizado (finalized). Se a exigência de tempo real não for alta, uma abordagem simples é confiar apenas nos blocos finalizados.
Para maior reatividade, é necessário considerar possíveis reorganizações de blocos, embora sejam raras. O consenso do Solana não depende do parentBlockHash para formar a cadeia, ao contrário do Ethereum, que usa parentBlockHash e blockHash para detectar forks. Como determinar se um bloco foi reorganizado?
Ao escanear localmente, devemos registrar o hash do bloco de cada slot. Se o hash de um slot específico mudar, indica uma reversão.
Compreendendo as diferenças do Solana, podemos avançar para a implementação, começando pelas modificações na base de dados:
Design da base de dados
Como o Solana possui dois tipos de tokens, devemos acrescentar uma coluna token_type na tabela tokens para distinguir entre spl-token e spl-token-2022.
Embora os endereços do Solana sejam diferentes dos do Ethereum, podem ser derivados usando BIP32 e BIP44, apenas com caminhos de derivação diferentes. Assim, podemos manter a tabela wallets existente, mas para suportar o mapeamento ATA e rastreamento de blocos do Solana, é necessário criar as seguintes tabelas:
Nome da Tabela
Campos-Chave
Descrição
solana_slots
slot, block_hash, status, parent_slot
Armazena informações redundantes de slots, útil para detectar forks e acionar rollback
solana_transactions
tx_hash, slot, to_addr, token_mint, amount, type
Detalhes de depósitos/saques, tx_hash único para rastreamento de assinaturas duplas
Regista o mapeamento ATA do utilizador, o módulo de scan pode consultar por ata_address
Detalhes adicionais:
solana_slots regista estados como confirmed, finalized ou skipped, e o scanner decide se armazena ou faz rollback com base nisso.
solana_transactions armazena valores em lamports ou unidades mínimas do token, com o campo type para distinguir entre depósitos, retiradas, etc.
solana_token_accounts garante a unicidade do ATA (combinando wallet_address e token_mint), sendo o índice principal na lógica de scan.
Para detalhes de definição, consulte db_gateway/database.md.
Processamento de depósitos de utilizador
Para processar depósitos, é necessário escanear continuamente os dados na cadeia do Solana, usando principalmente duas abordagens:
Escanear assinaturas com getSignaturesForAddress
Escanear blocos com getBlock
Método 1:
Escanear assinaturas de um endereço, chamando getSignaturesForAddress com o endereço do ATA gerado para o utilizador ou o programID (atenção: transferências do spl-token não incluem o endereço de mint na instrução). Passando os parâmetros before, until e limit, podemos obter assinaturas incrementais. Depois, usamos getTransaction para obter detalhes das transações.
Este método é adequado para poucos utilizadores ou contas. Para um volume elevado, o método de escanear blocos é preferível, que é o que utilizamos aqui.
Método 2:
Escanear blocos, obtendo o slot mais recente, chamando getBlock para obter detalhes completos, assinaturas e contas, filtrando por instruções e contas relevantes.
Nota: devido ao alto volume de transações e TPS do Solana, em produção, a velocidade de análise pode ficar aquém do ritmo de blocos. Recomenda-se usar filas de mensagens (Kafka, RabbitMQ) para filtrar transferências de tokens e enviar eventos potenciais de depósito. Utilizar Redis para armazenar dados de hotspots pode acelerar a filtragem. Para muitas addresses, dividir por ATA e usar múltiplos consumidores aumenta a eficiência.
Se não desejar escanear blocos manualmente, pode usar serviços de indexadores de terceiros, que oferecem Webhook, monitorização de contas e filtragem avançada, suportando grandes volumes de dados.
Fluxo de escaneamento de blocos
Utilizamos o método 2, com código principal em scan/solana-scan, nos ficheiros blockScanner.ts e txParser.ts. O fluxo geral:
1. Sincronização inicial (performInitialSync):
Começa do último slot escaneado, até ao mais recente.
A cada 100 slots, verifica se há novos slots.
Usa confirmação confirmada para equilibrar entre velocidade e fiabilidade.
2. Escaneamento contínuo (scanNewSlots):
Verifica constantemente novos slots.
Revalida slots confirmados, detectando rollback.
3. Análise do bloco (txParser.parseBlock):
Usa getBlock com commitment “confirmed” e encoding “jsonParsed”.
Percorre cada transação, analisando instructions e innerInstructions.
Processa apenas transações bem-sucedidas (tx.meta.err === null).
4. Análise de instruções (txParser.parseInstruction):
Transferências SOL: verifica se a instruction do System Program é do tipo transfer e se o destino está na lista de monitorados.
Transferências SPL Token: verifica se a instruction do Token Program ou Token-2022, do tipo transfer ou transferChecked, e se o destino é uma ATA mapeada.
Para cada instrução, mapeia ATA para endereço de carteira e token mint usando o banco de dados.
Ao detectar uma transação de depósito, após validação de segurança com DB Gateway e assinatura de risco, os dados são gravados na tabela de fluxos de fundos (credits).
Retiradas
O processo de retirada no Solana é semelhante ao do EVM, mas com diferenças na construção da transação:
Existem dois tipos de tokens: SPL-Token e SPL-Token 2022, com programIDs diferentes. Na construção, é preciso distinguir. (Atualmente, SPL-Token 2022 é menos comum e pode não ser suportado).
Uma transação do Solana consiste em duas partes: signatures (assinaturas ed25519) e message (com header, accountKeys, recentBlockhash, instructions). O recentBlockhash é usado para limitar a validade da transação, com uma validade de cerca de 150 blocos (~1 minuto). Assim, ao fazer uma retirada, deve-se obter o recentBlockhash mais recente na cadeia. Se a retirada precisar de revisão manual, é necessário reobter o recentBlockhash e assinar novamente.
Fluxo de retirada
[Imagem ilustrativa]
A obtenção do recentBlockhash deve ocorrer após a verificação de risco, para garantir que a transação seja válida.
Código principal de assinatura:
Para transferir SOL, cria-se uma instrução getTransferSolInstruction com origem, destino e valor.
Para transferir tokens, usa-se getTransferInstruction com ATA de origem, ATA de destino, autoridade e valor.
Constrói-se a mensagem de transação com createTransactionMessage, definindo o fee payer, o recentBlockhash, e adicionando a instrução.
A assinatura é feita com signTransactionMessageWithSigners.
A transação final é codificada em Base64 para envio.
Os detalhes completos do código de retirada encontram-se em:
Wallet: walletBusinessService.ts:405-754
Signer: solanaSigner.ts:29-122
Script de teste: requestWithdrawOnSolana.ts
Duas melhorias pendentes:
Verificação prévia de ATA: antes de retirar, garantir que a ATA de destino já exista, ou criar com custo adicional.
Prioridade de taxa: em congestão, ajustar computeUnitPrice para acelerar a transação.
Resumo
A integração do Solana na bolsa mantém a arquitetura geral, mas requer adaptação ao seu modelo de contas, estrutura de transações e mecanismo de consenso.
No processamento de depósitos, criar e manter o mapeamento ATA → carteira, monitorar mudanças de blockhash para detectar reorganizações, e atualizar o estado da transação de confirmed para finalized.
Na retirada, obter o recentBlockhash mais recente, distinguir entre SPL Token e Token-2022, e construir transações específicas para cada caso.
Esta página pode conter conteúdos de terceiros, que são fornecidos apenas para fins informativos (sem representações/garantias) e não devem ser considerados como uma aprovação dos seus pontos de vista pela Gate, nem como aconselhamento financeiro ou profissional. Consulte a Declaração de exoneração de responsabilidade para obter mais informações.
Desenvolvimento do sistema de carteiras da bolsa de valores — integração com a cadeia Solana
Na nossa última publicação, complementámos o sistema de controlo de riscos da bolsa. Nesta, vamos abordar a integração da carteira da bolsa com a cadeia Solana. O modelo de contas, armazenamento de logs e mecanismo de confirmação do Solana diferem bastante das cadeias baseadas em Ethereum. Se utilizarmos a abordagem do Ethereum, é fácil cometer erros. A seguir, faremos uma análise geral do raciocínio para lidar com o Solana.
Compreender o Solana de forma única
Modelo de contas do Solana
O Solana utiliza um modelo de separação entre programas e dados, onde os programas podem ser reutilizados, enquanto os dados do programa são armazenados em contas PDA (Program Derived Address) específicas. Como os programas são comuns, é necessário um Token Mint para distinguir diferentes tokens. A conta Token Mint armazena metadados globais do token, como autoridade de emissão (mint_authority), suprimento total (supply), número de casas decimais (decimals), etc.
Cada token possui um endereço de conta Mint único, por exemplo, o USD Coin (USDC) na rede principal do Solana tem o endereço de Mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.
No Solana, existem duas versões de programas SPL Token: uma é SPL Token e outra é SPL Token-2022. Cada SPL Token tem uma ATA (Associated Token Account) independente que armazena o saldo do utilizador. Quando ocorre uma transferência de token, na verdade, chama-se o programa correspondente para transferir entre contas ATA.
Limitações do Log do Solana
No Ethereum, obtém-se as transferências de tokens analisando os logs históricos. No entanto, os logs do Solana não são preservados permanentemente por padrão, e não pertencem ao estado do livro-razão (não há um filtro Bloom para logs). Além disso, podem ser truncados durante a execução.
Por isso, não podemos simplesmente escanear logs para reconciliar depósitos, mas sim usar funções como getBlock ou getSignaturesForAddress para interpretar as instruções.
Confirmação e reorganização do Solana
O tempo de bloco do Solana é de aproximadamente 400ms. Após 32 confirmações (cerca de 12 segundos), o bloco atinge o estado finalizado (finalized). Se a exigência de tempo real não for alta, uma abordagem simples é confiar apenas nos blocos finalizados.
Para maior reatividade, é necessário considerar possíveis reorganizações de blocos, embora sejam raras. O consenso do Solana não depende do parentBlockHash para formar a cadeia, ao contrário do Ethereum, que usa parentBlockHash e blockHash para detectar forks. Como determinar se um bloco foi reorganizado?
Ao escanear localmente, devemos registrar o hash do bloco de cada slot. Se o hash de um slot específico mudar, indica uma reversão.
Compreendendo as diferenças do Solana, podemos avançar para a implementação, começando pelas modificações na base de dados:
Design da base de dados
Como o Solana possui dois tipos de tokens, devemos acrescentar uma coluna token_type na tabela tokens para distinguir entre spl-token e spl-token-2022.
Embora os endereços do Solana sejam diferentes dos do Ethereum, podem ser derivados usando BIP32 e BIP44, apenas com caminhos de derivação diferentes. Assim, podemos manter a tabela wallets existente, mas para suportar o mapeamento ATA e rastreamento de blocos do Solana, é necessário criar as seguintes tabelas:
Detalhes adicionais:
Para detalhes de definição, consulte db_gateway/database.md.
Processamento de depósitos de utilizador
Para processar depósitos, é necessário escanear continuamente os dados na cadeia do Solana, usando principalmente duas abordagens:
Método 1:
Escanear assinaturas de um endereço, chamando getSignaturesForAddress com o endereço do ATA gerado para o utilizador ou o programID (atenção: transferências do spl-token não incluem o endereço de mint na instrução). Passando os parâmetros before, until e limit, podemos obter assinaturas incrementais. Depois, usamos getTransaction para obter detalhes das transações.
Este método é adequado para poucos utilizadores ou contas. Para um volume elevado, o método de escanear blocos é preferível, que é o que utilizamos aqui.
Método 2:
Escanear blocos, obtendo o slot mais recente, chamando getBlock para obter detalhes completos, assinaturas e contas, filtrando por instruções e contas relevantes.
Se não desejar escanear blocos manualmente, pode usar serviços de indexadores de terceiros, que oferecem Webhook, monitorização de contas e filtragem avançada, suportando grandes volumes de dados.
Fluxo de escaneamento de blocos
Utilizamos o método 2, com código principal em scan/solana-scan, nos ficheiros blockScanner.ts e txParser.ts. O fluxo geral:
1. Sincronização inicial (performInitialSync):
2. Escaneamento contínuo (scanNewSlots):
3. Análise do bloco (txParser.parseBlock):
4. Análise de instruções (txParser.parseInstruction):
Ao detectar uma transação de depósito, após validação de segurança com DB Gateway e assinatura de risco, os dados são gravados na tabela de fluxos de fundos (credits).
Retiradas
O processo de retirada no Solana é semelhante ao do EVM, mas com diferenças na construção da transação:
Fluxo de retirada
[Imagem ilustrativa]
A obtenção do recentBlockhash deve ocorrer após a verificação de risco, para garantir que a transação seja válida.
Código principal de assinatura:
Envio da transação à rede
Usa-se @solana/web3.js:
Os detalhes completos do código de retirada encontram-se em:
Duas melhorias pendentes:
Resumo
A integração do Solana na bolsa mantém a arquitetura geral, mas requer adaptação ao seu modelo de contas, estrutura de transações e mecanismo de consenso.
No processamento de depósitos, criar e manter o mapeamento ATA → carteira, monitorar mudanças de blockhash para detectar reorganizações, e atualizar o estado da transação de confirmed para finalized.
Na retirada, obter o recentBlockhash mais recente, distinguir entre SPL Token e Token-2022, e construir transações específicas para cada caso.