solana-dex-parser
Version:
Solana Dex Transaction Parser
223 lines • 10.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JupiterLimitOrderV2Parser = void 0;
const borsh_1 = require("borsh");
const constants_1 = require("../../constants");
const types_1 = require("../../types");
const utils_1 = require("../../utils");
const base_parser_1 = require("../base-parser");
const jupiter_limit_layout_1 = require("./layouts/jupiter-limit.layout");
class JupiterLimitOrderV2Parser extends base_parser_1.BaseParser {
processTrades() {
const trades = [];
this.classifiedInstructions.forEach(({ instruction, programId, outerIndex, innerIndex }) => {
if (programId == constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.id) {
const data = (0, utils_1.getInstructionData)(instruction);
const discriminator = Buffer.from(data.slice(0, 16));
if (discriminator.equals(constants_1.DISCRIMINATORS.JUPITER_LIMIT_ORDER_V2.TRADE_EVENT)) {
trades.push(this.parseFlashFilled(data, outerIndex, `${outerIndex}-${innerIndex ?? 0}`));
}
}
});
return trades;
}
parseFlashFilled(data, outerIndex, idx) {
// find outer instruction
const eventInstruction = this.adapter.instructions[outerIndex];
if (!eventInstruction) {
throw new Error('Event instruction not found');
}
// parse event data
const eventData = data.slice(16);
const layout = (0, borsh_1.deserializeUnchecked)(jupiter_limit_layout_1.JupiterLimitOrderV2TradeLayout.schema, jupiter_limit_layout_1.JupiterLimitOrderV2TradeLayout, Buffer.from(eventData));
const event = layout.toObject();
// get outer instruction accounts
const accounts = this.adapter.getInstructionAccounts(eventInstruction);
const outerData = (0, utils_1.getInstructionData)(eventInstruction);
const [inputToken, outputToken] = outerData.slice(0, 8).equals(constants_1.DISCRIMINATORS.JUPITER_LIMIT_ORDER_V2.UNKNOWN)
? [
this.adapter.splTokenMap.get(accounts[3]),
this.adapter.splTokenMap.get(accounts[4]), // Unknown instruction
]
: [
this.adapter.splTokenMap.get(accounts[3]),
{ mint: accounts[8], decimals: this.adapter.splDecimalsMap.get(accounts[8]) }, // FlashFillOrder instruction
];
if (!inputToken || !outputToken) {
throw new Error('inputToken or outputToken not found');
}
const [inputMint, inputDecimal, outputMint, outputDecimal] = [
inputToken.mint,
inputToken.decimals,
outputToken.mint,
outputToken.decimals,
];
// Jupiter fee 0.1%
const feeAmount = BigInt(event.takingAmount) / 1000n;
const outAmount = BigInt(event.takingAmount) - BigInt(feeAmount);
const tradeType = (0, utils_1.getTradeType)(inputMint, outputMint);
const trade = {
type: tradeType,
Pool: [],
inputToken: {
mint: inputMint,
amount: (0, types_1.convertToUiAmount)(event.makingAmount, inputDecimal),
amountRaw: event.makingAmount.toString(),
decimals: inputDecimal ?? 0,
},
outputToken: {
mint: outputMint,
amount: (0, types_1.convertToUiAmount)(outAmount, outputDecimal),
amountRaw: outAmount.toString(),
decimals: outputDecimal ?? 0,
},
fee: {
mint: outputMint,
amount: (0, types_1.convertToUiAmount)(feeAmount, outputDecimal),
amountRaw: feeAmount.toString(),
decimals: outputDecimal ?? 0,
},
user: event.taker,
programId: constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.id,
amm: this.getAmm(),
route: this.dexInfo?.route || '',
slot: this.adapter.slot,
timestamp: this.adapter.blockTime || 0,
signature: this.adapter.signature,
idx: idx || '',
};
return this.utils.attachTokenTransferInfo(trade, this.transferActions);
}
getAmm() {
const amms = (0, utils_1.getAMMs)(Object.keys(this.transferActions));
return amms.length > 0 ? amms[0] : this.dexInfo?.amm || constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.name;
}
processTransfers() {
const transfers = [];
this.classifiedInstructions.forEach(({ instruction, programId, outerIndex, innerIndex }) => {
if (programId == constants_1.DEX_PROGRAMS.JUPITER_LIMIT_ORDER_V2.id) {
const data = (0, utils_1.getInstructionData)(instruction);
if (Buffer.from(data.slice(0, 16)).equals(constants_1.DISCRIMINATORS.JUPITER_LIMIT_ORDER_V2.CREATE_ORDER_EVENT)) {
transfers.push(...this.parseInitializeOrder(data, programId, outerIndex, `${outerIndex}-${innerIndex ?? 0}`));
}
else if (Buffer.from(data.slice(0, 8)).equals(constants_1.DISCRIMINATORS.JUPITER_LIMIT_ORDER_V2.CANCEL_ORDER)) {
transfers.push(...this.parseCancelOrder(instruction, programId, outerIndex, innerIndex));
}
}
});
// Deduplicate transfers
if (transfers.length > 1) {
return [...new Map(transfers.map((item) => [`${item.idx}-${item.signature}=${item.isFee}`, item])).values()];
}
return transfers;
}
parseInitializeOrder(data, programId, outerIndex, idx) {
// find outer instruction
const eventInstruction = this.adapter.instructions[outerIndex];
if (!eventInstruction) {
throw new Error('Event instruction not found');
}
// parse event data
const eventData = data.slice(16);
const event = jupiter_limit_layout_1.JupiterLimitOrderV2CreateOrderLayout.deserialize(eventData).toObject();
// get outer instruction accounts
const accounts = this.adapter.getInstructionAccounts(eventInstruction);
const user = event.maker;
const [source, destination] = [accounts[4], accounts[3]];
const balance = event.inputMint == constants_1.TOKENS.SOL
? this.adapter.getAccountSolBalanceChanges().get(user)
: this.adapter.getAccountTokenBalanceChanges(true).get(user)?.get(event.inputMint);
if (!balance)
return [];
const decimals = this.adapter.getTokenDecimals(event.inputMint);
return [
{
type: 'initializeOrder',
programId: programId,
info: {
authority: this.adapter.getTokenAccountOwner(source) || user,
source: source,
destination: destination,
destinationOwner: this.adapter.getTokenAccountOwner(source),
mint: event.inputMint,
tokenAmount: {
amount: event.makingAmount.toString(),
uiAmount: (0, types_1.convertToUiAmount)(event.makingAmount, decimals),
decimals: decimals,
},
sourceBalance: balance.post,
sourcePreBalance: balance.pre,
},
idx: idx,
timestamp: this.adapter.blockTime,
signature: this.adapter.signature,
},
];
}
parseCancelOrder(instruction, programId, outerIndex, innerIndex) {
// get instruction accounts
const accounts = this.adapter.getInstructionAccounts(instruction);
const [user, mint, source, authority] = [accounts[1], accounts[5], accounts[3], accounts[2]];
const destination = mint == constants_1.TOKENS.SOL ? user : accounts[4];
const balance = mint == constants_1.TOKENS.SOL
? this.adapter.getAccountSolBalanceChanges().get(destination)
: this.adapter.getAccountTokenBalanceChanges().get(destination)?.get(mint);
if (!balance)
throw new Error('Balance not found');
const transfers = this.getTransfersForInstruction(programId, outerIndex, innerIndex);
const transfer = transfers.find((t) => t.info.mint == mint);
const decimals = transfer?.info.tokenAmount.decimals || this.adapter.getTokenDecimals(mint);
const tokenAmount = transfer?.info.tokenAmount.amount || balance.change.amount || '0';
const tokens = [];
tokens.push({
type: 'cancelOrder',
programId: programId,
info: {
authority: transfer?.info.authority || authority,
source: transfer?.info.source || source,
destination: transfer ? transfer?.info.destination || destination : user,
destinationOwner: this.adapter.getTokenAccountOwner(destination),
mint: mint,
tokenAmount: {
amount: tokenAmount,
uiAmount: (0, types_1.convertToUiAmount)(tokenAmount, decimals),
decimals: decimals,
},
destinationBalance: balance.post,
destinationPreBalance: balance.pre,
},
idx: `${outerIndex}-${innerIndex ?? 0}`,
timestamp: this.adapter.blockTime,
signature: this.adapter.signature,
});
if (mint !== constants_1.TOKENS.SOL) {
const solBalance = this.adapter.getAccountSolBalanceChanges().get(user);
if (solBalance) {
tokens.push({
type: 'cancelOrder',
programId: programId,
info: {
authority: transfer?.info.authority || authority,
source: transfer?.info.source || source,
destination: user,
mint: constants_1.TOKENS.SOL,
tokenAmount: {
amount: solBalance.change.amount,
uiAmount: solBalance.change.uiAmount || 0,
decimals: solBalance.change.decimals,
},
destinationBalance: solBalance.post,
destinationPreBalance: solBalance.pre,
},
idx: `${outerIndex}-${innerIndex ?? 0}`,
timestamp: this.adapter.blockTime,
signature: this.adapter.signature,
isFee: true,
});
}
}
return tokens;
}
}
exports.JupiterLimitOrderV2Parser = JupiterLimitOrderV2Parser;
//# sourceMappingURL=parser-jupiter-limit-v2.js.map