From 09de6ba649f20db03f9a14403828a1ac96026956 Mon Sep 17 00:00:00 2001 From: bijianing97 <826015751@qq.com> Date: Mon, 16 Mar 2026 11:37:19 +0800 Subject: [PATCH] Add dlmm claim fee and update dlmm add and remove --- meta.go | 6 + metaoradlmm.go | 524 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 530 insertions(+) diff --git a/meta.go b/meta.go index 445367f..1cce54e 100644 --- a/meta.go +++ b/meta.go @@ -81,11 +81,17 @@ var ( meteoraDlmmAddLiquidity2Discriminator = calculateDiscriminator("global:add_liquidity2") meteoraDlmmAddLiquidityByStrategyDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy") meteoraDlmmAddLiquidityByStrategy2Discriminator = calculateDiscriminator("global:add_liquidity_by_strategy2") + meteoraDlmmClaimFeeDiscriminator = calculateDiscriminator("global:claim_fee") + meteoraDlmmClaimFee2Discriminator = calculateDiscriminator("global:claim_fee2") + meteoraDlmmRebalanceLiquidityDiscriminator = calculateDiscriminator("global:rebalance_liquidity") meteoraDlmmRemoveLiquidityDiscriminator = calculateDiscriminator("global:remove_liquidity") meteoraDlmmRemoveLiquidity2Discriminator = calculateDiscriminator("global:remove_liquidity2") meteoraDlmmRemoveLiquidityByRangeDiscriminator = calculateDiscriminator("global:remove_liquidity_by_range") meteoraDlmmRemoveLiquidityByRange2Discriminator = calculateDiscriminator("global:remove_liquidity_by_range2") meteoraDlmmAddLiquidityEventDiscriminator = calculateDiscriminator("event:AddLiquidity") + meteoraDlmmClaimFeeEventDiscriminator = calculateDiscriminator("event:ClaimFee") + meteoraDlmmClaimFee2EventDiscriminator = calculateDiscriminator("event:ClaimFee2") + meteoraDlmmRebalancingEventDiscriminator = calculateDiscriminator("event:Rebalancing") meteoraDlmmRemoveLiquidityEventDiscriminator = calculateDiscriminator("event:RemoveLiquidity") ) diff --git a/metaoradlmm.go b/metaoradlmm.go index 5aa9fa4..024d073 100644 --- a/metaoradlmm.go +++ b/metaoradlmm.go @@ -55,6 +55,51 @@ type dlmmRemoveLiquidityEvent struct { ActiveBinId int32 } +type dlmmClaimFeeInnerEvent struct { + LbPair solana.PublicKey + Position solana.PublicKey + Owner solana.PublicKey + FeeX uint64 + FeeY uint64 + ActiveBinId int32 + HasActiveBin bool +} + +type dlmmClaimFeeEvent struct { + LbPair solana.PublicKey + Position solana.PublicKey + Owner solana.PublicKey + FeeX uint64 + FeeY uint64 +} + +type dlmmClaimFee2Event struct { + LbPair solana.PublicKey + Position solana.PublicKey + Owner solana.PublicKey + FeeX uint64 + FeeY uint64 + ActiveBinId int32 +} + +type dlmmRebalancingEvent struct { + LbPair solana.PublicKey + Position solana.PublicKey + Owner solana.PublicKey + ActiveBinId int32 + XWithdrawnAmount uint64 + XAddedAmount uint64 + YWithdrawnAmount uint64 + YAddedAmount uint64 + XFeeAmount uint64 + YFeeAmount uint64 + OldMinBinId int32 + OldMaxBinId int32 + NewMinBinId int32 + NewMaxBinId int32 + Rewards [2]uint64 +} + type dlmmBinLiquidityDistribution struct { BinId int32 DistributionX uint16 @@ -207,6 +252,10 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI case meteoraDlmmAddLiquidityDiscriminator, meteoraDlmmAddLiquidity2Discriminator, meteoraDlmmAddLiquidityByStrategyDiscriminator, meteoraDlmmAddLiquidityByStrategy2Discriminator: return metaoradlmmAddLiquidityParser(tx, instruction, innerInstructions, offset) + case meteoraDlmmClaimFeeDiscriminator, meteoraDlmmClaimFee2Discriminator: + return metaoradlmmClaimFeeParser(tx, instruction, innerInstructions, offset) + case meteoraDlmmRebalanceLiquidityDiscriminator: + return metaoradlmmRebalanceLiquidityParser(tx, instruction, innerInstructions, offset) case meteoraDlmmRemoveLiquidityDiscriminator, meteoraDlmmRemoveLiquidity2Discriminator, meteoraDlmmRemoveLiquidityByRangeDiscriminator, meteoraDlmmRemoveLiquidityByRange2Discriminator: return metaoradlmmRemoveLiquidityParser(tx, instruction, innerInstructions, offset) @@ -826,6 +875,274 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst return []Swap{swap}, offset, nil } +func metaoradlmmClaimFeeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { + result := tx.rawTx + + entryContract := result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] + 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 + } + } + } + + accounts, err := resolveDlmmClaimFeeAccounts(result, instruction.Data, instruction.Accounts) + if err != nil { + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm claim fee accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1]) + } + + claimEvent, nextOffset, err := dlmmClaimFeeEventFromInnerInstructions(innerInstructions, instruction, offset) + if err != nil { + return nil, nextOffset, err + } + offset = nextOffset + if claimEvent.FeeX == 0 && claimEvent.FeeY == 0 { + return nil, offset, InstructionIgnoredError + } + + pool := result.accountList[accounts.poolIdx] + tokenXMint := result.accountList[accounts.tokenXMintIdx] + tokenYMint := result.accountList[accounts.tokenYMintIdx] + tokenXProgram := result.accountList[accounts.tokenXProgramIdx] + tokenYProgram := result.accountList[accounts.tokenYProgramIdx] + + baseMint, quoteMint, baseIsX := dlmmSelectBaseQuote(tokenXMint, tokenYMint) + baseTokenProgram := tokenXProgram + quoteTokenProgram := tokenYProgram + baseReserveIdx := accounts.reserveXIdx + quoteReserveIdx := accounts.reserveYIdx + userBaseIdx := accounts.userTokenXIdx + userQuoteIdx := accounts.userTokenYIdx + baseAmount := decimal.NewFromUint64(claimEvent.FeeX) + quoteAmount := decimal.NewFromUint64(claimEvent.FeeY) + if !baseIsX { + baseTokenProgram = tokenYProgram + quoteTokenProgram = tokenXProgram + baseReserveIdx = accounts.reserveYIdx + quoteReserveIdx = accounts.reserveXIdx + userBaseIdx = accounts.userTokenYIdx + userQuoteIdx = accounts.userTokenXIdx + baseAmount = decimal.NewFromUint64(claimEvent.FeeY) + quoteAmount = decimal.NewFromUint64(claimEvent.FeeX) + } + + eventUser := result.accountList[accounts.userIdx] + if !claimEvent.Owner.IsZero() { + eventUser = claimEvent.Owner + } + + baseDecimals, ok := dlmmTokenDecimals(result, baseReserveIdx) + if !ok { + baseDecimals, _ = dlmmTokenDecimals(result, userBaseIdx) + } + quoteDecimals, ok := dlmmTokenDecimals(result, quoteReserveIdx) + if !ok { + quoteDecimals, _ = dlmmTokenDecimals(result, userQuoteIdx) + } + + if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) { + tx.Token[baseMint] = TokenMeta{ + Mint: baseMint, + Decimals: baseDecimals, + TokenProgram: baseTokenProgram, + } + } + if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) { + tx.Token[quoteMint] = TokenMeta{ + Mint: quoteMint, + Decimals: quoteDecimals, + TokenProgram: quoteTokenProgram, + } + } + + baseReserve := getAccountBalanceAfterTx(result, baseReserveIdx) + quoteReserve := getAccountBalanceAfterTx(result, quoteReserveIdx) + userBase := getAccountBalanceAfterTx(result, userBaseIdx) + userQuote := getAccountBalanceAfterTx(result, userQuoteIdx) + if quoteMint.Equals(wSolMint) { + if solAmount, err := GetSolAfterTx(result, accounts.userIdx); err == nil { + userQuote = userQuote.Add(decimal.NewFromUint64(solAmount)) + } + } + + swap := Swap{ + Program: SolProgramMeteoraDLMM, + Event: "claim_fee", + Pool: pool, + BaseMint: baseMint, + QuoteMint: quoteMint, + BaseTokenProgram: baseTokenProgram, + QuoteTokenProgram: quoteTokenProgram, + BaseMintDecimals: baseDecimals, + QuoteMintDecimals: quoteDecimals, + User: eventUser, + BaseAmount: baseAmount, + QuoteAmount: quoteAmount, + BaseReserve: baseReserve, + QuoteReserve: quoteReserve, + UserBaseBalance: userBase, + UserQuoteBalance: userQuote, + EntryContract: entryContract, + } + if claimEvent.HasActiveBin { + swap.StartBinId = claimEvent.ActiveBinId + swap.EndBinId = claimEvent.ActiveBinId + } + + return []Swap{swap}, offset, nil +} + +func metaoradlmmRebalanceLiquidityParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { + result := tx.rawTx + + entryContract := result.accountList[result.Transaction.Message.Instructions[offset[0]].ProgramIDIndex] + 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 + } + } + } + + accounts, err := resolveDlmmRebalanceAccounts(result, instruction.Accounts) + if err != nil { + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1]) + } + + event, nextOffset, err := dlmmRebalancingEventFromInnerInstructions(innerInstructions, instruction, offset) + if err != nil { + return nil, nextOffset, err + } + offset = nextOffset + + pool := result.accountList[accounts.poolIdx] + tokenXMint := result.accountList[accounts.tokenXMintIdx] + tokenYMint := result.accountList[accounts.tokenYMintIdx] + tokenXProgram := result.accountList[accounts.tokenXProgramIdx] + tokenYProgram := result.accountList[accounts.tokenYProgramIdx] + + baseMint, quoteMint, baseIsX := dlmmSelectBaseQuote(tokenXMint, tokenYMint) + baseTokenProgram := tokenXProgram + quoteTokenProgram := tokenYProgram + baseReserveIdx := accounts.reserveXIdx + quoteReserveIdx := accounts.reserveYIdx + userBaseIdx := accounts.userTokenXIdx + userQuoteIdx := accounts.userTokenYIdx + withdrawBase := event.XWithdrawnAmount + withdrawQuote := event.YWithdrawnAmount + addBase := event.XAddedAmount + addQuote := event.YAddedAmount + if !baseIsX { + baseTokenProgram = tokenYProgram + quoteTokenProgram = tokenXProgram + baseReserveIdx = accounts.reserveYIdx + quoteReserveIdx = accounts.reserveXIdx + userBaseIdx = accounts.userTokenYIdx + userQuoteIdx = accounts.userTokenXIdx + withdrawBase = event.YWithdrawnAmount + withdrawQuote = event.XWithdrawnAmount + addBase = event.YAddedAmount + addQuote = event.XAddedAmount + } + + eventUser := result.accountList[accounts.userIdx] + if !event.Owner.IsZero() { + eventUser = event.Owner + } + + baseDecimals, ok := dlmmTokenDecimals(result, baseReserveIdx) + if !ok { + baseDecimals, _ = dlmmTokenDecimals(result, userBaseIdx) + } + quoteDecimals, ok := dlmmTokenDecimals(result, quoteReserveIdx) + if !ok { + quoteDecimals, _ = dlmmTokenDecimals(result, userQuoteIdx) + } + + if _, exists := tx.Token[baseMint]; !exists && !baseMint.Equals(wSolMint) { + tx.Token[baseMint] = TokenMeta{ + Mint: baseMint, + Decimals: baseDecimals, + TokenProgram: baseTokenProgram, + } + } + if _, exists := tx.Token[quoteMint]; !exists && !quoteMint.Equals(wSolMint) { + tx.Token[quoteMint] = TokenMeta{ + Mint: quoteMint, + Decimals: quoteDecimals, + TokenProgram: quoteTokenProgram, + } + } + + baseReserve := getAccountBalanceAfterTx(result, baseReserveIdx) + quoteReserve := getAccountBalanceAfterTx(result, quoteReserveIdx) + userBase := getAccountBalanceAfterTx(result, userBaseIdx) + userQuote := getAccountBalanceAfterTx(result, userQuoteIdx) + if quoteMint.Equals(wSolMint) { + if solAmount, err := GetSolAfterTx(result, accounts.userIdx); err == nil { + userQuote = userQuote.Add(decimal.NewFromUint64(solAmount)) + } + } + + var swaps []Swap + if withdrawBase > 0 || withdrawQuote > 0 { + swaps = append(swaps, Swap{ + Program: SolProgramMeteoraDLMM, + Event: "remove", + Pool: pool, + BaseMint: baseMint, + QuoteMint: quoteMint, + BaseTokenProgram: baseTokenProgram, + QuoteTokenProgram: quoteTokenProgram, + BaseMintDecimals: baseDecimals, + QuoteMintDecimals: quoteDecimals, + User: eventUser, + BaseAmount: decimal.NewFromUint64(withdrawBase), + QuoteAmount: decimal.NewFromUint64(withdrawQuote), + BaseReserve: baseReserve, + QuoteReserve: quoteReserve, + UserBaseBalance: userBase, + UserQuoteBalance: userQuote, + EntryContract: entryContract, + StartBinId: event.OldMinBinId, + EndBinId: event.OldMaxBinId, + BinChanges: dlmmBinChangesFromRange(event.OldMinBinId, event.OldMaxBinId, 0), + }) + } + if addBase > 0 || addQuote > 0 { + swaps = append(swaps, Swap{ + Program: SolProgramMeteoraDLMM, + Event: "add", + Pool: pool, + BaseMint: baseMint, + QuoteMint: quoteMint, + BaseTokenProgram: baseTokenProgram, + QuoteTokenProgram: quoteTokenProgram, + BaseMintDecimals: baseDecimals, + QuoteMintDecimals: quoteDecimals, + User: eventUser, + BaseAmount: decimal.NewFromUint64(addBase), + QuoteAmount: decimal.NewFromUint64(addQuote), + BaseReserve: baseReserve, + QuoteReserve: quoteReserve, + UserBaseBalance: userBase, + UserQuoteBalance: userQuote, + EntryContract: entryContract, + StartBinId: event.NewMinBinId, + EndBinId: event.NewMaxBinId, + BinChanges: dlmmBinChangesFromRange(event.NewMinBinId, event.NewMaxBinId, 0), + }) + } + if len(swaps) == 0 { + return nil, offset, InstructionIgnoredError + } + + return swaps, offset, nil +} + func dlmmSelectBaseQuote(tokenX, tokenY solana.PublicKey) (baseMint solana.PublicKey, quoteMint solana.PublicKey, baseIsX bool) { priority := []solana.PublicKey{wSolMint, usdcMint, usd1Mint} for _, mint := range priority { @@ -887,6 +1204,68 @@ func dlmmRemoveLiquidityEventFromInnerInstructions(innerInstructions InnerInstru return dlmmRemoveLiquidityEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm remove liquidity event not found, offset, %d, %d", offset[0], prefixLen) } +func dlmmClaimFeeEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmClaimFeeInnerEvent, [2]uint, error) { + var prefixLen = offset[1] + inners, err := getInnerInstructions(innerInstructions, prefixLen) + if err != nil { + return dlmmClaimFeeInnerEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm claim fee get inner instructions error: %v, offset, %d, %d", err, offset[0], prefixLen) + } + + var ( + found bool + event dlmmClaimFeeInnerEvent + matchedIdx int + ) + for innerIndex, innerInstr := range inners { + if innerInstr.ProgramIDIndex != instruction.ProgramIDIndex { + continue + } + decoded, ok := dlmmDecodeClaimFeeEvent(innerInstr.Data) + if !ok { + continue + } + found = true + event = decoded + matchedIdx = innerIndex + if decoded.HasActiveBin { + break + } + } + if !found { + return dlmmClaimFeeInnerEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm claim fee event not found, offset, %d, %d", offset[0], prefixLen) + } + if offset[1] == 0 { + offset[0] += 1 + } else { + offset[1] = uint(matchedIdx) + 1 + prefixLen + } + return event, offset, nil +} + +func dlmmRebalancingEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmRebalancingEvent, [2]uint, error) { + var prefixLen = offset[1] + inners, err := getInnerInstructions(innerInstructions, prefixLen) + if err != nil { + return dlmmRebalancingEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity get inner instructions error: %v, offset, %d, %d", err, offset[0], prefixLen) + } + for innerIndex, innerInstr := range inners { + if innerInstr.ProgramIDIndex != instruction.ProgramIDIndex { + continue + } + event, ok := dlmmDecodeRebalancingEvent(innerInstr.Data) + if !ok { + continue + } + if offset[1] == 0 { + offset[0] += 1 + } else { + offset[1] = uint(innerIndex) + 1 + prefixLen + } + return event, offset, nil + } + return dlmmRebalancingEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity event not found, offset, %d, %d", offset[0], prefixLen) +} + func dlmmDecodeAddLiquidityEvent(data []byte) (dlmmAddLiquidityEvent, bool) { switch { case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmAddLiquidityEventDiscriminator[:]): @@ -929,6 +1308,64 @@ func dlmmDecodeRemoveLiquidityEvent(data []byte) (dlmmRemoveLiquidityEvent, bool } } +func dlmmDecodeClaimFeeEvent(data []byte) (dlmmClaimFeeInnerEvent, bool) { + switch { + case len(data) >= 16 && + bytes.Equal(data[:8], eventDiscriminator[:]) && + bytes.Equal(data[8:16], meteoraDlmmClaimFee2EventDiscriminator[:]): + var event dlmmClaimFee2Event + if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil { + return dlmmClaimFeeInnerEvent{}, false + } + return dlmmClaimFeeInnerEvent{ + LbPair: event.LbPair, + Position: event.Position, + Owner: event.Owner, + FeeX: event.FeeX, + FeeY: event.FeeY, + ActiveBinId: event.ActiveBinId, + HasActiveBin: true, + }, true + case len(data) >= 16 && + bytes.Equal(data[:8], eventDiscriminator[:]) && + bytes.Equal(data[8:16], meteoraDlmmClaimFeeEventDiscriminator[:]): + var event dlmmClaimFeeEvent + if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil { + return dlmmClaimFeeInnerEvent{}, false + } + return dlmmClaimFeeInnerEvent{ + LbPair: event.LbPair, + Position: event.Position, + Owner: event.Owner, + FeeX: event.FeeX, + FeeY: event.FeeY, + }, true + default: + return dlmmClaimFeeInnerEvent{}, false + } +} + +func dlmmDecodeRebalancingEvent(data []byte) (dlmmRebalancingEvent, bool) { + switch { + case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmRebalancingEventDiscriminator[:]): + var event dlmmRebalancingEvent + if err := agbinary.NewBorshDecoder(data[8:]).Decode(&event); err != nil { + return dlmmRebalancingEvent{}, false + } + return event, true + case len(data) >= 16 && + bytes.Equal(data[:8], eventDiscriminator[:]) && + bytes.Equal(data[8:16], meteoraDlmmRebalancingEventDiscriminator[:]): + var event dlmmRebalancingEvent + if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil { + return dlmmRebalancingEvent{}, false + } + return event, true + default: + return dlmmRebalancingEvent{}, false + } +} + func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, error) { if len(accounts) < 13 { return dlmmSwapAccounts{}, fmt.Errorf("accounts too short, expected at least 13") @@ -1046,6 +1483,93 @@ func resolveDlmmLiquidityAccounts(result *RawTx, accounts []int) (dlmmLiquidityA }, nil } +func resolveDlmmClaimFeeAccounts(result *RawTx, data []byte, accounts []int) (dlmmLiquidityAccounts, error) { + if len(data) < 8 { + return dlmmLiquidityAccounts{}, fmt.Errorf("instruction data too short") + } + discriminator := *(*[8]byte)(data[:8]) + accountList := result.accountList + + switch discriminator { + case meteoraDlmmClaimFee2Discriminator: + if len(accounts) < 14 { + return dlmmLiquidityAccounts{}, fmt.Errorf("accounts too short, expected at least 14") + } + if !accountList[accounts[12]].Equals(meteoraDlmmEventAuthority) { + return dlmmLiquidityAccounts{}, fmt.Errorf("event authority mismatch") + } + if !accountList[accounts[13]].Equals(meteoraDlmmProgram) { + return dlmmLiquidityAccounts{}, fmt.Errorf("program id mismatch") + } + return dlmmLiquidityAccounts{ + positionIdx: accounts[1], + poolIdx: accounts[0], + userTokenXIdx: accounts[5], + userTokenYIdx: accounts[6], + reserveXIdx: accounts[3], + reserveYIdx: accounts[4], + tokenXMintIdx: accounts[7], + tokenYMintIdx: accounts[8], + userIdx: accounts[2], + tokenXProgramIdx: accounts[9], + tokenYProgramIdx: accounts[10], + }, nil + case meteoraDlmmClaimFeeDiscriminator: + if len(accounts) < 14 { + return dlmmLiquidityAccounts{}, fmt.Errorf("accounts too short, expected at least 14") + } + if !accountList[accounts[12]].Equals(meteoraDlmmEventAuthority) { + return dlmmLiquidityAccounts{}, fmt.Errorf("event authority mismatch") + } + if !accountList[accounts[13]].Equals(meteoraDlmmProgram) { + return dlmmLiquidityAccounts{}, fmt.Errorf("program id mismatch") + } + return dlmmLiquidityAccounts{ + positionIdx: accounts[1], + poolIdx: accounts[0], + userTokenXIdx: accounts[7], + userTokenYIdx: accounts[8], + reserveXIdx: accounts[5], + reserveYIdx: accounts[6], + tokenXMintIdx: accounts[9], + tokenYMintIdx: accounts[10], + userIdx: accounts[4], + tokenXProgramIdx: accounts[11], + tokenYProgramIdx: accounts[11], + }, nil + default: + return dlmmLiquidityAccounts{}, fmt.Errorf("unsupported claim fee discriminator") + } +} + +func resolveDlmmRebalanceAccounts(result *RawTx, accounts []int) (dlmmLiquidityAccounts, error) { + if len(accounts) < 17 { + return dlmmLiquidityAccounts{}, fmt.Errorf("accounts too short, expected at least 17") + } + accountList := result.accountList + + if !accountList[accounts[15]].Equals(meteoraDlmmEventAuthority) { + return dlmmLiquidityAccounts{}, fmt.Errorf("event authority mismatch") + } + if !accountList[accounts[16]].Equals(meteoraDlmmProgram) { + return dlmmLiquidityAccounts{}, fmt.Errorf("program id mismatch") + } + + return dlmmLiquidityAccounts{ + positionIdx: accounts[0], + poolIdx: accounts[1], + userTokenXIdx: accounts[3], + userTokenYIdx: accounts[4], + reserveXIdx: accounts[5], + reserveYIdx: accounts[6], + tokenXMintIdx: accounts[7], + tokenYMintIdx: accounts[8], + userIdx: accounts[9], + tokenXProgramIdx: accounts[11], + tokenYProgramIdx: accounts[12], + }, nil +} + func dlmmTokenDecimals(result *RawTx, accountIndex int) (uint8, bool) { for _, meta := range result.Meta.PostTokenBalances { if meta.AccountIndex == accountIndex {