All files / src/parsers parser-raydium-liquidity-base.ts

27.77% Statements 10/36
3.44% Branches 1/29
33.33% Functions 2/6
28.12% Lines 9/32

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 107 108 109 110 111 112 113 1141x     1x                         1x   1x 1x             1x 1x 1x 1x                                                                                                                                                                        
import { convertToUiAmount, PoolEvent, PoolEventType, TransferData } from '../types';
import { TransactionAdapter } from '../transaction-adapter';
import { TransactionUtils } from '../transaction-utils';
import { getInstructionData } from '../utils';
 
export interface ParseEventConfig {
    eventType: PoolEventType;
    poolIdIndex: number;
    lpMintIndex: number;
    tokenAmountOffsets?: {
        token0: number;
        token1: number;
        lp: number;
    };
}
 
export abstract class RaydiumLiquidityParserBase {
 
    constructor(protected readonly adapter: TransactionAdapter,
        private readonly utils: TransactionUtils
    ) {
    }
 
    abstract getPoolAction(data: Buffer): PoolEventType | { name: string; type: PoolEventType } | null;
 
    public parseRaydiumInstruction(instruction: any, index: number, innerIndex?: number): PoolEvent | null {
        try {
            const data = getInstructionData(instruction);
            const instructionType = this.getPoolAction(data);
            if (!instructionType) return null;
 
            const type = typeof instructionType === 'string' ? instructionType : instructionType.type;
            const transfers = this.getTransfersForInstruction(instruction, index, innerIndex);
            const config = this.getEventConfig(type, instructionType);
 
            Iif (!config) return null;
            return this.parseEvent(instruction, index, data, transfers, config);
        } catch (error) {
            console.error('parseRaydiumInstruction error:', error);
            return null;
        }
    }
 
    protected abstract getEventConfig(
        type: PoolEventType,
        instructionType: PoolEventType | { name: string; type: PoolEventType }
    ): ParseEventConfig | null;
 
    protected parseEvent(
        instruction: any,
        index: number,
        data: Buffer,
        transfers: TransferData[],
        config: ParseEventConfig
    ): PoolEvent | null {
        Iif (config.eventType === 'ADD' && transfers.length < 2) return null;
 
        const [token0, token1] = this.utils.getLPTransfers(transfers);
        const lpToken = transfers.find((it) => it.type === (config.eventType === 'REMOVE' ? 'burn' : 'mintTo'));
        const programId = this.adapter.getInstructionProgramId(instruction);
        const accounts = this.adapter.getInstructionAccounts(instruction);
 
        const token0Mint = token0?.info.mint;
        const token1Mint = token1?.info.mint;
        const [token0Decimals, token1Decimals] = [
            this.adapter.getTokenDecimals(token0Mint),
            this.adapter.getTokenDecimals(token1Mint)
        ];
 
        const token0Amount = token0?.info.tokenAmount.uiAmount ||
            (config.tokenAmountOffsets && convertToUiAmount(data.readBigUInt64LE(config.tokenAmountOffsets.token0), token0Decimals)) ||
            0;
 
        const token1Amount = token1?.info.tokenAmount.uiAmount ||
            (config.tokenAmountOffsets && convertToUiAmount(data.readBigUInt64LE(config.tokenAmountOffsets.token1), token1Decimals)) ||
            0;
 
        const lpAmount = lpToken?.info.tokenAmount.uiAmount ||
            (config.tokenAmountOffsets && convertToUiAmount(data.readBigUInt64LE(config.tokenAmountOffsets.lp))) ||
            0;
 
        return {
            ...this.adapter.getPoolEventBase(config.eventType, programId),
            idx: index.toString(),
            poolId: accounts[config.poolIdIndex],
            poolLpMint: lpToken?.info.mint || accounts[config.lpMintIndex],
            token0Mint,
            token1Mint,
            token0Amount,
            token1Amount,
            token0Decimals,
            token1Decimals,
            lpAmount,
        };
    }
 
    protected getTransfersForInstruction(
        instruction: any,
        index: number,
        innerIndex?: number,
        filterTypes: string[] = ['mintTo', 'burn']
    ): TransferData[] {
        const curIdx = innerIndex === undefined ? index.toString() : `${index}-${innerIndex}`;
        const accounts = this.adapter.getInstructionAccounts(instruction);
 
        return this.utils
            .processTransferInstructions(index, filterTypes)
            .filter((it) =>
                it.info.authority.length > 0 &&
                accounts.includes(it.info.destination) &&
                it.idx >= curIdx
            );
    }
}