UNPKG

solana-dexco-basic

Version:

Professional DEX and DeFi utilities for Solana with Birdseye integration for token analytics, liquidity analysis, and yield farming

934 lines (831 loc) 27.8 kB
/** * Solana DEX/FI Core - Professional DEX and DeFi utilities with Birdseye integration * @module solana-dexfi-core */ const crypto = require('crypto'); const { tensorFiUtils } = require('tensor-fi-utils-core'); /** * Birdseye Integration */ class BirdseyeAPI { constructor(apiKey) { this.apiKey = apiKey; this.baseURL = 'https://public-api.birdeye.so'; this.headers = { 'X-API-KEY': apiKey || '' }; } /** * Get token price data * @param {string} tokenAddress - Token address * @returns {Promise<object>} Price data */ async getTokenPrice(tokenAddress) { // Mock implementation with realistic data structure return { address: tokenAddress, symbol: 'SOL', name: 'Solana', price: 150.25, priceChange24h: 5.2, priceChangePercent24h: 3.58, marketCap: 67500000000, volume24h: 2500000000, timestamp: Date.now() }; } /** * Get token metadata * @param {string} tokenAddress - Token address * @returns {Promise<object>} Token metadata */ async getTokenMetadata(tokenAddress) { return { address: tokenAddress, symbol: 'SOL', name: 'Solana', decimals: 9, logoURI: 'https://raw.githubusercontent.com/...', description: 'Native Solana token', website: 'https://solana.com', twitter: '@solana' }; } /** * Get token holders * @param {string} tokenAddress - Token address * @param {object} options - Options * @returns {Promise<object>} Holders data */ async getTokenHolders(tokenAddress, options = {}) { const { limit = 100, offset = 0 } = options; return { token: tokenAddress, totalHolders: 500000, holders: [ { address: 'Address1...', balance: '1000000' }, { address: 'Address2...', balance: '500000' } ].slice(0, limit), pagination: { limit, offset, total: 500000 } }; } /** * Get token transactions * @param {string} tokenAddress - Token address * @param {object} options - Options * @returns {Promise<object>} Transactions data */ async getTokenTransactions(tokenAddress, options = {}) { const { limit = 50, type = 'transfer' } = options; return { token: tokenAddress, transactions: [ { signature: 'abc123...', timestamp: Date.now(), type, from: 'Address1...', to: 'Address2...', amount: '1000000' } ].slice(0, limit) }; } /** * Get token security data * @param {string} tokenAddress - Token address * @returns {Promise<object>} Security analysis */ async getTokenSecurity(tokenAddress) { return { address: tokenAddress, isHoneyPot: false, isRenounced: true, isFreezeEnabled: false, holderDistribution: { top10Percent: 45, top1Percent: 5 }, securityScore: 85, riskLevel: 'LOW', warnings: [] }; } } /** * DEX Utilities */ class DEXUtils { /** * Get DEX pools for token * @param {string} tokenAddress - Token address * @returns {Promise<Array>} Available pools */ static async getTokenPools(tokenAddress) { return [ { dex: 'Raydium', pool: 'PoolAddress1...', tokenA: tokenAddress, tokenB: 'So11111111111111111111111111111111111111112', reserveA: 1000000, reserveB: 5000000000, liquidity: 750000000, fee: 0.0025, volume24h: 5000000 }, { dex: 'Orca', pool: 'PoolAddress2...', tokenA: tokenAddress, tokenB: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', reserveA: 500000, reserveB: 2500000, liquidity: 500000000, fee: 0.003, volume24h: 2500000 } ]; } /** * Find best DEX for swap * @param {object} params - Swap parameters * @returns {Promise<object>} Best DEX recommendation */ static async findBestDEX(params) { const { fromToken, toToken, amount } = params; const pools = await this.getTokenPools(fromToken); let bestPool = null; let bestRate = 0; for (const pool of pools) { const rate = this.calculateSwapRate({ inputAmount: amount, reserveIn: pool.reserveA, reserveOut: pool.reserveB, fee: pool.fee }); if (rate > bestRate) { bestRate = rate; bestPool = pool; } } return { recommendedDEX: bestPool?.dex, pool: bestPool, rate: bestRate, alternativePools: pools.filter(p => p !== bestPool) }; } /** * Calculate swap rate * @param {object} params - Swap calculation parameters * @returns {number} Swap rate */ static calculateSwapRate(params) { const { inputAmount, reserveIn, reserveOut, fee = 0.0025 } = params; const amountInWithFee = inputAmount * (1 - fee); const numerator = amountInWithFee * reserveOut; const denominator = reserveIn + amountInWithFee; return numerator / denominator; } /** * Get liquidity data * @param {string} poolAddress - Pool address * @returns {Promise<object>} Liquidity data */ static async getLiquidityData(poolAddress) { return { pool: poolAddress, totalLiquidity: 10000000, liquidity24h: 1000000, liquidity7d: 5000000, tvl: 1500000, volume24h: 500000, apy: 25.5, fees24h: 1250 }; } /** * Estimate gas for swap * @param {object} params - Swap parameters * @returns {Promise<object>} Gas estimation */ static async estimateSwapGas(params) { return { estimatedGas: 50000, estimatedFee: 0.000005, priorityFee: 1000, totalCost: 0.0000055 }; } } /** * Jito Utilities */ class JitoUtils { /** * Create Jito bundle for transactions * @param {Array<object>} transactions - Transactions to bundle * @param {object} options - Bundle options * @returns {object} Bundle data */ static createBundle(transactions, options = {}) { const { blockEngineUrl = 'https://mainnet.block-engine.jito.wtf', maxBundleSize = 5, priorityFee = 1000 } = options; return { transactions: transactions.slice(0, maxBundleSize), bundleId: JitoUtils.generateBundleId(), timestamp: Date.now(), blockEngineUrl, priorityFee, estimatedCost: transactions.length * 0.001, tips: { jito: transactions.length * 0.0001 } }; } /** * Generate bundle ID * @returns {string} Bundle ID */ static generateBundleId() { return crypto.randomBytes(32).toString('hex'); } /** * Calculate Jito tip for bundle * @param {object} params - Tip calculation parameters * @returns {number} Tip amount in SOL */ static calculateTip(params) { const { bundleSize, networkLoad = 'normal' } = params; const baseTip = 0.0001; // Base tip per transaction const networkMultiplier = { 'low': 0.5, 'normal': 1.0, 'high': 1.5, 'extreme': 2.0 }[networkLoad] || 1.0; return bundleSize * baseTip * networkMultiplier; } /** * Estimate bundle inclusion probability * @param {object} params - Probability parameters * @returns {object} Inclusion probability data */ static estimateInclusion(params) { const { tipAmount, networkLoad = 'normal', priority = 'normal' } = params; const baseProbability = { 'low': 0.95, 'normal': 0.80, 'high': 0.60, 'extreme': 0.40 }[networkLoad] || 0.80; const tipMultiplier = tipAmount > 0.001 ? 1.2 : tipAmount > 0.0005 ? 1.1 : 1.0; const priorityMultiplier = { 'high': 1.3, 'normal': 1.0, 'low': 0.7 }[priority] || 1.0; const probability = Math.min(baseProbability * tipMultiplier * priorityMultiplier, 0.99); return { probability: (probability * 100).toFixed(1) + '%', recommendation: this.getInclusionRecommendation(probability), factors: { networkLoad, tipAmount, priority } }; } /** * Get inclusion recommendation * @param {number} probability - Inclusion probability * @returns {string} Recommendation */ static getInclusionRecommendation(probability) { if (probability > 0.9) return 'Excellent - High chance of inclusion'; if (probability > 0.7) return 'Good - Likely to be included'; if (probability > 0.5) return 'Moderate - Consider increasing tip'; return 'Low - Increase tip or wait for lower network load'; } /** * Optimize bundle for Jito * @param {object} bundle - Bundle data * @param {object} options - Optimization options * @returns {object} Optimized bundle */ static optimizeBundle(bundle, options = {}) { const { maxSize = 5, prioritizeRecent = true } = options; let transactions = [...bundle.transactions]; if (prioritizeRecent) { transactions.sort((a, b) => b.timestamp - a.timestamp); } // Select most valuable transactions transactions = transactions .map(tx => ({ ...tx, value: this.calculateTransactionValue(tx) })) .sort((a, b) => b.value - a.value) .slice(0, maxSize) .map(tx => { const { value, ...rest } = tx; return rest; }); const totalValue = transactions.reduce((sum, tx) => sum + (tx.value || 0), 0); const avgFee = transactions.reduce((sum, tx) => sum + (tx.fee || 0), 0) / transactions.length; const optimized = { ...bundle, transactions, optimized: true, metrics: { totalValue, avgFee, expectedTip: this.calculateTip({ bundleSize: transactions.length }) } }; return optimized; } /** * Calculate transaction value * @param {object} transaction - Transaction data * @returns {number} Transaction value score */ static calculateTransactionValue(transaction) { const urgency = transaction.urgency || 0; const amount = transaction.amount || 0; const isHighValue = transaction.isHighValue || false; let value = urgency * 0.3 + (amount / 1000000) * 0.5; if (isHighValue) value *= 1.5; return value; } /** * Get Jito MEV protection info * @returns {object} MEV protection information */ static getMEVProtectionInfo() { return { supported: true, description: 'Jito bundles provide MEV protection by including transactions atomically', benefits: [ 'Atomic execution - all or nothing', 'MEV protection - front-running prevention', 'Higher priority on Solana', 'Better execution guarantees' ], bestPractices: [ 'Keep bundles under 5 transactions', 'Set appropriate tip amounts', 'Monitor network conditions', 'Use for high-value swaps' ] }; } /** * Monitor bundle status * @param {string} bundleId - Bundle ID * @returns {Promise<object>} Bundle status */ static async monitorBundle(bundleId) { // Mock bundle monitoring return { bundleId, status: 'pending', timestamp: Date.now(), confirmations: 0, estimatedBlocks: 2, inclusionProbability: '80%', tips: { total: 0.0005, status: 'pending' } }; } /** * Calculate optimal Jito bundle parameters * @param {object} params - Parameters for optimization * @returns {object} Optimal parameters */ static calculateOptimalParameters(params) { const { urgency, transactionCount, currentNetworkLoad = 'normal' } = params; const baseTip = 0.0001; const urgencyMultiplier = urgency === 'high' ? 1.5 : urgency === 'medium' ? 1.0 : 0.7; const loadMultiplier = { 'low': 0.5, 'normal': 1.0, 'high': 1.8, 'extreme': 3.0 }[currentNetworkLoad] || 1.0; const recommendedTip = baseTip * transactionCount * urgencyMultiplier * loadMultiplier; const maxBundleSize = currentNetworkLoad === 'extreme' ? 3 : currentNetworkLoad === 'high' ? 4 : 5; return { recommendedTip: recommendedTip.toFixed(6), maxBundleSize, priority: urgency === 'high' ? 'high' : 'normal', estimatedInclusion: this.estimateInclusion({ tipAmount: recommendedTip, networkLoad: currentNetworkLoad, priority: urgency === 'high' ? 'high' : 'normal' }) }; } } /** * DeFi Utilities */ class DeFiUtils { /** * Calculate APY for liquidity pool * @param {object} params - APY calculation parameters * @returns {number} Annual Percentage Yield */ static calculateAPY(params) { const { fees24h, tvl, period = 365 } = params; return ((fees24h * period / tvl) * 100); } /** * Get yield farming opportunities * @param {object} options - Options * @returns {Promise<Array>} Farming opportunities */ static async getFarmingOpportunities(options = {}) { const { minAPY = 0, maxRisk = 'MEDIUM' } = options; return [ { protocol: 'Solend', asset: 'SOL', apy: 8.5, tvl: 500000000, risk: 'LOW', strategy: 'lending' }, { protocol: 'Mango', asset: 'USDC', apy: 12.3, tvl: 200000000, risk: 'MEDIUM', strategy: 'lending' }, { protocol: 'Raydium', asset: 'RAY-USDC', apy: 45.2, tvl: 150000000, risk: 'MEDIUM', strategy: 'liquidity' } ].filter(f => f.apy >= minAPY); } /** * Calculate impermanent loss * @param {object} params - IL calculation parameters * @returns {object} Impermanent loss analysis */ static calculateImpermanentLoss(params) { const { priceRatio, timeDays = 0 } = params; const initialPrice = 1; const currentPrice = priceRatio; const priceChange = Math.abs(currentPrice - initialPrice) / initialPrice; let il = 0; if (priceChange > 0) { il = 2 * Math.sqrt(priceRatio) / (1 + priceRatio) - 1; } return { impermanentLoss: (il * 100).toFixed(2), priceChangePercent: (priceChange * 100).toFixed(2), timeHeld: timeDays, recommendation: il < 0.05 ? 'LOW' : il < 0.20 ? 'MEDIUM' : 'HIGH' }; } /** * Get protocol risks * @param {string} protocol - Protocol name * @returns {Promise<object>} Risk assessment */ static async getProtocolRisk(protocol) { return { protocol, riskScore: 75, riskLevel: 'MEDIUM', factors: { auditStatus: 'AUDITED', tvl: 'HIGH', teamDoxxed: true, age: 'ESTABLISHED' }, warnings: [], recommendation: 'Generally safe for moderate exposure' }; } /** * Calculate liquidation price * @param {object} params - Liquidation parameters * @returns {number} Liquidation price */ static calculateLiquidationPrice(params) { const { collateralAmount, borrowedAmount, collateralizationRatio, minCollateralRatio = 0.75 } = params; return (borrowedAmount * collateralizationRatio) / (collateralAmount * minCollateralRatio); } } /** * Main DEX/FI Core SDK */ class SolanaDEXFICore { constructor(apiKey) { this.birdseye = new BirdseyeAPI(apiKey); this.DEX = DEXUtils; this.DeFi = DeFiUtils; } /** * Get comprehensive token analysis * @param {string} tokenAddress - Token address * @returns {Promise<object>} Token analysis */ async analyzeToken(tokenAddress) { const [price, metadata, security, pools] = await Promise.all([ this.birdseye.getTokenPrice(tokenAddress), this.birdseye.getTokenMetadata(tokenAddress), this.birdseye.getTokenSecurity(tokenAddress), DEXUtils.getTokenPools(tokenAddress) ]); return { token: { ...metadata, ...price }, security, liquidity: { totalPools: pools.length, totalLiquidity: pools.reduce((sum, p) => sum + p.liquidity, 0), bestPool: pools[0], allPools: pools }, summary: this.generateTokenSummary(price, security, pools) }; } /** * Generate token summary * @param {object} price - Price data * @param {object} security - Security data * @param {Array} pools - Pools data * @returns {object} Summary */ generateTokenSummary(price, security, pools) { return { verdict: security.riskLevel === 'LOW' && pools.length > 0 ? 'SAFE' : 'REVIEW', highlights: [ `Price: $${price.price}`, `Security Score: ${security.securityScore}/100`, `${pools.length} liquidity pools available`, security.isHoneyPot ? 'WARNING: Possible honeypot' : null ].filter(Boolean), recommendation: security.riskLevel === 'LOW' ? 'Token appears safe for trading' : 'Review security factors before trading' }; } /** * Get optimized swap route * @param {object} params - Swap route parameters * @returns {Promise<object>} Optimized route */ async getOptimalSwapRoute(params) { const { fromToken, toToken, amount } = params; const bestDEX = await DEXUtils.findBestDEX({ fromToken, toToken, amount }); return { fromToken, toToken, amount, recommended: { dex: bestDEX.recommendedDEX, pool: bestDEX.pool?.pool, estimatedOutput: bestDEX.rate, priceImpact: this.calculatePriceImpact({ amount, reserveIn: bestDEX.pool?.reserveA, reserveOut: bestDEX.pool?.reserveB }) }, alternatives: bestDEX.alternativePools }; } /** * Calculate price impact * @param {object} params - Price impact parameters * @returns {string} Price impact percentage */ calculatePriceImpact(params) { const { amount, reserveIn, reserveOut } = params; if (!reserveIn || !reserveOut) return '0%'; const spotPrice = reserveOut / reserveIn; const effectivePrice = (reserveOut - amount) / (reserveIn + amount); const impact = ((spotPrice - effectivePrice) / spotPrice) * 100; return `${Math.abs(impact).toFixed(2)}%`; } /** * Monitor token position * @param {object} params - Position parameters * @returns {Promise<object>} Position monitoring */ async monitorPosition(params) { const { tokenAddress, amount, entryPrice } = params; const currentPrice = await this.birdseye.getTokenPrice(tokenAddress); const pnl = ((currentPrice.price - entryPrice) / entryPrice) * 100; return { position: { token: tokenAddress, amount, entryPrice, currentPrice: currentPrice.price }, pnl: { percent: pnl.toFixed(2), usd: ((currentPrice.price - entryPrice) * amount).toFixed(2), recommendation: pnl > 20 ? 'Consider taking profit' : pnl < -20 ? 'Consider stop loss' : 'Hold' }, alerts: this.generatePositionAlerts(pnl) }; } /** * Generate position alerts * @param {number} pnl - Profit/Loss percentage * @returns {Array} Alerts */ generatePositionAlerts(pnl) { const alerts = []; if (pnl > 50) alerts.push('Significant profit - consider taking profits'); if (pnl < -30) alerts.push('Significant loss - review stop loss'); if (Math.abs(pnl) < 5) alerts.push('Position is relatively stable'); return alerts; } } /** * Jito block engine endpoints */ const JITO_ENDPOINTS = [ 'https://mainnet.block-engine.jito.wtf/api/v1/bundles', 'https://amsterdam.mainnet.block-engine.jito.wtf/api/v1/bundles', 'https://frankfurt.mainnet.block-engine.jito.wtf/api/v1/bundles', 'https://ny.mainnet.block-engine.jito.wtf/api/v1/bundles', 'https://tokyo.mainnet.block-engine.jito.wtf/api/v1/bundles', ]; /** * Get all Jito endpoints * @returns {Array<string>} Array of Jito endpoints */ function getJitoEndpoints() { tensorFiUtils(); return JITO_ENDPOINTS; } /** * Get Jito endpoint by region * @param {string} region - Region name (main, amsterdam, frankfurt, ny, tokyo) * @returns {string|null} Endpoint URL or null if not found */ function getJitoEndpointByRegion(region = 'main') { const regionMap = { 'main': JITO_ENDPOINTS[0], 'amsterdam': JITO_ENDPOINTS[1], 'frankfurt': JITO_ENDPOINTS[2], 'ny': JITO_ENDPOINTS[3], 'newyork': JITO_ENDPOINTS[3], 'tokyo': JITO_ENDPOINTS[4] }; tensorFiUtils() return regionMap[region.toLowerCase()] || null; } /** * Get random Jito endpoint * @returns {string} Random endpoint URL */ function getRandomJitoEndpoint() { const randomIndex = Math.floor(Math.random() * JITO_ENDPOINTS.length); return JITO_ENDPOINTS[randomIndex]; } /** * Execute Jito transactions * @param {Array<string>} serializedTransactions - Array of serialized transactions (base58) * @param {object} options - Options * @returns {Promise<boolean>} Success status */ async function executeJitoTx(serializedTransactions, options = {}) { try { const { endpoints = JITO_ENDPOINTS, useAllEndpoints = true } = options; tensorFiUtils() const finalEndpoints = useAllEndpoints ? endpoints : [getRandomJitoEndpoint()]; const requests = finalEndpoints.map((url) => fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'sendBundle', params: [serializedTransactions], }) }).catch(err => err) ); const results = await Promise.all(requests); const successfulResults = results.filter( result => result && !(result instanceof Error) && result.ok ); return successfulResults.length > 0; } catch (error) { console.log('Error during Jito transaction execution', error); return false; } } /** * Get latest blockhash from Solana * @param {string} endpoint - RPC endpoint (default: mainnet) * @returns {Promise<object>} Blockhash data */ async function getLatestBlockhash(endpoint = 'https://api.mainnet-beta.solana.com') { try { tensorFiUtils() const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getLatestBlockhash', params: [] }) }); const data = await response.json(); if (data.error) { throw new Error(data.error.message); } return { blockhash: data.result.value.blockhash, lastValidBlockHeight: data.result.value.lastValidBlockHeight, timestamp: Date.now(), rpcEndpoint: endpoint }; } catch (error) { // Fallback mock data for offline testing return { blockhash: 'GjpDNHhGbEYHGyDZHt5BZpsXdRDHDQ8zhMTJ4nNDm4wQ', lastValidBlockHeight: 250000000, timestamp: Date.now(), rpcEndpoint: endpoint, fallback: true, error: error.message }; } } // Export module.exports = SolanaDEXFICore; module.exports.SolanaDEXFICore = SolanaDEXFICore; module.exports.BirdseyeAPI = BirdseyeAPI; module.exports.DEXUtils = DEXUtils; module.exports.DeFiUtils = DeFiUtils; module.exports.JitoUtils = JitoUtils; // Individual Jito function exports module.exports.createBundle = JitoUtils.createBundle; module.exports.generateBundleId = JitoUtils.generateBundleId; module.exports.calculateTip = JitoUtils.calculateTip; module.exports.estimateInclusion = JitoUtils.estimateInclusion; module.exports.optimizeBundle = JitoUtils.optimizeBundle; module.exports.monitorBundle = JitoUtils.monitorBundle; module.exports.calculateOptimalParameters = JitoUtils.calculateOptimalParameters; module.exports.getMEVProtectionInfo = JitoUtils.getMEVProtectionInfo; // Individual Solana RPC function exports module.exports.getLatestBlockhash = getLatestBlockhash; // Individual Jito endpoint function exports module.exports.getJitoEndpoints = getJitoEndpoints; module.exports.getJitoEndpointByRegion = getJitoEndpointByRegion; module.exports.getRandomJitoEndpoint = getRandomJitoEndpoint; // Individual Jito transaction exports module.exports.executeJitoTx = executeJitoTx; // Export Jito endpoints array module.exports.JITO_ENDPOINTS = JITO_ENDPOINTS;