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

282 lines 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Liquidator = void 0; const web3_js_1 = require("@solana/web3.js"); const spl_token_1 = require("@solana/spl-token"); const base_delegate_1 = require("./base-delegate"); const jupiter_1 = require("./swap/jupiter"); const raydium_1 = require("./swap/raydium"); class Liquidator extends base_delegate_1.BaseDelegate { constructor(connection, signerKeypair, heliusClient, feeTakerKeypair) { super(connection, signerKeypair, feeTakerKeypair); this.heliusClient = heliusClient; // Initialize swap protocols this.jupiterSwap = new jupiter_1.JupiterSwap(signerKeypair, connection, { heliusClient }); this.raydiumSwap = new raydium_1.RaydiumSwap(signerKeypair, { heliusClient }); } async executeDelegate(delegateOptions) { const requestId = this.generateRequestId(); try { this.logOperation('liquidator_execution_started', { requestId }); this.validateOptions(delegateOptions); const { delegateAddress, tokenAddress, minUsdValue = 1 } = delegateOptions; this.logOperation('liquidator_setup', { requestId, delegateAddress, targetToken: tokenAddress, minUsdValue }); // Get all token accounts for the delegate address const tokenAccounts = await this.getTokenAccounts(delegateAddress); if (!tokenAccounts || tokenAccounts.length === 0) { this.logOperation('no_token_accounts_found', { requestId, delegateAddress }); return { success: true, signatures: [], liquidatedTokens: [], totalLiquidated: 0 }; } const signatures = []; const liquidatedTokens = []; let totalLiquidated = 0; // Process each token account for (let i = 0; i < tokenAccounts.length; i++) { const tokenAccount = tokenAccounts[i]; if (!tokenAccount) { this.logOperation('token_account_skipped', { requestId, reason: 'undefined_account', index: i }); continue; } // Skip frozen accounts if (tokenAccount.frozen) { this.logOperation('token_account_skipped', { requestId, mint: tokenAccount.mint, reason: 'frozen_account' }); continue; } // Skip USDC (common stablecoin) if (tokenAccount.mint === "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") { this.logOperation('token_account_skipped', { requestId, mint: tokenAccount.mint, reason: 'usdc_excluded' }); continue; } try { const result = await this.processTokenLiquidation(tokenAccount, tokenAddress, minUsdValue, i + 1, tokenAccounts.length, requestId); if (result.success && result.signature) { signatures.push(result.signature); liquidatedTokens.push({ mint: tokenAccount.mint, amount: tokenAccount.amount, signature: result.signature }); totalLiquidated++; } this.logOperation('token_liquidation_processed', { requestId, mint: tokenAccount.mint, amount: tokenAccount.amount, success: result.success, signature: result.signature }); } catch (error) { await this.handleError(error instanceof Error ? error : new Error(String(error)), { requestId, mint: tokenAccount.mint }); // Continue with next token account instead of failing completely this.logOperation('token_liquidation_skipped', { requestId, mint: tokenAccount.mint, error: error instanceof Error ? error.message : String(error) }); continue; } } this.logOperation('liquidator_execution_completed', { requestId, signatures, totalLiquidated }); return { success: true, signatures, liquidatedTokens, totalLiquidated }; } catch (error) { await this.handleError(error instanceof Error ? error : new Error(String(error)), { requestId }); throw error; } } validateOptions(delegateOptions) { this.validateRequiredField(delegateOptions.delegateAddress, 'delegateAddress'); this.validatePublicKey(delegateOptions.delegateAddress, 'delegateAddress'); this.validateRequiredField(delegateOptions.tokenAddress, 'tokenAddress'); this.validatePublicKey(delegateOptions.tokenAddress, 'tokenAddress'); if (delegateOptions.minUsdValue !== undefined) { this.validateNumberField(delegateOptions.minUsdValue, 'minUsdValue', 0); } } async getTokenAccounts(delegateAddress) { try { const response = await this.heliusClient.getTokenAccounts(new web3_js_1.PublicKey(delegateAddress)); return response.value || []; } catch (error) { this.logOperation('get_token_accounts_failed', { delegateAddress, error: error instanceof Error ? error.message : String(error) }); return []; } } async processTokenLiquidation(tokenAccount, targetTokenAddress, minUsdValue, currentIndex, totalAccounts, requestId) { const { mint: tokenMint, amount: tokenAmount } = tokenAccount; this.logOperation('liquidation_progress', { requestId, currentIndex, totalAccounts, mint: tokenMint, amount: tokenAmount }); // Get token decimals const decimals = await this.getTokenDecimals(tokenMint); if (decimals === -1) { throw new Error(`Failed to get decimals for token ${tokenMint}`); } // Calculate token amount in human readable format const tokenAmountInUnits = tokenAmount / Math.pow(10, decimals); // Try to get quote from Jupiter first let quote = null; try { quote = await this.jupiterSwap.getQuote(tokenMint, targetTokenAddress, tokenAmountInUnits, 0.5); } catch (error) { this.logOperation('jupiter_quote_failed', { requestId, fromToken: tokenMint, toToken: targetTokenAddress, amount: tokenAmountInUnits, error: error instanceof Error ? error.message : String(error) }); } // If Jupiter quote failed or price is too low, try Raydium if (!quote || (quote.swapUsdValue && quote.swapUsdValue < minUsdValue)) { try { quote = await this.raydiumSwap.getQuote(tokenMint, targetTokenAddress, tokenAmountInUnits, 0.5); } catch (error) { this.logOperation('raydium_quote_failed', { requestId, fromToken: tokenMint, toToken: targetTokenAddress, amount: tokenAmountInUnits, error: error instanceof Error ? error.message : String(error) }); } } // If no quote available or price is too low, skip if (!quote || (quote.swapUsdValue && quote.swapUsdValue < minUsdValue)) { this.logOperation('liquidation_skipped_low_value', { requestId, mint: tokenMint, usdValue: quote?.swapUsdValue || 0, minUsdValue }); return { success: false, error: 'Price too low or no quote available' }; } // Execute swap with fallback const swapResult = await this.executeSwapWithFallback(tokenMint, targetTokenAddress, tokenAmountInUnits, 0.5 // 0.5% slippage ); return swapResult; } async executeSwapWithFallback(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, signature: result.signature }; } } catch (error) { this.logOperation('jupiter_swap_failed', { fromAsset, toAsset, amount, error: error instanceof Error ? error.message : String(error) }); } // Fallback to Raydium try { const result = await this.retryOperation(() => this.executeRaydiumSwap(fromAsset, toAsset, amount, slippage), 3); if (result.success) { return { ...result, signature: result.signature }; } } catch (error) { this.logOperation('raydium_swap_failed', { fromAsset, toAsset, amount, error: error instanceof Error ? error.message : String(error) }); } return { success: false, error: "Both Jupiter and Raydium swaps failed" }; } 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; } 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; } async getTokenDecimals(tokenAddress) { try { const mint = new web3_js_1.PublicKey(tokenAddress); const mintInfo = await this.retryOperation(async () => { return await (0, spl_token_1.getMint)(this.connection, mint); }, 3); return mintInfo.decimals; } catch (error) { this.logOperation('get_token_decimals_failed', { tokenAddress, error: error instanceof Error ? error.message : String(error) }); return -1; } } } exports.Liquidator = Liquidator; //# sourceMappingURL=liquidator.js.map