All files parser.ts

86.76% Statements 59/68
69.23% Branches 9/13
76.92% Functions 20/26
88.88% Lines 56/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106  1x   1x 1x 1x 1x 1x 1x 1x 1x   1x 16x     16x     16x       16x 16x 16x 16x 2x   2x 1x 1x 1x     16x     16x 4x   16x     16x         4x   4x 23x 4x 4x 4x       4x       9x 9x   9x 9x   9x 44x   11x 19x   11x 55x 55x 21x 21x 21x 6x           9x       25x   2x 2x 4x 2x           25x      
import { Connection, ParsedTransactionWithMeta } from "@solana/web3.js";
import { DEX_PROGRAMS } from "./constants";
import { DexInfo, TradeInfo } from "./types";
import { getDexInfo } from "./utils";
import { MoonshotParser } from "./parser-moonshot";
import { MeteoraParser } from "./parser-meteora";
import { PumpfunParser } from "./parser-pumpfun";
import { DefaultParser } from "./parser-default";
import { RaydiumParser } from "./parser-raydium";
import { OrcaParser } from "./parser-orca";
import { JupiterParser } from "./parser-jupiter";
 
export class TransactionParser {
  constructor(private connection: Connection) { }
 
  public async parseTransaction(signature: string): Promise<TradeInfo[]> {
    const tx = await this.connection.getParsedTransaction(signature, {
      maxSupportedTransactionVersion: 0,
    });
    return tx ? this.parseTrades(tx) : [];
  }
 
  private parseTrades(tx: ParsedTransactionWithMeta): TradeInfo[] {
    const dexInfo = getDexInfo(tx);
    Iif (!dexInfo.programId) return [];
    console.log('dexInfo', dexInfo);
    const specificParsers = {
      [DEX_PROGRAMS.JUPITER.id]: () => new JupiterParser(tx, dexInfo).processTrades(),
      [DEX_PROGRAMS.JUPITER_DCA.id]: () => new JupiterParser(tx, dexInfo).processTrades(),
      [DEX_PROGRAMS.MOONSHOT.id]: () => new MoonshotParser(tx).processTrades(),
      [DEX_PROGRAMS.METEORA.id]: () => new MeteoraParser(tx, dexInfo).processTrades(),
      [DEX_PROGRAMS.METEORA_POOLS.id]: () => new MeteoraParser(tx, dexInfo).processTrades(),
      [DEX_PROGRAMS.PUMP_FUN.id]: () => new PumpfunParser(tx).processTrades(),
    };
 
    const trades = specificParsers[dexInfo.programId]?.() ||
      this.parseRouteInstructions(tx, dexInfo);
 
    if (trades.length == 0)
      trades.push(...this.parseOuterInstructions(tx, dexInfo));
 
    Iif (trades.length == 0)
      trades.push(...new DefaultParser(tx).parseTradesByBalanceChanges(tx, dexInfo));
 
    return trades;
  }
 
  private parseOuterInstructions(tx: ParsedTransactionWithMeta, dexInfo: DexInfo): TradeInfo[] {
 
    const trades: TradeInfo[] = [];
 
    tx.transaction.message.instructions.forEach((instruction: any, index: number) => {
      if (dexInfo.programId !== instruction.programId.toBase58()) return;
      const parser = this.getParserForProgram(instruction.programId, tx, dexInfo);
      if (parser) {
        trades.push(...parser.processInstructionTrades(index));
      }
    });
 
    return trades;
  }
 
  private parseRouteInstructions(tx: ParsedTransactionWithMeta, dexInfo: DexInfo): TradeInfo[] {
    const innerInstructions = tx.meta?.innerInstructions;
    Iif (!innerInstructions) return [];
 
    const processedProtocols = new Set<string>();
    const trades: TradeInfo[] = [];
 
    tx.transaction.message.instructions.forEach((instruction: any, index: number) => {
      if (dexInfo.programId !== instruction.programId.toBase58()) return;
 
      innerInstructions
        .filter(set => set.index === index)
        .forEach(set => {
          set.instructions.forEach(innerInstruction => {
            const innerProgramId = innerInstruction.programId.toBase58();
            if (processedProtocols.has(innerProgramId)) return;
            processedProtocols.add(innerProgramId);
            const parser = this.getParserForProgram(innerProgramId, tx, dexInfo);
            if (parser) {
              trades.push(...parser.processInstructionTrades(index));
            }
          });
        });
    });
 
    return trades;
  }
 
  private getParserForProgram(programId: string, tx: ParsedTransactionWithMeta, dexInfo: DexInfo) {
    const parsers = {
      [DEX_PROGRAMS.RAYDIUM_AMM.id]: () => new RaydiumParser(tx, dexInfo),
      [DEX_PROGRAMS.RAYDIUM_CL.id]: () => new RaydiumParser(tx, dexInfo),
      [DEX_PROGRAMS.RAYDIUM_CPMM.id]: () => new RaydiumParser(tx, dexInfo),
      [DEX_PROGRAMS.RAYDIUM_V4.id]: () => new RaydiumParser(tx, dexInfo),
      [DEX_PROGRAMS.ORCA.id]: () => new OrcaParser(tx, dexInfo),
      [DEX_PROGRAMS.MOONSHOT.id]: () => new MoonshotParser(tx),
      [DEX_PROGRAMS.METEORA.id]: () => new MeteoraParser(tx, dexInfo),
      [DEX_PROGRAMS.METEORA_POOLS.id]: () => new MeteoraParser(tx, dexInfo),
      [DEX_PROGRAMS.PUMP_FUN.id]: () => new PumpfunParser(tx)
    };
    return parsers[programId]?.();
  }
 
}