this is SolanaProvider.jsimport { Provider } from '@modules/core/provider/base/Provider';import axios from 'axios';import {Connection, PublicKey, clusterApiUrl, Transaction, SystemProgram, LAMPORTS_PER_SOL} from '@solana/web3.js';import {Cluster} from "@solana/web3.js";
export class SolanaProvider implements Provider {apiInstance: typeof axios; // Axios instance for HTTP requestsconnection: Connection; // Solana connection object
constructor({ rpcUrl, apiEndpoint, apiKey }) { this.apiInstance = axios.create({ baseURL: `${apiEndpoint}` || 'https://api.mainnet-beta.solana.com', // Default to Solana mainnet API headers: apiKey ? { 'Authorization': apiKey } : {}, // Optional API key }); this.connection = new Connection(rpcUrl || clusterApiUrl('mainnet-beta')); // Establish connection to Solana network}async getNetwork(): Promise<Cluster> { const connection = new Connection(process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com', 'confirmed'); // Or use your preferred RPC try { const version = await connection.getVersion(); // Get the Solana node's version information const cluster = version['solana-core'].includes('testnet') ? 'testnet' : 'mainnet-beta'; // Basic cluster identification return cluster; } catch (error) { console.error("Error fetching network information:", error); throw new Error('Failed to get network information'); }}async getGasPrice(): Promise<number> { // Solana doesn't have a gas price concept like Ethereum. // Instead, you pay transaction fees in SOL. // Here, you can fetch the recent blockhash to estimate fees: const recentBlockhash = await this.connection.getRecentBlockhash(); return recentBlockhash.feeCalculator.lamportsPerSignature; // Lamports per signature is the fee unit in Solana}async getFeeData(): Promise<{ lamportsPerSignature: number }> { const connection = new Connection(process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com', 'confirmed'); // Or your preferred RPC endpoint try { const feeCalculator = await connection.getFeeCalculatorForBlockhash( await connection.getRecentBlockhash('confirmed'), // Get the recent blockhash ); return { lamportsPerSignature: feeCalculator.value.lamportsPerSignature }; } catch (error) { console.error("Error fetching fee data:", error); throw new Error("Failed to get fee data"); }}async estimateGas({ from, to, data }) { // In Solana, transaction size influences fees, not gas. // You can estimate transaction size here: const transaction = new Transaction().add( SystemProgram.transfer({ fromPubkey: new PublicKey(from), toPubkey: new PublicKey(to), lamports: LAMPORTS_PER_SOL / 100, // Example: Transfer 0.01 SOL }) ); transaction.recentBlockhash = (await this.connection.getRecentBlockhash('finalized')).blockhash; const transactionSize = transaction.serialize({requireAllSignatures: false}).length; const estimatedFee = transactionSize * await this.getGasPrice(); return {success: true, data: estimatedFee};}async getTransactions( address: string, limit: number = 10, // Default to fetching 10 transactions before?: string, // Optional parameter for pagination until?: string, // Optional parameter for pagination): Promise<Array<any>> { // Adjust type as needed for your transaction representation const connection = new Connection(process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com', 'confirmed'); const pubKey = new PublicKey(address); try { let transactions: Array<any> = []; // Fetch signatures first for efficiency (can be batched if needed) const signatures = await connection.getSignaturesForAddress( pubKey, { limit, before, until } ); // Fetch transaction details for each signature for (const signature of signatures) { const transaction = await connection.getTransaction(signature.signature, { commitment: 'confirmed' }); if (transaction) { transactions.push(transaction); // Add to the results } } return transactions; } catch (error) { console.error("Error fetching transactions:", error); throw new Error("Failed to get transactions"); }}
}
and this is SolanaWallet.js
import 'react-native-get-random-values';import {Logs} from '@modules/log/logs';import {Keypair, PublicKey, SystemProgram, Transaction} from '@solana/web3.js';import * as Bip39 from 'bip39';import {derivePath} from 'ed25519-hd-key';import bs58 from 'bs58';import {SolanaProvider} from '@modules/core/provider/solana/SolanaProvider'; // Assuming you have thisimport {Wallet} from "ethers";import { mnemonicToSeedSync } from '@scure/bip39';
export const SOLANA_BIP39_PATH = "m/44'/501'/0'/0'";
export class SolanaWallet implements Wallet{provider: SolanaProvider;keypair: Keypair;
constructor(provider: SolanaProvider) { this.provider = provider;}async mnemonicToSeed(mnemonic: string): Promise<Buffer> { // Solana uses the "m/44'/501'/0'/0'" derivation path (hardened) const path = "m/44'/501'/0'/0'"; const seed = mnemonicToSeedSync(mnemonic); return derivePath(path, seed.toString('hex')).key;}async fromMnemonic(data, mnemonic): Promise<Object> { try { const seed = await this.mnemonicToSeed(mnemonic); const derivedSeed = derivePath(SOLANA_BIP39_PATH, seed.toString('hex')).key; this.keypair = Keypair.fromSeed(derivedSeed); return { success: true, data: { ...data, walletAddress: this.keypair.publicKey.toString(), privateKey: bs58.encode(this.keypair.secretKey), // Encode to Base58 }, }; } catch (e) { Logs.info('SolanaWallet: fromMnemonic', e); // ... (Error handling) }}async fromPrivateKey(data, privateKey): Promise<Object> { try { this.keypair = Keypair.fromSecretKey(bs58.decode(privateKey)); // Decode from Base58 return { success: true, data: { ...data, walletAddress: this.keypair.publicKey.toString(), }, }; } catch (e) { Logs.info('SolanaWallet: fromPrivateKey', e); // ... (Error handling) }}async sendTransaction({to, value, feePayer}): Promise<Object> { try { const transaction = new Transaction().add( SystemProgram.transfer({ fromPubkey: this.keypair.publicKey, toPubkey: new PublicKey(to), lamports: value, // Value should be in lamports }) ); transaction.feePayer = feePayer || this.keypair.publicKey; // Optionally specify a fee payer const blockhash = await this.provider.connection.getRecentBlockhash(); transaction.recentBlockhash = blockhash.blockhash; transaction.sign(this.keypair); const txid = await this.provider.connection.sendRawTransaction(transaction.serialize()); return { success: true, data: { txid }, }; } catch (e) { Logs.info('SolanaWallet: sendTransaction', e); // ... (Error handling) }}async getTransactions(wallet, limit = 20, before = null): Promise<Object> { try { const signatures = await this.provider.connection.getSignaturesForAddress( new PublicKey(wallet.walletAddress), { limit, before } ); const transactions = await this.provider.connection.getParsedConfirmedTransactions(signatures.map(s => s.signature)); return { success: true, data: transactions.filter(tx => tx !== null).map(tx => { // Filter out null transactions (might occur if not yet confirmed) return { signature: tx.transaction.signatures[0], blockTime: tx.blockTime, from: tx.transaction.message.accountKeys[0].pubkey, // Assuming simple transfer to: tx.transaction.message.instructions[0]?.parsed.info.destination, value: tx.transaction.message.instructions[0]?.parsed.info.lamports, fee: tx.meta.fee, status: tx.meta.err ? 'Failed' : 'Success', // ... (Add other relevant fields as needed) }; }), }; } catch (e) { Logs.info('SolanaWallet: getTransactions', e); return { success: false, data: [], }; }}
}