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
JavaScript
/**
* 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;