UNPKG

delegate-framework

Version:

A TypeScript framework for building robust, production-ready blockchain workflows with comprehensive error handling, logging, and testing. Maintained by delegate.fun

212 lines 8.76 kB
"use strict"; /** * Framework-ized Swapper for executing token swaps with fallback between protocols * * Usage Example: * ```typescript * const swapper = new Swapper( * privateKey, * 'https://mainnet.helius-rpc.com', * 'your-api-key', * { * jupiter: { * tokenListUrl: 'https://token.jup.ag/all', * fallbackDecimals: 6 * } * } * ); * * const result = await swapper.executeSwap({ * fromAsset: 'So11111111111111111111111111111111111111112', // SOL * toAssets: ['EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'], // USDC * numTokens: 0.1, // Optional: specific amount, otherwise uses available balance * slippage: 0.5 // Optional: default 0.5% * }); * * if (result.success) { * console.log(`Swap successful using ${result.protocol}: ${result.signature}`); * } else { * console.log(`Swap failed: ${result.error}`); * } * ``` */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Swapper = void 0; const web3_js_1 = require("@solana/web3.js"); const spl_token_1 = require("@solana/spl-token"); const bs58_1 = __importDefault(require("bs58")); const jupiter_1 = require("./swap/jupiter"); const raydium_1 = require("./swap/raydium"); const helius_1 = require("../solana/clients/helius"); const base_delegate_1 = require("./base-delegate"); class Swapper extends base_delegate_1.BaseDelegate { constructor(privateKey, rpcUrl, apiKey, config = {}) { const keypair = web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(privateKey)); const connection = new web3_js_1.Connection(rpcUrl, { commitment: 'confirmed' }); super(connection, keypair); this.heliusClient = new helius_1.HeliusClient({ apiKey, rpcUrl }); // Initialize swap protocols with HeliusClient this.jupiterSwap = new jupiter_1.JupiterSwap(keypair, connection, { ...config.jupiter, heliusClient: this.heliusClient }); this.raydiumSwap = new raydium_1.RaydiumSwap(keypair, { ...config.raydium, heliusClient: this.heliusClient }); } /** * Execute a swap task with fallback between protocols */ async executeSwap(task) { const requestId = this.generateRequestId(); try { this.logOperation('swap_execution_started', { requestId }); // Validate task if (!task.fromAsset || !task.toAssets || task.toAssets.length !== 1) { throw new Error("Invalid swap task: fromAsset and exactly one toAsset required"); } const fromAsset = task.fromAsset; // Now TypeScript knows this is not undefined const toAsset = task.toAssets[0]; // Assert non-null since we validated length const slippage = task.slippage || 0.5; // Default 0.5% slippage // Get swap amount const swapAmount = await this.getSwapAmount(fromAsset, task.numTokens); console.log(`Attempting swap: ${swapAmount} ${fromAsset} -> ${toAsset}`); // Try Jupiter first, then Raydium as fallback const result = await this.swapWithFallback(fromAsset, toAsset, swapAmount, slippage); if (result.success) { console.log(`Swap successful using ${result.protocol}: ${result.signature}`); this.logOperation('swap_execution_completed', { requestId, protocol: result.protocol, signature: result.signature }); } else { console.log(`Swap failed: ${result.error}`); this.logOperation('swap_execution_failed', { requestId, error: result.error }); } return result; } catch (error) { await this.handleError(error instanceof Error ? error : new Error(String(error)), { requestId }); throw error; } } /** * Get the appropriate swap amount based on asset type and balance */ async getSwapAmount(fromAsset, specifiedAmount) { if (specifiedAmount) { return specifiedAmount; } if (fromAsset === "So11111111111111111111111111111111111111112") { // SOL - use available balance minus buffer const balance = await this.heliusClient.getBalance(this.signerKeypair.publicKey); const costBuffer = 0.005 * web3_js_1.LAMPORTS_PER_SOL; // Keep 0.005 SOL for fees return Math.max(0, (balance - costBuffer) / web3_js_1.LAMPORTS_PER_SOL); } else { // Token - get token balance const tokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(new web3_js_1.PublicKey(fromAsset), this.signerKeypair.publicKey, true); const balanceInfo = await this.heliusClient.getTokenAccountBalance(tokenAccount); return parseFloat(balanceInfo.value.uiAmountString || '0'); } } /** * Execute swap with fallback between protocols */ async swapWithFallback(fromAsset, toAsset, amount, slippage) { // Try Jupiter first try { const result = await this.retryOperation(() => this.executeJupiterSwap(fromAsset, toAsset, amount, slippage), 3); if (result.success) { return { ...result, protocol: 'Jupiter' }; } } catch (error) { console.warn("Jupiter swap failed, trying Raydium:", error); } // Fallback to Raydium try { const result = await this.retryOperation(() => this.executeRaydiumSwap(fromAsset, toAsset, amount, slippage), 3); if (result.success) { return { ...result, protocol: 'Raydium' }; } } catch (error) { console.error("Raydium swap also failed:", error); } return { success: false, error: "Both Jupiter and Raydium swaps failed" }; } /** * Execute Jupiter swap */ async executeJupiterSwap(fromAsset, toAsset, amount, slippage) { // Get quote const quote = await this.jupiterSwap.getQuote(fromAsset, toAsset, amount, slippage); if (!quote) { throw new Error("Failed to get Jupiter quote"); } // Create transaction const transaction = await this.jupiterSwap.createSwapTransaction(quote); // Execute swap const result = await this.jupiterSwap.executeSwap(transaction); return result; } /** * Execute Raydium swap */ async executeRaydiumSwap(fromAsset, toAsset, amount, slippage) { // Get quote const quote = await this.raydiumSwap.getQuote(fromAsset, toAsset, amount, slippage); if (!quote) { throw new Error("Failed to get Raydium quote"); } // Create transaction const transaction = await this.raydiumSwap.createSwapTransaction(quote); // Execute swap const result = await this.raydiumSwap.executeSwap(transaction); return result; } /** * Get current balances for all assets */ async getBalances() { const requestId = this.generateRequestId(); try { this.logOperation('balance_check_started', { requestId }); const solBalance = await this.heliusClient.getBalance(this.signerKeypair.publicKey); const tokenAccounts = await this.heliusClient.getTokenAccounts(this.signerKeypair.publicKey); const tokens = await Promise.all(tokenAccounts.value.map(async (account) => { const balanceInfo = await this.heliusClient.getTokenAccountBalance(account.pubkey); return { mint: balanceInfo.value.mint, balance: parseFloat(balanceInfo.value.uiAmountString || '0'), decimals: balanceInfo.value.decimals }; })); const result = { sol: solBalance / web3_js_1.LAMPORTS_PER_SOL, tokens }; this.logOperation('balance_check_completed', { requestId, tokenCount: tokens.length }); return result; } catch (error) { await this.handleError(error instanceof Error ? error : new Error(String(error)), { requestId }); throw error; } } // Required abstract method implementations async executeDelegate() { throw new Error("Use executeSwap instead"); } validateOptions() { // No validation needed for this delegate } } exports.Swapper = Swapper; //# sourceMappingURL=swapper.js.map