From cd1d6816219213644c3dc74997e5e30966a87f83 Mon Sep 17 00:00:00 2001 From: thloyi Date: Mon, 2 Mar 2026 15:47:11 +0800 Subject: [PATCH 1/8] record ProgramFailedToComplete failed tx --- pump.go | 2 +- pumpamm.go | 4 ++-- rawtx.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pump.go b/pump.go index 353faf7..ccef571 100644 --- a/pump.go +++ b/pump.go @@ -225,7 +225,7 @@ func failedTxBuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions if tx.Err.Variant != InstructionError { return nil, increaseOffset(offset), fmt.Errorf("failed tx pump failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1]) } - if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded { + if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded && tx.Err.Enum != ProgramFailedToComplete { return nil, increaseOffset(offset), fmt.Errorf("failed tx pump failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1]) } if tx.Err.Enum == Custom { diff --git a/pumpamm.go b/pumpamm.go index ed28214..7d3c61b 100644 --- a/pumpamm.go +++ b/pumpamm.go @@ -268,7 +268,7 @@ func failedTxAmmBuyParser(tx *Tx, instruction Instruction, innerInstructions Inn if tx.Err.Variant != InstructionError { return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1]) } - if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded { + if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded && tx.Err.Enum != ProgramFailedToComplete { return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1]) } if tx.Err.Enum == Custom { @@ -392,7 +392,7 @@ func failedTxAmmSellParser(tx *Tx, instruction Instruction, innerInstructions In if tx.Err.Variant != InstructionError { return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error variant is not instruction error, offset, %d, %d", offset[0], offset[1]) } - if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded { + if tx.Err.Enum != Custom && tx.Err.Enum != ComputationalBudgetExceeded && tx.Err.Enum != ProgramFailedToComplete { return nil, increaseOffset(offset), fmt.Errorf("failed tx pump amm sell failed but error is not custom or computational budget exceeded, offset, %d, %d", offset[0], offset[1]) } if tx.Err.Enum == Custom { diff --git a/rawtx.go b/rawtx.go index 00bf158..c4dc716 100644 --- a/rawtx.go +++ b/rawtx.go @@ -354,7 +354,7 @@ func FromRpcTransactionWithMeta(tx rpc.TransactionWithMeta, blockTime *uint64, s } errDetail, ok := oErr[1].(string) if ok { - if errDetail == "ComputationalBudgetExceeded" { + if errDetail == "ComputationalBudgetExceeded" || errDetail == "ProgramFailedToComplete" { sTx.Meta.Err.Enum = ComputationalBudgetExceeded } else { sTx.Meta.Err.UnKnown = errDetail From e9ba16766f779dad3b83d8f40422b0c96e30d9f4 Mon Sep 17 00:00:00 2001 From: thloyi Date: Mon, 2 Mar 2026 18:01:32 +0800 Subject: [PATCH 2/8] chain link parser --- chainlink.go | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++ meta.go | 2 + parser.go | 5 ++- tx.go | 5 +++ 4 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 chainlink.go diff --git a/chainlink.go b/chainlink.go new file mode 100644 index 0000000..5347507 --- /dev/null +++ b/chainlink.go @@ -0,0 +1,120 @@ +package pump_parser + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "math/big" + + "github.com/gagliardetto/solana-go" + "github.com/shopspring/decimal" +) + +var ( + chainlinkSOLUSDFeedAccount = solana.MustPublicKeyFromBase58("CH31Xns5z3M1cTAbKW34jcxPPciazARpijcHj9rxtemt") + chainlinkSubmitDiscriminator = calculateDiscriminator("global:submit") +) + +func chainLinkParser(tx *Tx, instruction Instruction, inners InnerInstructions, offset [2]uint) ([2]uint, error) { + if !tx.rawTx.accountList[instruction.ProgramIDIndex].Equals(chainLinkProgram) { + return increaseOffset(offset), fmt.Errorf("system program instruction not found, block: %d, tx: %s, outerIndex: %d, innerIndex: %d", tx.rawTx.Slot, tx.rawTx.TxHash(), offset[0], offset[1]) + } + if tx.rawTx.Meta.Err != nil { + return increaseOffset(offset), nil + } + + decode := instruction.Data + discriminator := binary.LittleEndian.Uint32(decode[0:4]) + + switch discriminator { + case transferDiscriminator: + return chainLinkSubmitParser(instruction, inners, offset, tx, decode[4:]) + default: + return increaseOffset(offset), nil + } +} + +func chainLinkSubmitParser(instruction Instruction, inners InnerInstructions, offset [2]uint, tx *Tx, decodeData []byte) ([2]uint, error) { + if len(instruction.Accounts) < 6 { + return increaseOffset(offset), InstructionIgnoredError + } + + inner, err := getInnerInstructions(inners, offset[1]) + if err != nil { + return increaseOffset(offset), err + } + + if len(inner) < 1 { + return increaseOffset(offset), InstructionIgnoredError + } + storeInstruction := inner[0] + if len(storeInstruction.Accounts) < 2 { + return increaseOffset(offset), InstructionIgnoredError + } + if storeInstruction.Accounts[0] >= len(tx.rawTx.accountList) || tx.rawTx.accountList[storeInstruction.Accounts[0]] != chainlinkSOLUSDFeedAccount { + return increaseOffset(offset), InstructionIgnoredError + } + if !bytes.Equal(storeInstruction.Data[0:8], chainlinkSubmitDiscriminator[:]) { + return increaseOffset(offset), InstructionIgnoredError + } + data, err := parseChainLinkSubmitData(storeInstruction.Data) + if err != nil { + return increaseOffset(offset), InstructionIgnoredError + } + tx.ChainLink.Timestamp = int64(data.Timestamp) + tx.ChainLink.Price = decimal.NewFromBigInt(data.Price(), -8) + return increaseOffset(offset), nil +} + +type SubmitData struct { + Discriminator [8]byte + Timestamp uint64 + Answer [16]byte +} + +func parseChainLinkSubmitData(data []byte) (*SubmitData, error) { + if len(data) != 32 { + return nil, errors.New("invalid submit data length") + } + var submitData SubmitData + copy(submitData.Discriminator[:], data[:8]) + submitData.Timestamp = binary.LittleEndian.Uint64(data[8:16]) + copy(submitData.Answer[:], data[16:32]) + return &submitData, nil +} + +func (s *SubmitData) Price() *big.Int { + return int128LEBytesToBigInt(s.Answer) +} + +func int128LEBytesToBigInt(bytes [16]byte) *big.Int { + // Create new big.Int + bigInt := new(big.Int) + + // Reverse bytes for little-endian to big-endian conversion + reversed := make([]byte, 16) + for i := 0; i < 16; i++ { + reversed[15-i] = bytes[i] + } + + // Check if negative (first byte in little-endian is highest byte) + isNegative := bytes[15]&0x80 != 0 + + if isNegative { + // If negative, flip all bits + for i := range reversed { + reversed[i] = ^reversed[i] + } + // Convert to big.Int + bigInt.SetBytes(reversed) + // Add 1 and negate + bigInt.Add(bigInt, big.NewInt(1)) + bigInt.Neg(bigInt) + } else { + // If positive, convert directly + bigInt.SetBytes(reversed) + } + + return bigInt +} diff --git a/meta.go b/meta.go index 09cb4b9..a92cc59 100644 --- a/meta.go +++ b/meta.go @@ -236,4 +236,6 @@ var createAccountWithSeedDiscriminator = uint32(3) var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111") var momoProgram = solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr") +var chainLinkProgram = solana.MustPublicKeyFromBase58("cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ") + var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29} diff --git a/parser.go b/parser.go index a5379f3..cceccfa 100644 --- a/parser.go +++ b/parser.go @@ -60,8 +60,9 @@ func WithMeteoraDlmm() ParserOption { } var actionPrograms = map[solana.PublicKey]actionParser{ - systemProgram: systemParser, - budgGetProgram: budgetParser, + systemProgram: systemParser, + budgGetProgram: budgetParser, + chainLinkProgram: chainLinkParser, } func ParseRawTx(rawTx *RawTx) (*Tx, error) { diff --git a/tx.go b/tx.go index 379b89f..d06537d 100644 --- a/tx.go +++ b/tx.go @@ -74,6 +74,10 @@ type SolTransfer struct { To solana.PublicKey Amount decimal.Decimal } +type ChainLink struct { + Timestamp int64 + Price decimal.Decimal +} type Tx struct { rawTx *RawTx @@ -83,6 +87,7 @@ type Tx struct { Swaps []Swap `json:"swaps,omitempty"` SolTransfer []SolTransfer `json:"sol_transfer,omitempty"` Block uint64 `json:"block"` + ChainLink ChainLink `json:"chain_link"` BlockIndex uint64 `json:"index"` TxHash *[64]byte `json:"-"` BlockAt int64 `json:"block_at"` From 8c4b43747caf34e785423c5aac3017cf0279a47d Mon Sep 17 00:00:00 2001 From: sfng Date: Mon, 2 Mar 2026 20:04:38 +0800 Subject: [PATCH 3/8] fix EntryContractAxiom --- consts.go | 3 +++ pump.go | 12 +++++++----- pumpamm.go | 24 ++++++++++++++---------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/consts.go b/consts.go index 31fbfd6..97adaf9 100644 --- a/consts.go +++ b/consts.go @@ -360,6 +360,7 @@ var entryContractAddresses = map[solana.PublicKey]string{ solana.MustPublicKeyFromBase58("B3111yJCeHBcA1bizdJjUFPALfhAfSRnAbJzGUtnt56A"): EntryContractBinanceWallet, solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9"): EntryContractAxiom, solana.MustPublicKeyFromBase58("F5tfvbLog9VdGUPqBDTT8rgXvTTcq7e5UiGnupL1zvBq"): EntryContractAxiom, + solana.MustPublicKeyFromBase58("Gz9VPiSLQYbvKyb3jZPjNfyA6n4T4qVFUuAukgL964nL"): EntryContractAxiom, solana.MustPublicKeyFromBase58("B3jytJa6Tzpn4Ly7GNnDm3dMGqUin5aMRm5aPsJGU5G7"): EntryContractTradewiz, solana.MustPublicKeyFromBase58("DBotWvSso9oD1ZB3aHx2LiD2ZoFpF8PbKjaT4uHKLLVs"): EntryContractDbot, solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3"): EntryContractPadre, @@ -367,3 +368,5 @@ var entryContractAddresses = map[solana.PublicKey]string{ var okxDexRoutersV2 = solana.MustPublicKeyFromBase58("proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u") var okxAggregatorV2 = solana.MustPublicKeyFromBase58("6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma") + +var axiomOuterContract = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9") diff --git a/pump.go b/pump.go index ccef571..ccf7501 100644 --- a/pump.go +++ b/pump.go @@ -344,11 +344,13 @@ func BuyOrSellParser(tx *Tx, instruction Instruction, innerInstructions InnerIns if err != nil { return nil, increaseOffset(offset), fmt.Errorf("pump create get inner instructions error: %v,offset, %d, %d", err, offset[0], offset[1]) } - if instruction.StackHeight != nil && *instruction.StackHeight > 2 { - for _, innerInstr := range innerInstructions.Instructions { - if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 { - entryContract = result.accountList[innerInstr.ProgramIDIndex] - break + if !entryContract.Equals(axiomOuterContract) { + if instruction.StackHeight != nil && *instruction.StackHeight > 2 { + for _, innerInstr := range innerInstructions.Instructions { + if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 { + entryContract = result.accountList[innerInstr.ProgramIDIndex] + break + } } } } diff --git a/pumpamm.go b/pumpamm.go index 7d3c61b..474a36d 100644 --- a/pumpamm.go +++ b/pumpamm.go @@ -512,11 +512,13 @@ func ammBuyParser(tx *Tx, instruction Instruction, innerInstructions InnerInstru if err != nil { return nil, increaseOffset(offset), fmt.Errorf("pumpamm create get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen) } - if instruction.StackHeight != nil && *instruction.StackHeight > 2 { - for _, innerInstr := range innerInstructions.Instructions { - if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 { - entryContract = result.accountList[innerInstr.ProgramIDIndex] - break + if !entryContract.Equals(axiomOuterContract) { + if instruction.StackHeight != nil && *instruction.StackHeight > 2 { + for _, innerInstr := range innerInstructions.Instructions { + if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 { + entryContract = result.accountList[innerInstr.ProgramIDIndex] + break + } } } } @@ -633,11 +635,13 @@ func ammSellParser(tx *Tx, instruction Instruction, innerInstructions InnerInstr return nil, increaseOffset(offset), fmt.Errorf("pumpamm sell get inner instructions error: %v, offset: %d, %d", err, offset[0], prefixLen) } - if instruction.StackHeight != nil && *instruction.StackHeight > 2 { - for _, innerInstr := range innerInstructions.Instructions { - if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 { - entryContract = result.accountList[innerInstr.ProgramIDIndex] - break + if !entryContract.Equals(axiomOuterContract) { + if instruction.StackHeight != nil && *instruction.StackHeight > 2 { + for _, innerInstr := range innerInstructions.Instructions { + if innerInstr.StackHeight != nil && *innerInstr.StackHeight == *instruction.StackHeight-1 { + entryContract = result.accountList[innerInstr.ProgramIDIndex] + break + } } } } From 149dfae3782279302f2e6701476dd2e4fadd29d0 Mon Sep 17 00:00:00 2001 From: samlior Date: Tue, 10 Mar 2026 11:23:15 +0800 Subject: [PATCH 4/8] chore: add trojan fee address --- consts.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/consts.go b/consts.go index 97adaf9..0ce5dc2 100644 --- a/consts.go +++ b/consts.go @@ -41,6 +41,12 @@ var platformFeeAddresses = map[solana.PublicKey]string{ solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan, solana.MustPublicKeyFromBase58("92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH"): PlatformTrojan, solana.MustPublicKeyFromBase58("65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH"): PlatformTrojan, + solana.MustPublicKeyFromBase58("8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn"): PlatformTrojan, + solana.MustPublicKeyFromBase58("BJgbYMZgqm79gNrmm31tV3L8GQorw91XFm4m7evyfPjr"): PlatformTrojan, + solana.MustPublicKeyFromBase58("BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp"): PlatformTrojan, + solana.MustPublicKeyFromBase58("GV4Bt6ehW5x5dqtaWAJBSnz8uum5Z2Rp9P2Tr5iVuQn5"): PlatformTrojan, + solana.MustPublicKeyFromBase58("2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ"): PlatformTrojan, + solana.MustPublicKeyFromBase58("66N1M2aaDSdJFZ1d7YoVN4EU45ju6XiscapLVHn5FLms"): PlatformTrojan, solana.MustPublicKeyFromBase58("4mih95RmBqfHYvEfqq6uGGLp1Fr3gVS3VNSEa3JVRfQK"): PlatformRaybot, solana.MustPublicKeyFromBase58("3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N"): PlatformMoonshot, solana.MustPublicKeyFromBase58("3kxSQybWEeQZsMuNWMRJH4TxrhwoDwfv41TNMLRzFP5A"): PlatformMEVX, From 879b7fefad3a0033de2e2ec80a7157b2acdcb259 Mon Sep 17 00:00:00 2001 From: samlior Date: Thu, 12 Mar 2026 11:52:58 +0800 Subject: [PATCH 5/8] chore: add PlatformDexScreener --- consts.go | 1 + enum.go | 1 + 2 files changed, 2 insertions(+) diff --git a/consts.go b/consts.go index 0ce5dc2..b208f91 100644 --- a/consts.go +++ b/consts.go @@ -67,6 +67,7 @@ var platformFeeAddresses = map[solana.PublicKey]string{ solana.MustPublicKeyFromBase58("HkJYryz2BNeMQfuuSWDYktWt5fZLV26eK6nqu7EJycoG"): PlatformAxiom, solana.MustPublicKeyFromBase58("BfFX9rUm8qTZiZjmeq9BktWVTNuG3YWMc5AvkrCKJike"): PlatformAxiom, solana.MustPublicKeyFromBase58("2ApLdwLrGayEmxgpLX9BTR47Q2QprfMg5SpjrLeaK8s7"): PlatformAxiom, + solana.MustPublicKeyFromBase58("AVahywMVNRYzdgWrufSWrtdGXAeNEvfpJFxhVFK516mT"): PlatformDexScreener, } var mevAgentFeeAddresses = map[solana.PublicKey]string{ diff --git a/enum.go b/enum.go index 776e890..b5355b3 100644 --- a/enum.go +++ b/enum.go @@ -75,6 +75,7 @@ const ( PlatformMaestro = "maestro" PlatformBonkBot = "bonkbot" PlatformPadre = "padre" + PlatformDexScreener = "dexscreener" // used to flag transactions impersonating platform users PlatformFake = "fake" From 66f0d247f524d384d63bea9840d705678ca4a9c2 Mon Sep 17 00:00:00 2001 From: samlior Date: Thu, 12 Mar 2026 12:08:06 +0800 Subject: [PATCH 6/8] chore: add astralane fee address --- consts.go | 1 + 1 file changed, 1 insertion(+) diff --git a/consts.go b/consts.go index b208f91..0cb67ae 100644 --- a/consts.go +++ b/consts.go @@ -197,6 +197,7 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{ solana.MustPublicKeyFromBase58("ASde6y8pBCU1aityWHRpqT7pEAcEonjCgFUMeh5egRes"): MevAgentAstralane, solana.MustPublicKeyFromBase58("ASUv6G8Cj6zt71UAqD1aVtDC3CRn6FFddqF17ZiegrES"): MevAgentAstralane, solana.MustPublicKeyFromBase58("ASY4mvCtrACKFK8Jiuvqcu8fad9gGTzvfm5zp4megRes"): MevAgentAstralane, + solana.MustPublicKeyFromBase58("astraubkDw81n4LuutzSQ8uzHCv4BhPVhfvTcYv8SKC"): MevAgentAstralane, solana.MustPublicKeyFromBase58("astraEJ2fEj8Xmy6KLG7B3VfbKfsHXhHrNdCQx7iGJK"): MevAgentAstralane, solana.MustPublicKeyFromBase58("B1ooMsWjc4SUVVuLyCu1ig2RdomQnHKgMzBMfmSo3DK"): MevAgentAstralane, solana.MustPublicKeyFromBase58("B1ooMZfUJmAvppzc5cr7eYG8Cenig4FbQGBytr4DGCh"): MevAgentAstralane, From eb2bde98ac05e7574010c0d6878df47f863215f6 Mon Sep 17 00:00:00 2001 From: thloyi Date: Wed, 11 Mar 2026 14:41:56 +0800 Subject: [PATCH 7/8] update parsed tx: add swap at instr idx --- parser.go | 5 +++++ tx.go | 2 ++ 2 files changed, 7 insertions(+) diff --git a/parser.go b/parser.go index cceccfa..aea992c 100644 --- a/parser.go +++ b/parser.go @@ -127,6 +127,9 @@ func (tx *Tx) Parser() error { //fmt.Printf("parser failed tx error: %s, block: %d tx: %s\n", err, tx.Block, tx.GetTxHash()) } if len(swaps) > 0 { + for i := range swaps { + swaps[i].InstrIdx = tx.Err.Index + } tx.Swaps = swaps } for i, instr := range tx.rawTx.Transaction.Message.Instructions { @@ -172,6 +175,7 @@ func (tx *Tx) Parser() error { swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9)) } } + swap.InstrIdx = uint8(i) tx.Swaps = append(tx.Swaps, swap) } @@ -219,6 +223,7 @@ func (tx *Tx) Parser() error { swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9)) } } + swap.InstrIdx = uint8(i) tx.Swaps = append(tx.Swaps, swap) } // tx.Swaps = append(tx.Swaps, swaps...) diff --git a/tx.go b/tx.go index d06537d..cca598c 100644 --- a/tx.go +++ b/tx.go @@ -12,6 +12,8 @@ type Swap struct { TxIndex int + InstrIdx uint8 + Pool solana.PublicKey BaseMint solana.PublicKey QuoteMint solana.PublicKey From 1e276e8bd29da400954697fc79d2d18d2ffedd4e Mon Sep 17 00:00:00 2001 From: thloyi Date: Thu, 12 Mar 2026 13:48:57 +0800 Subject: [PATCH 8/8] [tx] swap inner idx --- parser.go | 1 + tx.go | 1 + 2 files changed, 2 insertions(+) diff --git a/parser.go b/parser.go index aea992c..e2453d3 100644 --- a/parser.go +++ b/parser.go @@ -224,6 +224,7 @@ func (tx *Tx) Parser() error { } } swap.InstrIdx = uint8(i) + swap.InnerIdx = uint8(j) tx.Swaps = append(tx.Swaps, swap) } // tx.Swaps = append(tx.Swaps, swaps...) diff --git a/tx.go b/tx.go index cca598c..8cc0182 100644 --- a/tx.go +++ b/tx.go @@ -13,6 +13,7 @@ type Swap struct { TxIndex int InstrIdx uint8 + InnerIdx uint8 Pool solana.PublicKey BaseMint solana.PublicKey