solana-dex-parser
Version:
Solana Dex Transaction Parser
203 lines (188 loc) • 7.23 kB
text/typescript
import { DEX_PROGRAMS, DISCRIMINATORS } from '../../constants';
import { InstructionClassifier } from '../../instruction-classifier';
import { TransactionAdapter } from '../../transaction-adapter';
import {
ClassifiedInstruction,
EventParser,
PumpswapBuyEvent,
PumpswapCreatePoolEvent,
PumpswapDepositEvent,
PumpswapEvent,
PumpswapSellEvent,
PumpswapWithdrawEvent,
} from '../../types';
import { getInstructionData } from '../../utils';
import { BinaryReader } from '../binary-reader';
export class PumpswapEventParser {
constructor(private readonly adapter: TransactionAdapter) {}
private readonly eventParsers: Record<string, EventParser<any>> = {
CREATE: {
discriminator: DISCRIMINATORS.PUMPSWAP.CREATE_POOL,
decode: this.decodeCreateEvent.bind(this),
},
ADD: {
discriminator: DISCRIMINATORS.PUMPSWAP.ADD_LIQUIDITY,
decode: this.decodeAddLiquidity.bind(this),
},
REMOVE: {
discriminator: DISCRIMINATORS.PUMPSWAP.REMOVE_LIQUIDITY,
decode: this.decodeRemoveLiquidity.bind(this),
},
BUY: {
discriminator: DISCRIMINATORS.PUMPSWAP.BUY,
decode: this.decodeBuyEvent.bind(this),
},
SELL: {
discriminator: DISCRIMINATORS.PUMPSWAP.SELL,
decode: this.decodeSellEvent.bind(this),
},
};
public processEvents(): PumpswapEvent[] {
const instructions = new InstructionClassifier(this.adapter).getInstructions(DEX_PROGRAMS.PUMP_SWAP.id);
return this.parseInstructions(instructions);
}
public parseInstructions(instructions: ClassifiedInstruction[]): PumpswapEvent[] {
return instructions
.map(({ instruction, outerIndex, innerIndex }) => {
try {
const data = getInstructionData(instruction);
const discriminator = Buffer.from(data.slice(0, 16));
for (const [type, parser] of Object.entries(this.eventParsers)) {
if (discriminator.equals(parser.discriminator)) {
const eventData = parser.decode(data.slice(16));
if (!eventData) return null;
const event = {
type: type as 'CREATE' | 'ADD' | 'REMOVE' | 'BUY' | 'SELL',
data: eventData,
slot: this.adapter.slot,
timestamp: this.adapter.blockTime || 0,
signature: this.adapter.signature,
idx: `${outerIndex}-${innerIndex ?? 0}`,
};
return event;
}
}
} catch (error) {
console.error('Failed to parse Pumpswap event:', error);
}
return null;
})
.filter((event): event is PumpswapEvent => event !== null);
}
private decodeBuyEvent(data: Buffer): PumpswapBuyEvent {
const reader = new BinaryReader(data);
return {
timestamp: Number(reader.readI64()),
baseAmountOut: reader.readU64(),
maxQuoteAmountIn: reader.readU64(),
userBaseTokenReserves: reader.readU64(),
userQuoteTokenReserves: reader.readU64(),
poolBaseTokenReserves: reader.readU64(),
poolQuoteTokenReserves: reader.readU64(),
quoteAmountIn: reader.readU64(),
lpFeeBasisPoints: reader.readU64(),
lpFee: reader.readU64(),
protocolFeeBasisPoints: reader.readU64(),
protocolFee: reader.readU64(),
quoteAmountInWithLpFee: reader.readU64(),
userQuoteAmountIn: reader.readU64(),
pool: reader.readPubkey(),
user: reader.readPubkey(),
userBaseTokenAccount: reader.readPubkey(),
userQuoteTokenAccount: reader.readPubkey(),
protocolFeeRecipient: reader.readPubkey(),
protocolFeeRecipientTokenAccount: reader.readPubkey(),
};
}
private decodeSellEvent(data: Buffer): PumpswapSellEvent {
const reader = new BinaryReader(data);
return {
timestamp: Number(reader.readI64()),
baseAmountIn: reader.readU64(),
minQuoteAmountOut: reader.readU64(),
userBaseTokenReserves: reader.readU64(),
userQuoteTokenReserves: reader.readU64(),
poolBaseTokenReserves: reader.readU64(),
poolQuoteTokenReserves: reader.readU64(),
quoteAmountOut: reader.readU64(),
lpFeeBasisPoints: reader.readU64(),
lpFee: reader.readU64(),
protocolFeeBasisPoints: reader.readU64(),
protocolFee: reader.readU64(),
quoteAmountOutWithoutLpFee: reader.readU64(),
userQuoteAmountOut: reader.readU64(),
pool: reader.readPubkey(),
user: reader.readPubkey(),
userBaseTokenAccount: reader.readPubkey(),
userQuoteTokenAccount: reader.readPubkey(),
protocolFeeRecipient: reader.readPubkey(),
protocolFeeRecipientTokenAccount: reader.readPubkey(),
};
}
private decodeAddLiquidity(data: Buffer): PumpswapDepositEvent {
const reader = new BinaryReader(data);
return {
timestamp: Number(reader.readI64()),
lpTokenAmountOut: reader.readU64(),
maxBaseAmountIn: reader.readU64(),
maxQuoteAmountIn: reader.readU64(),
userBaseTokenReserves: reader.readU64(),
userQuoteTokenReserves: reader.readU64(),
poolBaseTokenReserves: reader.readU64(),
poolQuoteTokenReserves: reader.readU64(),
baseAmountIn: reader.readU64(),
quoteAmountIn: reader.readU64(),
lpMintSupply: reader.readU64(),
pool: reader.readPubkey(),
user: reader.readPubkey(),
userBaseTokenAccount: reader.readPubkey(),
userQuoteTokenAccount: reader.readPubkey(),
userPoolTokenAccount: reader.readPubkey(),
};
}
private decodeCreateEvent(data: Buffer): PumpswapCreatePoolEvent {
const reader = new BinaryReader(data);
return {
timestamp: Number(reader.readI64()),
index: reader.readU16(),
creator: reader.readPubkey(),
baseMint: reader.readPubkey(),
quoteMint: reader.readPubkey(),
baseMintDecimals: reader.readU8(),
quoteMintDecimals: reader.readU8(),
baseAmountIn: reader.readU64(),
quoteAmountIn: reader.readU64(),
poolBaseAmount: reader.readU64(),
poolQuotAmount: reader.readU64(),
minimumLiquidity: reader.readU64(),
initialLiquidity: reader.readU64(),
lpTokenAmountOut: reader.readU64(),
poolBump: reader.readU8(),
pool: reader.readPubkey(),
lpMint: reader.readPubkey(),
userBaseTokenAccount: reader.readPubkey(),
userQuoteTokenAccount: reader.readPubkey(),
};
}
private decodeRemoveLiquidity(data: Buffer): PumpswapWithdrawEvent {
const reader = new BinaryReader(data);
return {
timestamp: Number(reader.readI64()),
lpTokenAmountIn: reader.readU64(),
minBaseAmountOut: reader.readU64(),
minQuoteAmountOut: reader.readU64(),
userBaseTokenReserves: reader.readU64(),
userQuoteTokenReserves: reader.readU64(),
poolBaseTokenReserves: reader.readU64(),
poolQuoteTokenReserves: reader.readU64(),
baseAmountOut: reader.readU64(),
quoteAmountOut: reader.readU64(),
lpMintSupply: reader.readU64(),
pool: reader.readPubkey(),
user: reader.readPubkey(),
userBaseTokenAccount: reader.readPubkey(),
userQuoteTokenAccount: reader.readPubkey(),
userPoolTokenAccount: reader.readPubkey(),
};
}
}