all parser
This commit is contained in:
375
raydiumclmm.go
Normal file
375
raydiumclmm.go
Normal file
@@ -0,0 +1,375 @@
|
||||
package pump_parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func raydiumClmmParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(raydiumClmmProgramID) {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("raydiumClmm instruction not found, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
|
||||
decode := instruction.Data
|
||||
if len(decode) < 8 {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("raydiumClmm program instruction data too short, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
|
||||
discriminator := *(*[8]byte)(decode[:8])
|
||||
|
||||
switch discriminator {
|
||||
case raydiumClmmCreatePoolDiscriminator:
|
||||
return raydiumClmmCreatePoolParser(tx, instruction, innerInstructions, offset)
|
||||
case raydiumClmmIncreaseLiquidityDiscriminator, raydiumClmmIncreaseLiquidityV2Discriminator, raydiumClmmOpenPositionDiscriminator, raydiumClmmOpenPositionV2Discriminator, raydiumClmmOpenPositionWithToken22NftDiscriminator:
|
||||
return raydiumClmmAddLiquidityParser(tx, instruction, innerInstructions, offset)
|
||||
case raydiumClmmDecreaseLiquidityDiscriminator, raydiumClmmDecreaseLiquidityV2Discriminator:
|
||||
return raydiumClmmDecreaseLiquidityParser(tx, instruction, innerInstructions, offset)
|
||||
case raydiumClmmCollectFundFeeDiscriminator, raydiumClmmCollectProtocolFeeDiscriminator:
|
||||
return raydiumClmmCollectFeeParser(tx, instruction, innerInstructions, offset)
|
||||
case raydiumClmmSwapDiscriminator, raydiumClmmSwapV2Discriminator:
|
||||
return raydiumClmmSwapParser(tx, instruction, innerInstructions, offset)
|
||||
default:
|
||||
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
}
|
||||
|
||||
func raydiumClmmCreatePoolParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
if len(instruction.Accounts) < 13 {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for raydiumClmm create pool instruction, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||
|
||||
pool := tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
creator := tx.rawTx.accountList[instruction.Accounts[0]]
|
||||
|
||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, instruction.Accounts[5])
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, instruction.Accounts[6])
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
baseReserve, _ := decimal.NewFromString(baseTokenBalance.UITokenAmount.Amount)
|
||||
quoteReserve, _ := decimal.NewFromString(quoteTokenBalance.UITokenAmount.Amount)
|
||||
offset[1] += 9
|
||||
return []Swap{
|
||||
{
|
||||
Program: SolProgramRaydiumCLMM,
|
||||
Event: "create",
|
||||
Pool: pool,
|
||||
BaseMint: baseTokenBalance.MintAccount,
|
||||
QuoteMint: quoteTokenBalance.MintAccount,
|
||||
BaseTokenProgram: baseTokenBalance.ProgramIDAccount,
|
||||
QuoteTokenProgram: quoteTokenBalance.ProgramIDAccount,
|
||||
Creator: creator,
|
||||
BaseMintDecimals: uint8(baseTokenBalance.UITokenAmount.Decimals),
|
||||
QuoteMintDecimals: uint8(quoteTokenBalance.UITokenAmount.Decimals),
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
User: tx.rawTx.accountList[instruction.Accounts[0]],
|
||||
EntryContract: entryContract,
|
||||
},
|
||||
}, offset, nil
|
||||
}
|
||||
|
||||
func raydiumClmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
discriminator := *(*[8]byte)(instruction.Data[:8])
|
||||
var (
|
||||
accountMin int
|
||||
market solana.PublicKey
|
||||
//token0 solana.PublicKey
|
||||
//token1 solana.PublicKey
|
||||
lpToken solana.PublicKey
|
||||
vault0 int
|
||||
vault1 int
|
||||
)
|
||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||
|
||||
switch discriminator {
|
||||
case raydiumClmmIncreaseLiquidityDiscriminator:
|
||||
accountMin = 12
|
||||
market = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
vault0 = instruction.Accounts[9]
|
||||
vault1 = instruction.Accounts[10]
|
||||
case raydiumClmmIncreaseLiquidityV2Discriminator:
|
||||
accountMin = 15
|
||||
market = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
vault0 = instruction.Accounts[9]
|
||||
vault1 = instruction.Accounts[10]
|
||||
//token0 = tx.rawTx.accountList[instruction.Accounts[13]]
|
||||
//token1 = tx.rawTx.accountList[instruction.Accounts[14]]
|
||||
case raydiumClmmOpenPositionDiscriminator:
|
||||
accountMin = 19
|
||||
market = tx.rawTx.accountList[instruction.Accounts[5]]
|
||||
vault0 = instruction.Accounts[12]
|
||||
vault1 = instruction.Accounts[13]
|
||||
lpToken = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
case raydiumClmmOpenPositionV2Discriminator:
|
||||
accountMin = 22
|
||||
market = tx.rawTx.accountList[instruction.Accounts[5]]
|
||||
vault0 = instruction.Accounts[12]
|
||||
vault1 = instruction.Accounts[13]
|
||||
lpToken = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
//token0 = tx.rawTx.accountList[instruction.Accounts[20]]
|
||||
//token1 = tx.rawTx.accountList[instruction.Accounts[21]]
|
||||
case raydiumClmmOpenPositionWithToken22NftDiscriminator:
|
||||
accountMin = 20
|
||||
market = tx.rawTx.accountList[instruction.Accounts[4]]
|
||||
vault0 = instruction.Accounts[11]
|
||||
vault1 = instruction.Accounts[12]
|
||||
lpToken = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
//token0 = tx.rawTx.accountList[instruction.Accounts[18]]
|
||||
//token1 = tx.rawTx.accountList[instruction.Accounts[19]]
|
||||
default:
|
||||
return nil, increaseOffset(offset), fmt.Errorf("invalid discriminator")
|
||||
}
|
||||
|
||||
if len(instruction.Accounts) < accountMin {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for raydiumClmm add liquidity instruction, offset, %d, %d", offset[0], offset[1])
|
||||
}
|
||||
|
||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault0)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault1)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
baseReserve, _ := decimal.NewFromString(baseTokenBalance.UITokenAmount.Amount)
|
||||
quoteReserve, _ := decimal.NewFromString(quoteTokenBalance.UITokenAmount.Amount)
|
||||
|
||||
offset[1] += 2
|
||||
|
||||
return []Swap{
|
||||
{
|
||||
Program: SolProgramRaydiumCLMM,
|
||||
Event: "add_liquidity",
|
||||
Pool: market,
|
||||
BaseMint: baseTokenBalance.MintAccount,
|
||||
QuoteMint: quoteTokenBalance.MintAccount,
|
||||
BaseTokenProgram: baseTokenBalance.ProgramIDAccount,
|
||||
QuoteTokenProgram: quoteTokenBalance.ProgramIDAccount,
|
||||
LpMint: lpToken,
|
||||
BaseMintDecimals: uint8(baseTokenBalance.UITokenAmount.Decimals),
|
||||
QuoteMintDecimals: uint8(quoteTokenBalance.UITokenAmount.Decimals),
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
User: tx.rawTx.accountList[instruction.Accounts[0]],
|
||||
EntryContract: entryContract,
|
||||
},
|
||||
}, offset, nil
|
||||
}
|
||||
|
||||
func raydiumClmmDecreaseLiquidityParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
discriminator := *(*[8]byte)(instruction.Data[:8])
|
||||
var (
|
||||
accountMin int
|
||||
market solana.PublicKey
|
||||
vault0 int
|
||||
vault1 int
|
||||
)
|
||||
|
||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||
if discriminator == raydiumClmmDecreaseLiquidityDiscriminator {
|
||||
accountMin = 14
|
||||
} else if discriminator == raydiumClmmDecreaseLiquidityV2Discriminator {
|
||||
accountMin = 16
|
||||
}
|
||||
if len(instruction.Accounts) < accountMin {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for decrease liquidity instruction")
|
||||
}
|
||||
market = tx.rawTx.accountList[instruction.Accounts[3]]
|
||||
vault0 = instruction.Accounts[5]
|
||||
vault1 = instruction.Accounts[6]
|
||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault0)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault1)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
baseReserve, _ := decimal.NewFromString(baseTokenBalance.UITokenAmount.Amount)
|
||||
quoteReserve, _ := decimal.NewFromString(quoteTokenBalance.UITokenAmount.Amount)
|
||||
|
||||
offset[1] += 2
|
||||
|
||||
return []Swap{
|
||||
{
|
||||
Program: SolProgramRaydiumCLMM,
|
||||
Event: "remove_liquidity",
|
||||
Pool: market,
|
||||
BaseMint: baseTokenBalance.MintAccount,
|
||||
QuoteMint: quoteTokenBalance.MintAccount,
|
||||
BaseTokenProgram: baseTokenBalance.ProgramIDAccount,
|
||||
QuoteTokenProgram: quoteTokenBalance.ProgramIDAccount,
|
||||
BaseMintDecimals: uint8(baseTokenBalance.UITokenAmount.Decimals),
|
||||
QuoteMintDecimals: uint8(quoteTokenBalance.UITokenAmount.Decimals),
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
User: tx.rawTx.accountList[instruction.Accounts[0]],
|
||||
EntryContract: entryContract,
|
||||
},
|
||||
}, offset, nil
|
||||
}
|
||||
|
||||
func raydiumClmmCollectFeeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
if len(instruction.Accounts) < 11 {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for CollectFeeParser instruction")
|
||||
}
|
||||
pool := tx.rawTx.accountList[instruction.Accounts[1]]
|
||||
|
||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||
vault0 := instruction.Accounts[3]
|
||||
vault1 := instruction.Accounts[4]
|
||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault0)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token0 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, vault1)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get token1 vault balance after tx: %v", err)
|
||||
}
|
||||
|
||||
baseReserve, _ := decimal.NewFromString(baseTokenBalance.UITokenAmount.Amount)
|
||||
quoteReserve, _ := decimal.NewFromString(quoteTokenBalance.UITokenAmount.Amount)
|
||||
|
||||
offset[1] += 2
|
||||
|
||||
return []Swap{
|
||||
{
|
||||
Program: SolProgramRaydiumCLMM,
|
||||
Event: "remove_liquidity",
|
||||
Pool: pool,
|
||||
BaseMint: baseTokenBalance.MintAccount,
|
||||
QuoteMint: quoteTokenBalance.MintAccount,
|
||||
BaseTokenProgram: baseTokenBalance.ProgramIDAccount,
|
||||
QuoteTokenProgram: quoteTokenBalance.ProgramIDAccount,
|
||||
BaseMintDecimals: uint8(baseTokenBalance.UITokenAmount.Decimals),
|
||||
QuoteMintDecimals: uint8(quoteTokenBalance.UITokenAmount.Decimals),
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
User: tx.rawTx.accountList[instruction.Accounts[0]],
|
||||
EntryContract: entryContract,
|
||||
},
|
||||
}, offset, nil
|
||||
}
|
||||
|
||||
func raydiumClmmSwapParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
discriminator := *(*[8]byte)(instruction.Data[:8])
|
||||
var (
|
||||
pool solana.PublicKey
|
||||
|
||||
accountMin int
|
||||
tokenInVault int
|
||||
tokenOutVault int
|
||||
userTokenInAccount int
|
||||
userTokenOutAccount int
|
||||
)
|
||||
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||
if discriminator == raydiumClmmSwapDiscriminator {
|
||||
accountMin = 9
|
||||
pool = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
userTokenInAccount = instruction.Accounts[3]
|
||||
userTokenOutAccount = instruction.Accounts[4]
|
||||
tokenInVault = instruction.Accounts[5]
|
||||
tokenOutVault = instruction.Accounts[6]
|
||||
} else if discriminator == raydiumClmmSwapV2Discriminator {
|
||||
accountMin = 13
|
||||
pool = tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
userTokenInAccount = instruction.Accounts[3]
|
||||
userTokenOutAccount = instruction.Accounts[4]
|
||||
tokenInVault = instruction.Accounts[5]
|
||||
tokenOutVault = instruction.Accounts[6]
|
||||
}
|
||||
if len(instruction.Accounts) < accountMin {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("not enough accounts for swap instruction")
|
||||
}
|
||||
|
||||
baseTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, tokenInVault)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get tokenIn vault balance after tx: %w", err)
|
||||
}
|
||||
quoteTokenBalance, err := getTokenBalanceAfterTx(tx.rawTx, tokenOutVault)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get tokenOut vault balance after tx: %w", err)
|
||||
}
|
||||
baseReserve, _ := decimal.NewFromString(baseTokenBalance.UITokenAmount.Amount)
|
||||
quoteReserve, _ := decimal.NewFromString(quoteTokenBalance.UITokenAmount.Amount)
|
||||
baseTokenProgram := baseTokenBalance.ProgramIDAccount
|
||||
quoteTokenProgram := quoteTokenBalance.ProgramIDAccount
|
||||
baseMint := baseTokenBalance.MintAccount
|
||||
quoteMint := quoteTokenBalance.MintAccount
|
||||
baseMintDecimals := uint8(baseTokenBalance.UITokenAmount.Decimals)
|
||||
quoteMintDecimals := uint8(quoteTokenBalance.UITokenAmount.Decimals)
|
||||
|
||||
userBase := getAccountBalanceAfterTx(tx.rawTx, userTokenInAccount)
|
||||
userQuote := getAccountBalanceAfterTx(tx.rawTx, userTokenOutAccount)
|
||||
|
||||
inners, err := getInnerInstructions(innerInstructions, offset[1])
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to get inner instructions: %w", err)
|
||||
}
|
||||
if len(inners) < 2 {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("not enough inner instructions for swap instruction")
|
||||
}
|
||||
baseVaultAccount := tx.rawTx.accountList[tokenInVault]
|
||||
quoteVaultAccount := tx.rawTx.accountList[tokenOutVault]
|
||||
userBaseAccount := tx.rawTx.accountList[userTokenInAccount]
|
||||
userQuoteAccount := tx.rawTx.accountList[userTokenOutAccount]
|
||||
var baseAmount, quoteAmount decimal.Decimal
|
||||
var baseFound, quoteFound bool
|
||||
for i := 0; i < 2; i++ {
|
||||
inner := inners[i]
|
||||
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to parse token transfer: %w", err)
|
||||
}
|
||||
if from.Equals(userBaseAccount) && to.Equals(baseVaultAccount) && !baseFound {
|
||||
baseAmount = decimal.NewFromUint64(amount)
|
||||
baseFound = true
|
||||
} else if from.Equals(quoteVaultAccount) && to.Equals(userQuoteAccount) && !quoteFound {
|
||||
quoteAmount = decimal.NewFromUint64(amount)
|
||||
quoteFound = true
|
||||
}
|
||||
}
|
||||
if !baseFound || !quoteFound {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("failed to find token transfer in inner instructions")
|
||||
}
|
||||
|
||||
offset[1] += 2
|
||||
|
||||
return []Swap{
|
||||
{
|
||||
Program: SolProgramRaydiumCLMM,
|
||||
Event: "sell",
|
||||
Pool: pool,
|
||||
BaseMint: baseMint,
|
||||
QuoteMint: quoteMint,
|
||||
BaseTokenProgram: baseTokenProgram,
|
||||
QuoteTokenProgram: quoteTokenProgram,
|
||||
BaseMintDecimals: baseMintDecimals,
|
||||
QuoteMintDecimals: quoteMintDecimals,
|
||||
User: tx.rawTx.accountList[instruction.Accounts[0]],
|
||||
BaseAmount: baseAmount,
|
||||
QuoteAmount: quoteAmount,
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
UserBaseBalance: userBase,
|
||||
UserQuoteBalance: userQuote,
|
||||
EntryContract: entryContract,
|
||||
},
|
||||
}, offset, nil
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user