From a74f7690640fd7530ed0acaad609a8000fcc7d58 Mon Sep 17 00:00:00 2001 From: bijianing97 <826015751@qq.com> Date: Thu, 12 Mar 2026 16:21:38 +0800 Subject: [PATCH] Add raydiumv4 swapv2 --- meta.go | 6 ++-- raydiumv4.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/meta.go b/meta.go index 09cb4b9..66afd97 100644 --- a/meta.go +++ b/meta.go @@ -200,8 +200,10 @@ var ( const ( raydiumV4InitializePoolDiscriminator = uint8(1) - raydiumV4SwapBaseInDiscriminator = uint8(9) - raydiumV4SwapBaseOutDiscriminator = uint8(11) + raydiumV4SwapBaseInDiscriminator = uint8(9) + raydiumV4SwapBaseOutDiscriminator = uint8(11) + raydiumV4SwapBaseInV2Discriminator = uint8(16) + raydiumV4SwapBaseOutV2Discriminator = uint8(17) raydiumV4AddLiquidityDiscriminator = uint8(3) raydiumV4RemoveLiquidityDiscriminator = uint8(4) diff --git a/raydiumv4.go b/raydiumv4.go index 9dfe885..6931097 100644 --- a/raydiumv4.go +++ b/raydiumv4.go @@ -29,6 +29,8 @@ func raydiumV4Parser(tx *Tx, instruction Instruction, innerInstructions InnerIns return raydiumv4WithdrawPNLParser(tx, instruction, innerInstructions, offset) case raydiumV4SwapBaseInDiscriminator, raydiumV4SwapBaseOutDiscriminator: return raydiumv4SwapParser(tx, instruction, innerInstructions, offset) + case raydiumV4SwapBaseInV2Discriminator, raydiumV4SwapBaseOutV2Discriminator: + return raydiumv4SwapV2Parser(tx, instruction, innerInstructions, offset) default: return nil, increaseOffset(offset), InstructionIgnoredError @@ -397,3 +399,99 @@ func raydiumv4SwapParser(tx *Tx, instruction Instruction, innerInstructions Inne }, }, offset, nil } + +func raydiumv4SwapV2Parser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { + accountsLen := len(instruction.Accounts) + if accountsLen != 8 { + return nil, increaseOffset(offset), fmt.Errorf("invalid number of accounts for raydiumv4 swapv2 instruction, offset %d, %d", offset[0], offset[1]) + } + var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] + + ammAccount := tx.rawTx.accountList[instruction.Accounts[1]] + user := tx.rawTx.accountList[instruction.Accounts[7]] + userSourceTokenAccount := tx.rawTx.accountList[instruction.Accounts[5]] + userDestinationTokenAccount := tx.rawTx.accountList[instruction.Accounts[6]] + baseVaultIdx := instruction.Accounts[3] + quoteVaultIdx := instruction.Accounts[4] + + baseTokenbalance, err := getTokenBalanceAfterTx(tx.rawTx, baseVaultIdx) + if err != nil { + return nil, increaseOffset(offset), fmt.Errorf("failed to get base vault balance after tx: %v", err) + } + quoteTokenbalance, err := getTokenBalanceAfterTx(tx.rawTx, quoteVaultIdx) + if err != nil { + return nil, increaseOffset(offset), fmt.Errorf("failed to get quote vault balance after tx: %v", err) + } + inners, err := getInnerInstructions(innerInstructions, offset[1]) + if err != nil { + return nil, increaseOffset(offset), err + } + + var nextIndex int + var srcFound, destFound bool + var baseAmount, quoteAmount decimal.Decimal + var event string + for i, inner := range inners { + from, to, amount, err := parseTokenTransfer(tx.rawTx, inner) + if err != nil { + continue + } + if from.Equals(userSourceTokenAccount) && !srcFound { + if to.Equals(tx.rawTx.accountList[baseVaultIdx]) { + event = "sell" + baseAmount = decimal.NewFromUint64(amount) + srcFound = true + } else if to.Equals(tx.rawTx.accountList[quoteVaultIdx]) { + event = "buy" + quoteAmount = decimal.NewFromUint64(amount) + srcFound = true + } + } else if to.Equals(userDestinationTokenAccount) && !destFound { + if from.Equals(tx.rawTx.accountList[quoteVaultIdx]) { + event = "sell" + quoteAmount = decimal.NewFromUint64(amount) + destFound = true + } else if from.Equals(tx.rawTx.accountList[baseVaultIdx]) { + event = "buy" + baseAmount = decimal.NewFromUint64(amount) + destFound = true + } + } + if srcFound && destFound { + nextIndex = i + 1 + break + } + } + if !srcFound || !destFound { + return nil, increaseOffset(offset), fmt.Errorf("raydiumv4 failed to find token transfer inner instruction for swapv2, offset %d, %d", offset[0], offset[1]) + } + offset[1] += uint(nextIndex + 1) + + userBase := getAccountBalanceAfterTx(tx.rawTx, instruction.Accounts[5]) + userQuote := getAccountBalanceAfterTx(tx.rawTx, instruction.Accounts[6]) + baseReserve, _ := decimal.NewFromString(baseTokenbalance.UITokenAmount.Amount) + quoteReserve, _ := decimal.NewFromString(quoteTokenbalance.UITokenAmount.Amount) + + return []Swap{ + { + Program: SolProgramRaydiumV4, + Event: event, + Pool: ammAccount, + BaseMint: baseTokenbalance.MintAccount, + QuoteMint: quoteTokenbalance.MintAccount, + BaseTokenProgram: baseTokenbalance.ProgramIDAccount, + QuoteTokenProgram: quoteTokenbalance.ProgramIDAccount, + BaseMintDecimals: uint8(baseTokenbalance.UITokenAmount.Decimals), + QuoteMintDecimals: uint8(quoteTokenbalance.UITokenAmount.Decimals), + User: user, + BaseAmount: baseAmount, + QuoteAmount: quoteAmount, + BaseReserve: baseReserve, + QuoteReserve: quoteReserve, + Mayhem: false, + UserBaseBalance: userBase, + UserQuoteBalance: userQuote, + EntryContract: entryContract, + }, + }, offset, nil +}