solana-dex-parser
Version:
Solana Dex Transaction Parser
245 lines • 13 kB
JavaScript
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.MOONIT.id]: parsers_1.MoonitParser,
[constants_1.DEX_PROGRAMS.METEORA.id]: parsers_1.MeteoraParser,
[constants_1.DEX_PROGRAMS.METEORA_DAMM.id]: parsers_1.MeteoraParser,
[constants_1.DEX_PROGRAMS.METEORA_DAMM_V2.id]: parsers_1.MeteoraParser,
[constants_1.DEX_PROGRAMS.METEORA_DBC.id]: parsers_1.MeteoraDBCParser,
[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_AMM.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_DAMM.id]: parsers_1.MeteoraPoolsParser,
[constants_1.DEX_PROGRAMS.METEORA_DAMM_V2.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.RaydiumCLPoolV2Parser,
[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,
};
// Meme parser mapping
this.parseMemeEventMap = {
[constants_1.DEX_PROGRAMS.PUMP_FUN.id]: parsers_1.PumpfunEventParser,
[constants_1.DEX_PROGRAMS.METEORA_DBC.id]: parsers_1.MeteoraDBCEventParser,
[constants_1.DEX_PROGRAMS.RAYDIUM_LCP.id]: parsers_1.RaydiumLaunchpadEventParser,
[constants_1.DEX_PROGRAMS.BOOP_FUN.id]: parsers_1.BoopfunEventParser,
[constants_1.DEX_PROGRAMS.MOONIT.id]: parsers_1.MoonitEventParser,
[constants_1.DEX_PROGRAMS.HEAVEN.id]: parsers_1.HeavenEventParser,
[constants_1.DEX_PROGRAMS.SUGAR.id]: parsers_1.SugarEventParser,
};
}
/**
* 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: [],
memeEvents: [],
slot: tx.slot,
msg: '',
timestamp: 0,
signature: '',
signer: [],
computeUnits: 0,
txStatus: 'unknown'
};
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);
// Get DEX information and validate
const dexInfo = utils.getDexInfo(classifier);
const allProgramIds = classifier.getAllProgramIds();
result.timestamp = adapter.blockTime;
result.signature = adapter.signature;
result.signer = adapter.signers;
result.computeUnits = adapter.computeUnits;
result.txStatus = adapter.txStatus;
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.aggregateTrade = 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()));
}
}
if (parseType === 'all') {
const MemeParserClass = this.parseMemeEventMap[programId];
if (MemeParserClass) {
const parser = new MemeParserClass(adapter, transferActions);
result.memeEvents.push(...parser.processEvents());
}
}
}
// Deduplicate trades
if (result.trades.length > 0) {
result.trades = [...new Map(result.trades.map((item) => [`${item.idx}-${item.signature}`, item])).values()];
if (config.aggregateTrades == true) {
result.aggregateTrade = utils.attachTradeFee((0, utils_1.getFinalSwap)(result.trades));
}
}
// 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());
}
}
}
}
catch (error) {
if (config.throwError) {
throw error;
}
const msg = `Parse error: ${tx?.transaction?.signatures?.[0]} ${error}`;
result.state = false;
result.msg = msg;
}
return result;
}
/**
* 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
;