UNPKG

solana-dex-parser

Version:
234 lines 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DexParser = void 0; const constants_1 = require("./constants"); const instruction_classifier_1 = require("./instruction-classifier"); const parsers_1 = require("./parsers"); const parser_boopfun_1 = require("./parsers/boopfun/parser-boopfun"); const parser_jupiter_dca_1 = require("./parsers/jupiter/parser-jupiter-dca"); const transaction_adapter_1 = require("./transaction-adapter"); const transaction_utils_1 = require("./transaction-utils"); const utils_1 = require("./utils"); /** * Main parser class for Solana DEX transactions */ class DexParser { constructor() { // Trade parser mapping this.parserMap = { [constants_1.DEX_PROGRAMS.JUPITER.id]: parsers_1.JupiterParser, [constants_1.DEX_PROGRAMS.JUPITER_DCA.id]: parser_jupiter_dca_1.JupiterDcaParser, [constants_1.DEX_PROGRAMS.JUPITER_VA.id]: parsers_1.JupiterVAParser, [constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.id]: parsers_1.JupiterLimitOrderV2Parser, [constants_1.DEX_PROGRAMS.MOONSHOT.id]: parsers_1.MoonshotParser, [constants_1.DEX_PROGRAMS.METEORA.id]: parsers_1.MeteoraParser, [constants_1.DEX_PROGRAMS.METEORA_POOLS.id]: parsers_1.MeteoraParser, [constants_1.DEX_PROGRAMS.METEORA_DAMM.id]: parsers_1.MeteoraParser, [constants_1.DEX_PROGRAMS.PUMP_FUN.id]: parsers_1.PumpfunParser, [constants_1.DEX_PROGRAMS.PUMP_SWAP.id]: parsers_1.PumpswapParser, [constants_1.DEX_PROGRAMS.RAYDIUM_ROUTE.id]: parsers_1.RaydiumParser, [constants_1.DEX_PROGRAMS.RAYDIUM_CL.id]: parsers_1.RaydiumParser, [constants_1.DEX_PROGRAMS.RAYDIUM_CPMM.id]: parsers_1.RaydiumParser, [constants_1.DEX_PROGRAMS.RAYDIUM_V4.id]: parsers_1.RaydiumParser, [constants_1.DEX_PROGRAMS.RAYDIUM_LCP.id]: parsers_1.RaydiumLaunchpadParser, [constants_1.DEX_PROGRAMS.ORCA.id]: parsers_1.OrcaParser, [constants_1.DEX_PROGRAMS.BOOP_FUN.id]: parser_boopfun_1.BoopfunParser, }; // Liquidity parser mapping this.parseLiquidityMap = { [constants_1.DEX_PROGRAMS.METEORA.id]: parsers_1.MeteoraDLMMPoolParser, [constants_1.DEX_PROGRAMS.METEORA_POOLS.id]: parsers_1.MeteoraPoolsParser, [constants_1.DEX_PROGRAMS.METEORA_DAMM.id]: parsers_1.MeteoraDAMMPoolParser, [constants_1.DEX_PROGRAMS.RAYDIUM_V4.id]: parsers_1.RaydiumV4PoolParser, [constants_1.DEX_PROGRAMS.RAYDIUM_CPMM.id]: parsers_1.RaydiumCPMMPoolParser, [constants_1.DEX_PROGRAMS.RAYDIUM_CL.id]: parsers_1.RaydiumCLPoolParser, [constants_1.DEX_PROGRAMS.ORCA.id]: parsers_1.OrcaLiquidityParser, [constants_1.DEX_PROGRAMS.PUMP_FUN.id]: parsers_1.PumpswapLiquidityParser, [constants_1.DEX_PROGRAMS.PUMP_SWAP.id]: parsers_1.PumpswapLiquidityParser, }; // Transfer parser mapping this.parseTransferMap = { [constants_1.DEX_PROGRAMS.JUPITER_DCA.id]: parser_jupiter_dca_1.JupiterDcaParser, [constants_1.DEX_PROGRAMS.JUPITER_VA.id]: parsers_1.JupiterVAParser, [constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER.id]: parsers_1.JupiterLimitOrderParser, [constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.id]: parsers_1.JupiterLimitOrderV2Parser, }; } /** * Parse transaction with specific type */ parseWithClassifier(tx, config = { tryUnknowDEX: true, aggregateTrades: true }, parseType) { const result = { state: true, fee: { amount: '0', uiAmount: 0, decimals: 9 }, trades: [], liquidities: [], transfers: [], moreEvents: {}, context: {}, }; try { const adapter = new transaction_adapter_1.TransactionAdapter(tx, config); const utils = new transaction_utils_1.TransactionUtils(adapter); const classifier = new instruction_classifier_1.InstructionClassifier(adapter); result.context = { utils, adapter, }; // Get DEX information and validate const dexInfo = utils.getDexInfo(classifier); const allProgramIds = classifier.getAllProgramIds(); if (config?.programIds && !config.programIds.some((id) => allProgramIds.includes(id))) { result.state = false; return result; } const transferActions = utils.getTransferActions(['mintTo', 'burn', 'mintToChecked', 'burnChecked']); // Process fee result.fee = adapter.fee; // Process user balance change result.solBalanceChange = adapter.getAccountSolBalanceChanges(false)?.get(adapter.signer); result.tokenBalanceChange = adapter.getAccountTokenBalanceChanges(true)?.get(adapter.signer); // Try specific parser first if (dexInfo.programId && [ constants_1.DEX_PROGRAMS.JUPITER.id, constants_1.DEX_PROGRAMS.JUPITER_DCA.id, constants_1.DEX_PROGRAMS.JUPITER_DCA_KEEPER1.id, constants_1.DEX_PROGRAMS.JUPITER_DCA_KEEPER2.id, constants_1.DEX_PROGRAMS.JUPITER_DCA_KEEPER3.id, constants_1.DEX_PROGRAMS.JUPITER_VA.id, constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.id, ].includes(dexInfo.programId)) { if (parseType === 'trades' || parseType === 'all') { const jupiterInstructions = classifier.getInstructions(dexInfo.programId); const TradeParserClass = this.parserMap[dexInfo.programId]; if (TradeParserClass) { const parser = new TradeParserClass(adapter, { ...dexInfo, programId: dexInfo.programId, amm: (0, utils_1.getProgramName)(dexInfo.programId) }, transferActions, jupiterInstructions); const trades = parser.processTrades(); if (trades.length > 0) { if (config.aggregateTrades == true) { result.trades.push(utils.attachTradeFee((0, utils_1.getFinalSwap)(trades))); } else { result.trades.push(...trades); } } } } if (result.trades.length > 0) { return result; } } // Process instructions for each program for (const programId of allProgramIds) { if (config?.programIds && !config.programIds.some((id) => id == programId)) continue; if (config?.ignoreProgramIds && config.ignoreProgramIds.some((id) => id == programId)) continue; const classifiedInstructions = classifier.getInstructions(programId); // Process trades if needed if (parseType === 'trades' || parseType === 'all') { const TradeParserClass = this.parserMap[programId]; if (TradeParserClass) { const parser = new TradeParserClass(adapter, { ...dexInfo, programId: programId, amm: (0, utils_1.getProgramName)(programId) }, transferActions, classifiedInstructions); result.trades.push(...parser.processTrades()); } else if (config?.tryUnknowDEX) { // Handle unknown DEX programs const transfers = Object.entries(transferActions).find(([key]) => key.startsWith(programId))?.[1]; if (transfers && transfers.length >= 2 && transfers.some((it) => adapter.isSupportedToken(it.info.mint))) { const trade = utils.processSwapData(transfers, { ...dexInfo, programId: programId, amm: (0, utils_1.getProgramName)(programId), }); if (trade) result.trades.push(utils.attachTokenTransferInfo(trade, transferActions)); } } } // Process liquidity if needed if (parseType === 'liquidity' || parseType === 'all') { const LiquidityParserClass = this.parseLiquidityMap[programId]; if (LiquidityParserClass) { const parser = new LiquidityParserClass(adapter, transferActions, classifiedInstructions); result.liquidities.push(...utils.attachUserBalanceToLPs(parser.processLiquidity())); } } } // Deduplicate trades if (result.trades.length > 0) { result.trades = [...new Map(result.trades.map((item) => [`${item.idx}-${item.signature}`, item])).values()]; } // Process transfer if needed (if no trades and no liquidity) if (result.trades.length == 0 && result.liquidities.length == 0) { if (parseType === 'transfer' || parseType === 'all') { if (dexInfo.programId) { const classifiedInstructions = classifier.getInstructions(dexInfo.programId); const TransferParserClass = this.parseTransferMap[dexInfo.programId]; if (TransferParserClass) { const parser = new TransferParserClass(adapter, dexInfo, transferActions, classifiedInstructions); result.transfers.push(...parser.processTransfers()); } } if (result.transfers.length == 0) { result.transfers.push(...Object.values(transferActions).flat()); } } } // Process more events if needed this.processMoreEvents(parseType, result, allProgramIds, adapter, transferActions, classifier); } catch (error) { if (config.throwError) { throw error; } const msg = `Parse error: ${tx?.transaction?.signatures?.[0]} ${error}`; result.state = false; result.msg = msg; } return result; } processMoreEvents(parseType, result, allProgramIds, adapter, transferActions, classifier) { if (parseType === 'all') { if (allProgramIds.includes(constants_1.DEX_PROGRAMS.PUMP_FUN.id)) { result.moreEvents[constants_1.DEX_PROGRAMS.PUMP_FUN.name] = new parsers_1.PumpfunEventParser(adapter).parseInstructions(classifier.getInstructions(constants_1.DEX_PROGRAMS.PUMP_FUN.id)); } if (allProgramIds.includes(constants_1.DEX_PROGRAMS.PUMP_SWAP.id)) { result.moreEvents[constants_1.DEX_PROGRAMS.PUMP_SWAP.name] = new parsers_1.PumpswapEventParser(adapter).parseInstructions(classifier.getInstructions(constants_1.DEX_PROGRAMS.PUMP_SWAP.id)); } if (allProgramIds.includes(constants_1.DEX_PROGRAMS.BOOP_FUN.id)) { result.moreEvents[constants_1.DEX_PROGRAMS.BOOP_FUN.name] = new parsers_1.BoopfunEventParser(adapter, transferActions).parseInstructions(classifier.getInstructions(constants_1.DEX_PROGRAMS.BOOP_FUN.id)); } if (allProgramIds.includes(constants_1.DEX_PROGRAMS.RAYDIUM_LCP.id)) { result.moreEvents[constants_1.DEX_PROGRAMS.RAYDIUM_LCP.name] = new parsers_1.RaydiumLaunchpadEventParser(adapter).parseInstructions(classifier.getInstructions(constants_1.DEX_PROGRAMS.RAYDIUM_LCP.id)); } } } /** * Parse trades from transaction */ parseTrades(tx, config) { return this.parseWithClassifier(tx, config, 'trades').trades; } /** * Parse liquidity events from transaction */ parseLiquidity(tx, config) { return this.parseWithClassifier(tx, config, 'liquidity').liquidities; } /** * Parse transfers from transaction (if no trades and no liquidity) */ parseTransfers(tx, config) { return this.parseWithClassifier(tx, config, 'transfer').transfers; } /** * Parse both trades and liquidity events from transaction */ parseAll(tx, config) { return this.parseWithClassifier(tx, config, 'all'); } } exports.DexParser = DexParser; //# sourceMappingURL=dex-parser.js.map