瀏覽代碼

feat(blockchain): 新增 BSC 和 SOL 钱包功能- 新增 BSC 和 SOL地址生成方法
- 新增 BSC 和 SOL 余额查询功能
- 更新相关依赖:
- 添加 @solana/web3.js - 添加 bignumber.js - 更新 axios 版本

wui 7 月之前
父節點
當前提交
82a474ee28
共有 3 個文件被更改,包括 284 次插入138 次删除
  1. 157 13
      app/Services/BlockchainWalletService.ts
  2. 2 0
      package.json
  3. 125 125
      yarn.lock

+ 157 - 13
app/Services/BlockchainWalletService.ts

@@ -6,6 +6,8 @@ import { TronWeb } from 'tronweb'
 import * as ecc from 'tiny-secp256k1'
 import Logger from '@ioc:Adonis/Core/Logger'
 import axios from 'axios'
+import { Connection, PublicKey, LAMPORTS_PER_SOL, Keypair } from '@solana/web3.js'
+import BigNumber from 'bignumber.js'
 
 interface WalletBalance {
     address: string
@@ -15,20 +17,30 @@ interface WalletBalance {
 interface WalletAddresses {
     btc: WalletBalance
     eth: WalletBalance
+    bsc: WalletBalance
     tron: WalletBalance
+    sol: WalletBalance
 }
 
 const CONFIG = {
     ETH_API_KEY: process.env.ETH_API_KEY || 'JSYCSZEYYRI1G6CTYW8J6RPXEZGTMBFFMM',
+    BSC_API_KEY: process.env.BSC_API_KEY || 'APB5SN5JUZM44V5MB9U1KGJDITPU2IJDD4',
     BITCOIN_API_URL: 'https://blockchain.info/rawaddr',
     ETHERSCAN_API_URL: 'https://api.etherscan.io/v2/api',
+    BSCSCAN_API_URL: 'https://api.bscscan.com/api',
     TRON_API_URL: 'https://apilist.tronscan.org/api/account',
     TRON_FULL_HOST: 'https://api.trongrid.io',
+    SOLANA_API_URL: 'https://api.mainnet-beta.solana.com',
     DECIMAL_PLACES: 8,
+    CHAIN_IDS: {
+        ETH: 1,
+        BSC: 56
+    },
     BIP_PATHS: {
         BTC: (accountIndex: number) => `m/44'/0'/${accountIndex}'/0/0`,
         ETH: (accountIndex: number) => `m/44'/60'/${accountIndex}'/0/0`,
-        TRON: (accountIndex: number) => `m/44'/195'/${accountIndex}'/0/0`
+        TRON: (accountIndex: number) => `m/44'/195'/${accountIndex}'/0/0`,
+        SOL: (accountIndex: number) => `m/44'/501'/${accountIndex}'/0/0`
     }
 }
 
@@ -49,7 +61,9 @@ class BlockchainWalletService {
         const defaultResult: WalletAddresses = {
             btc: { address: '-', balance: this.formatBalance(0) },
             eth: { address: '-', balance: this.formatBalance(0) },
-            tron: { address: '-', balance: this.formatBalance(0) }
+            bsc: { address: '-', balance: this.formatBalance(0) },
+            tron: { address: '-', balance: this.formatBalance(0) },
+            sol: { address: '-', balance: this.formatBalance(0) }
         }
 
         try {
@@ -60,23 +74,33 @@ class BlockchainWalletService {
             const seed = this.getSeedFromMnemonic(mnemonic)
 
             // 并行生成所有地址
-            const [btcAddress, ethAddress, tronAddress] = await Promise.all([
-                Promise.resolve(this.generateBTCAddress(seed, accountIndex)),
-                Promise.resolve(this.generateETHAddress(seed, accountIndex)),
-                Promise.resolve(this.generateTRONAddress(seed, accountIndex))
-            ])
+            const [btcAddress, ethAddress, bscAddress, tronAddress, solAddress] = await Promise.all(
+                [
+                    Promise.resolve(this.generateBTCAddress(seed, accountIndex)),
+                    Promise.resolve(this.generateETHAddress(seed, accountIndex)),
+                    Promise.resolve(this.generateBSCAddress(seed, accountIndex)),
+                    Promise.resolve(this.generateTRONAddress(seed, accountIndex)),
+                    Promise.resolve(this.generateSOLAddress(seed, accountIndex))
+                ]
+            )
 
             // 并行获取所有余额
-            const [btcBalance, ethBalance, tronBalance] = await Promise.all([
-                this.getBitcoinBalance(btcAddress),
-                this.getEthereumBalance(ethAddress),
-                this.getTronBalance(tronAddress)
-            ])
+            const [btcBalance, ethBalance, bscBalance, tronBalance, solBalance] = await Promise.all(
+                [
+                    this.getBitcoinBalance(btcAddress),
+                    this.getEthereumBalance(ethAddress),
+                    this.getBscBalance(bscAddress),
+                    this.getTronBalance(tronAddress),
+                    this.getSolanaBalance(solAddress)
+                ]
+            )
 
             return {
                 btc: { address: btcAddress, balance: btcBalance },
                 eth: { address: ethAddress, balance: ethBalance },
-                tron: { address: tronAddress, balance: tronBalance }
+                bsc: { address: bscAddress, balance: bscBalance },
+                tron: { address: tronAddress, balance: tronBalance },
+                sol: { address: solAddress, balance: solBalance }
             }
         } catch (error) {
             Logger.error(`Failed to get addresses: ${(error as Error).message}`)
@@ -210,6 +234,55 @@ class BlockchainWalletService {
         return account
     }
 
+    /**
+     * 生成BSC地址
+     * @param seed 助记词生成的种子
+     * @param accountIndex 账户索引,默认为0
+     * @returns BSC地址
+     * @throws {Error} 如果地址生成失败
+     */
+    private generateBSCAddress(seed: Buffer, accountIndex: number = 0): string {
+        const path = CONFIG.BIP_PATHS.ETH(accountIndex)
+        const child = this.deriveChildNodeFromSeed(seed, path)
+
+        if (!child.privateKey) {
+            throw new Error('Failed to generate BSC private key')
+        }
+
+        const privateKeyHex = Buffer.from(child.privateKey).toString('hex')
+        const wallet = new ethers.Wallet(`0x${privateKeyHex}`)
+
+        return wallet.address
+    }
+
+    /**
+     * 生成Solana(SOL)地址
+     * @param seed 助记词生成的种子
+     * @param accountIndex 账户索引,默认为0
+     * @returns SOL地址
+     * @throws {Error} 如果地址生成失败
+     */
+    private generateSOLAddress(seed: Buffer, accountIndex: number = 0): string {
+        try {
+            const path = CONFIG.BIP_PATHS.SOL(accountIndex)
+            const child = this.deriveChildNodeFromSeed(seed, path)
+
+            if (!child.privateKey) {
+                throw new Error('Failed to generate SOL private key')
+            }
+
+            // 使用私钥创建 Solana 密钥对
+            const keypair = Keypair.fromSeed(child.privateKey.slice(0, 32))
+            return keypair.publicKey.toString()
+        } catch (error) {
+            Logger.error('Failed to generate SOL address:', error)
+            if (error instanceof Error) {
+                throw error
+            }
+            throw new Error('Failed to generate SOL address')
+        }
+    }
+
     /**
      * 生成比特币(BTC)地址 - 公共接口
      * @param mnemonic 助记词字符串
@@ -248,6 +321,28 @@ class BlockchainWalletService {
         return this.generateTRONAddress(seed, accountIndex)
     }
 
+    /**
+     * 生成BSC地址 - 公共接口
+     * @param mnemonic 助记词字符串
+     * @param accountIndex 账户索引,默认为0
+     * @returns BSC地址
+     */
+    public getBSCAddress(mnemonic: string, accountIndex: number = 0): string {
+        const seed = this.getSeedFromMnemonic(mnemonic)
+        return this.generateBSCAddress(seed, accountIndex)
+    }
+
+    /**
+     * 生成Solana(SOL)地址 - 公共接口
+     * @param mnemonic 助记词字符串
+     * @param accountIndex 账户索引,默认为0
+     * @returns SOL地址
+     */
+    public getSOLAddress(mnemonic: string, accountIndex: number = 0): string {
+        const seed = this.getSeedFromMnemonic(mnemonic)
+        return this.generateSOLAddress(seed, accountIndex)
+    }
+
     /**
      * 获取比特币余额
      * @param address 比特币地址
@@ -324,6 +419,55 @@ class BlockchainWalletService {
             return this.formatBalance(0)
         }
     }
+
+    /**
+     * 获取BSC余额
+     * @param address BSC地址
+     * @returns BSC余额(8位小数格式字符串)
+     */
+    public async getBscBalance(address: string): Promise<string> {
+        try {
+            const response = await axios.get(CONFIG.BSCSCAN_API_URL, {
+                params: {
+                    module: 'account',
+                    action: 'balance',
+                    address,
+                    tag: 'latest',
+                    apikey: CONFIG.BSC_API_KEY
+                }
+            })
+
+            if (response.data.status === '1' && response.data.message === 'OK') {
+                return new BigNumber(response.data.result)
+                    .dividedBy(new BigNumber(10).pow(18))
+                    .toFixed(CONFIG.DECIMAL_PLACES)
+            }
+
+            Logger.error(`Failed to get balance for BSC: ${response.data.message}`)
+            return this.formatBalance(0)
+        } catch (error) {
+            Logger.error('Failed to get BSC balance:', error)
+            return this.formatBalance(0)
+        }
+    }
+
+    /**
+     * 获取Solana余额
+     * @param address Solana地址
+     * @returns Solana余额(8位小数格式字符串)
+     */
+    public async getSolanaBalance(address: string): Promise<string> {
+        try {
+            const connection = new Connection(CONFIG.SOLANA_API_URL)
+            const publicKey = new PublicKey(address)
+
+            const balance = await connection.getBalance(publicKey)
+            return this.formatBalance(balance / LAMPORTS_PER_SOL)
+        } catch (error) {
+            Logger.error('Failed to get Solana balance:', error)
+            return this.formatBalance(0)
+        }
+    }
 }
 
 export default new BlockchainWalletService()

+ 2 - 0
package.json

@@ -72,8 +72,10 @@
         "@alicloud/tea-util": "^1.4.7",
         "@aws-sdk/client-s3": "^3.458.0",
         "@aws-sdk/client-sts": "^3.458.0",
+        "@solana/web3.js": "^1.98.2",
         "ali-oss": "^6.18.1",
         "axios": "^1.6.7",
+        "bignumber.js": "^9.3.0",
         "bip32": "^5.0.0-rc.0",
         "bip39": "^3.1.0",
         "bitcoinjs-lib": "^6.1.7",

文件差異過大導致無法顯示
+ 125 - 125
yarn.lock


部分文件因文件數量過多而無法顯示