UNPKG

p-sdk-wallet

Version:

A comprehensive wallet SDK for React Native (pwc), supporting multi-chain and multi-account features.

189 lines (188 loc) 7.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BatchProcessor = void 0; const ChainService_1 = require("../chain/ChainService"); const SolanaChainService_1 = require("../chain/SolanaChainService"); const ethers_1 = require("ethers"); /** * Handles batch processing of transfers for both native tokens and ERC-20/SPL tokens. * Processes transfers in batches to optimize gas usage and provide progress tracking. */ class BatchProcessor { /** * Processes a batch of native token transfers. * @param chainService - The chain service instance (EVM or Solana) * @param recipients - Array of recipients to transfer to * @param onProgress - Optional progress callback * @returns Promise resolving to batch processing results */ static async processNativeTokenBatch(chainService, recipients, onProgress) { const transactions = []; const failed = []; let totalGasUsed = BigInt(0); for (let i = 0; i < recipients.length; i++) { const recipient = recipients[i]; try { let txResult; if (chainService instanceof SolanaChainService_1.SolanaChainService) { // Solana native token transfer txResult = await chainService.sendTransaction(recipient.address, recipient.amount); } else { // EVM native token transfer txResult = await chainService.sendTransaction({ to: recipient.address, value: ethers_1.ethers.parseEther(recipient.amount) }); } transactions.push(txResult); // Update gas used (only for EVM chains that support it) if (chainService instanceof ChainService_1.ChainService && txResult && typeof txResult === 'object' && 'gasUsed' in txResult) { const gasUsed = txResult.gasUsed; if (gasUsed && typeof gasUsed === 'bigint') { totalGasUsed += gasUsed; } } // Progress callback if (onProgress) { onProgress(i + 1, recipients.length, txResult.hash); } // Small delay to prevent rate limiting if (i < recipients.length - 1) { await new Promise(resolve => setTimeout(resolve, 100)); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; failed.push({ recipient, error: new Error(`Failed to transfer to ${recipient.address}: ${errorMessage}`) }); // Still call progress callback for failed transfers if (onProgress) { onProgress(i + 1, recipients.length, 'FAILED'); } } } return { transactions, failed, gasUsed: totalGasUsed }; } /** * Processes a batch of ERC-20/SPL token transfers. * @param chainService - The chain service instance (EVM or Solana) * @param tokenAddress - The token contract address * @param recipients - Array of recipients to transfer to * @param onProgress - Optional progress callback * @returns Promise resolving to batch processing results */ static async processTokenBatch(chainService, tokenAddress, recipients, onProgress) { const transactions = []; const failed = []; let totalGasUsed = BigInt(0); for (let i = 0; i < recipients.length; i++) { const recipient = recipients[i]; try { let txResult; if (chainService instanceof SolanaChainService_1.SolanaChainService) { // Solana SPL token transfer txResult = await chainService.sendToken(tokenAddress, recipient.address, recipient.amount); } else { // EVM ERC-20 token transfer txResult = await chainService.sendToken(tokenAddress, recipient.address, recipient.amount); } transactions.push(txResult); // Update gas used (only for EVM chains that support it) if (chainService instanceof ChainService_1.ChainService && txResult && typeof txResult === 'object' && 'gasUsed' in txResult) { const gasUsed = txResult.gasUsed; if (gasUsed && typeof gasUsed === 'bigint') { totalGasUsed += gasUsed; } } // Progress callback if (onProgress) { onProgress(i + 1, recipients.length, txResult.hash); } // Small delay to prevent rate limiting if (i < recipients.length - 1) { await new Promise(resolve => setTimeout(resolve, 100)); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; failed.push({ recipient, error: new Error(`Failed to transfer token to ${recipient.address}: ${errorMessage}`) }); // Still call progress callback for failed transfers if (onProgress) { onProgress(i + 1, recipients.length, 'FAILED'); } } } return { transactions, failed, gasUsed: totalGasUsed }; } /** * Splits recipients into batches of specified size. * @param recipients - Array of all recipients * @param batchSize - Size of each batch * @returns Array of recipient batches */ static splitIntoBatches(recipients, batchSize) { const batches = []; for (let i = 0; i < recipients.length; i += batchSize) { batches.push(recipients.slice(i, i + batchSize)); } return batches; } /** * Validates recipient addresses and amounts. * @param recipients - Array of recipients to validate * @returns Validation result */ static validateRecipients(recipients) { const errors = []; if (!recipients || recipients.length === 0) { errors.push('No recipients provided'); return { isValid: false, errors }; } if (recipients.length > 100) { errors.push('Maximum 100 recipients allowed per operation'); } const addresses = new Set(); recipients.forEach((recipient, index) => { // Validate address if (!recipient.address || recipient.address.trim() === '') { errors.push(`Recipient ${index + 1}: Invalid address`); } else if (addresses.has(recipient.address.toLowerCase())) { errors.push(`Recipient ${index + 1}: Duplicate address ${recipient.address}`); } else { addresses.add(recipient.address.toLowerCase()); } // Validate amount if (!recipient.amount || recipient.amount.trim() === '') { errors.push(`Recipient ${index + 1}: Invalid amount`); } else { const amount = parseFloat(recipient.amount); if (isNaN(amount) || amount <= 0) { errors.push(`Recipient ${index + 1}: Amount must be a positive number`); } } }); return { isValid: errors.length === 0, errors }; } } exports.BatchProcessor = BatchProcessor;