diff --git a/meta.go b/meta.go index 1cce54e..0bb1a2d 100644 --- a/meta.go +++ b/meta.go @@ -68,31 +68,40 @@ var ( ) var ( - meteoraInitializeLbPairDiscriminator = calculateDiscriminator("global:initialize_lb_pair2") - meteoraInitializeLbPairEventDiscriminator = calculateDiscriminator("event:LbPairCreate") - meteoraDlmmSwapDiscriminator = calculateDiscriminator("global:swap") - meteoraDlmmSwap2Discriminator = calculateDiscriminator("global:swap2") - meteoraDlmmSwapExactOutDiscriminator = calculateDiscriminator("global:swap_exact_out") - meteoraDlmmSwapExactOut2Discriminator = calculateDiscriminator("global:swap_exact_out2") - meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact") - meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2") - meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap") - meteoraDlmmAddLiquidityDiscriminator = calculateDiscriminator("global:add_liquidity") - 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") + meteoraInitializeLbPairDiscriminator = calculateDiscriminator("global:initialize_lb_pair2") + meteoraInitializeLbPairEventDiscriminator = calculateDiscriminator("event:LbPairCreate") + meteoraDlmmSwapDiscriminator = calculateDiscriminator("global:swap") + meteoraDlmmSwap2Discriminator = calculateDiscriminator("global:swap2") + meteoraDlmmSwapExactOutDiscriminator = calculateDiscriminator("global:swap_exact_out") + meteoraDlmmSwapExactOut2Discriminator = calculateDiscriminator("global:swap_exact_out2") + meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact") + meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2") + meteoraDlmmInitializePositionDiscriminator = calculateDiscriminator("global:initialize_position") + meteoraDlmmInitializePosition2Discriminator = calculateDiscriminator("global:initialize_position2") + meteoraDlmmInitializePositionByOperatorDiscriminator = calculateDiscriminator("global:initialize_position_by_operator") + meteoraDlmmInitializePositionPdaDiscriminator = calculateDiscriminator("global:initialize_position_pda") + meteoraDlmmClosePositionDiscriminator = calculateDiscriminator("global:close_position") + meteoraDlmmClosePosition2Discriminator = calculateDiscriminator("global:close_position2") + meteoraDlmmClosePositionIfEmptyDiscriminator = calculateDiscriminator("global:close_position_if_empty") + meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap") + meteoraDlmmAddLiquidityDiscriminator = calculateDiscriminator("global:add_liquidity") + 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") + meteoraDlmmPositionCloseEventDiscriminator = calculateDiscriminator("event:PositionClose") + meteoraDlmmPositionCreateEventDiscriminator = calculateDiscriminator("event:PositionCreate") + meteoraDlmmRebalancingEventDiscriminator = calculateDiscriminator("event:Rebalancing") + meteoraDlmmRemoveLiquidityEventDiscriminator = calculateDiscriminator("event:RemoveLiquidity") ) var ( diff --git a/metaoradlmm.go b/metaoradlmm.go index ad38642..0c7ccb7 100644 --- a/metaoradlmm.go +++ b/metaoradlmm.go @@ -55,6 +55,17 @@ type dlmmRemoveLiquidityEvent struct { ActiveBinId int32 } +type dlmmPositionCreateEvent struct { + LbPair solana.PublicKey + Position solana.PublicKey + Owner solana.PublicKey +} + +type dlmmPositionCloseEvent struct { + Position solana.PublicKey + Owner solana.PublicKey +} + type dlmmClaimFeeInnerEvent struct { LbPair solana.PublicKey Position solana.PublicKey @@ -172,6 +183,18 @@ type dlmmRemoveLiquidityByRange2Args struct { RemainingAccountsInfo dlmmRemainingAccountsInfo } +type dlmmInitializePositionArgs struct { + LowerBinId int32 + Width int32 +} + +type dlmmInitializePositionByOperatorArgs struct { + LowerBinId int32 + Width int32 + FeeOwner solana.PublicKey + LockReleasePoint uint64 +} + type dlmmRemainingAccountsInfo struct{} func (dlmmRemainingAccountsInfo) UnmarshalWithDecoder(decoder *agbinary.Decoder) error { @@ -245,6 +268,9 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI switch discriminator { case meteoraInitializeLbPairDiscriminator: return metaoradlmmInitializeParser(tx, instruction, innerInstructions, offset) + case meteoraDlmmInitializePositionDiscriminator, meteoraDlmmInitializePosition2Discriminator, + meteoraDlmmInitializePositionByOperatorDiscriminator, meteoraDlmmInitializePositionPdaDiscriminator: + return metaoradlmmPositionCreateParser(tx, instruction, innerInstructions, offset) case meteoraDlmmSwapDiscriminator, meteoraDlmmSwapExactOutDiscriminator, meteoraDlmmSwapWithPriceImpactDiscriminator: return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset) case meteoraDlmmSwap2Discriminator, meteoraDlmmSwapExactOut2Discriminator, meteoraDlmmSwapWithPriceImpact2Discriminator: @@ -259,6 +285,8 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI case meteoraDlmmRemoveLiquidityDiscriminator, meteoraDlmmRemoveLiquidity2Discriminator, meteoraDlmmRemoveLiquidityByRangeDiscriminator, meteoraDlmmRemoveLiquidityByRange2Discriminator: return metaoradlmmRemoveLiquidityParser(tx, instruction, innerInstructions, offset) + case meteoraDlmmClosePositionDiscriminator, meteoraDlmmClosePosition2Discriminator, meteoraDlmmClosePositionIfEmptyDiscriminator: + return metaoradlmmPositionCloseParser(tx, instruction, innerInstructions, offset) default: return nil, increaseOffset(offset), InstructionIgnoredError } @@ -315,6 +343,141 @@ func metaoradlmmInitializeParser(tx *Tx, instruction Instruction, innerInstructi return []Swap{swap}, offset, nil } +func metaoradlmmPositionCreateParser(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 + } + } + } + + decode := instruction.Data + if len(decode) < 8 { + offset[1] += 1 + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm create position instruction data too short, offset, %d, %d", offset[0], offset[1]) + } + + discriminator := *(*[8]byte)(decode[:8]) + var ( + lowerBinId int32 + width int32 + ) + + switch discriminator { + case meteoraDlmmInitializePositionDiscriminator, meteoraDlmmInitializePosition2Discriminator, meteoraDlmmInitializePositionPdaDiscriminator: + var args dlmmInitializePositionArgs + if err := agbinary.NewBorshDecoder(decode[8:]).Decode(&args); err != nil { + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm create position decode error: %v, offset, %d, %d", err, offset[0], offset[1]) + } + lowerBinId = args.LowerBinId + width = args.Width + case meteoraDlmmInitializePositionByOperatorDiscriminator: + var args dlmmInitializePositionByOperatorArgs + if err := agbinary.NewBorshDecoder(decode[8:]).Decode(&args); err != nil { + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm create position by operator decode error: %v, offset, %d, %d", err, offset[0], offset[1]) + } + lowerBinId = args.LowerBinId + width = args.Width + default: + return nil, increaseOffset(offset), InstructionIgnoredError + } + + pool, positionAccount, eventUser, err := dlmmPositionCreateInstructionAccounts(result, discriminator, instruction.Accounts) + if err != nil { + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm create position accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1]) + } + + createEvent, nextOffset, err := dlmmPositionCreateEventFromInnerInstructions(innerInstructions, instruction, offset) + if err != nil { + return nil, nextOffset, err + } + offset = nextOffset + + if !createEvent.LbPair.IsZero() { + pool = createEvent.LbPair + } + if !createEvent.Position.IsZero() { + positionAccount = createEvent.Position + } + if !createEvent.Owner.IsZero() { + eventUser = createEvent.Owner + } + + swap := Swap{ + Program: SolProgramMeteoraDLMM, + Event: "open", + Pool: pool, + User: eventUser, + EntryContract: entryContract, + StartBinId: lowerBinId, + EndBinId: dlmmPositionUpperBinId(lowerBinId, width), + PositionAccount: positionAccount, + } + + return []Swap{swap}, offset, nil +} + +func metaoradlmmPositionCloseParser(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 + } + } + } + + decode := instruction.Data + if len(decode) < 8 { + offset[1] += 1 + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm close position instruction data too short, offset, %d, %d", offset[0], offset[1]) + } + + discriminator := *(*[8]byte)(decode[:8]) + pool, positionAccount, eventUser, err := dlmmPositionCloseInstructionAccounts(result, discriminator, instruction.Accounts) + if err != nil { + return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm close position accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1]) + } + + closeEvent, nextOffset, found, err := dlmmPositionCloseEventFromInnerInstructions(innerInstructions, instruction, offset) + if err != nil { + return nil, nextOffset, err + } + if !found { + if discriminator == meteoraDlmmClosePositionIfEmptyDiscriminator { + return nil, nextOffset, InstructionIgnoredError + } + return nil, nextOffset, fmt.Errorf("meteora dlmm close position event not found, offset, %d, %d", nextOffset[0], nextOffset[1]) + } + offset = nextOffset + + if !closeEvent.Position.IsZero() { + positionAccount = closeEvent.Position + } + if !closeEvent.Owner.IsZero() { + eventUser = closeEvent.Owner + } + + swap := Swap{ + Program: SolProgramMeteoraDLMM, + Event: "close", + Pool: pool, + User: eventUser, + EntryContract: entryContract, + PositionAccount: positionAccount, + } + + return []Swap{swap}, offset, nil +} + func metaoradlmmSwapParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) { result := tx.rawTx @@ -704,6 +867,7 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc StartBinId: startBinId, EndBinId: endBinId, BinChanges: binChanges, + PositionAccount: result.accountList[accounts.positionIdx], } return []Swap{swap}, offset, nil @@ -879,6 +1043,7 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst EndBinId: endBinId, RemoveBp: removeBp, BinChanges: binChanges, + PositionAccount: result.accountList[accounts.positionIdx], } return []Swap{swap}, offset, nil @@ -994,6 +1159,7 @@ func metaoradlmmClaimFeeParser(tx *Tx, instruction Instruction, innerInstruction UserBaseBalance: userBase, UserQuoteBalance: userQuote, EntryContract: entryContract, + PositionAccount: result.accountList[accounts.positionIdx], } if claimEvent.HasActiveBin { swap.ActiveBinId = claimEvent.ActiveBinId @@ -1121,6 +1287,7 @@ func metaoradlmmRebalanceLiquidityParser(tx *Tx, instruction Instruction, innerI StartBinId: event.OldMinBinId, EndBinId: event.OldMaxBinId, BinChanges: dlmmBinChangesFromRange(event.OldMinBinId, event.OldMaxBinId, 0), + PositionAccount: result.accountList[accounts.positionIdx], }) } if addBase > 0 || addQuote > 0 { @@ -1146,6 +1313,7 @@ func metaoradlmmRebalanceLiquidityParser(tx *Tx, instruction Instruction, innerI StartBinId: event.NewMinBinId, EndBinId: event.NewMaxBinId, BinChanges: dlmmBinChangesFromRange(event.NewMinBinId, event.NewMaxBinId, 0), + PositionAccount: result.accountList[accounts.positionIdx], }) } if len(swaps) == 0 { @@ -1278,6 +1446,54 @@ func dlmmRebalancingEventFromInnerInstructions(innerInstructions InnerInstructio return dlmmRebalancingEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm rebalance liquidity event not found, offset, %d, %d", offset[0], prefixLen) } +func dlmmPositionCreateEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmPositionCreateEvent, [2]uint, error) { + var prefixLen = offset[1] + inners, err := getInnerInstructions(innerInstructions, prefixLen) + if err != nil { + return dlmmPositionCreateEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm create position 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 := dlmmDecodePositionCreateEvent(innerInstr.Data) + if !ok { + continue + } + if offset[1] == 0 { + offset[0] += 1 + } else { + offset[1] = uint(innerIndex) + 1 + prefixLen + } + return event, offset, nil + } + return dlmmPositionCreateEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm create position event not found, offset, %d, %d", offset[0], prefixLen) +} + +func dlmmPositionCloseEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmPositionCloseEvent, [2]uint, bool, error) { + var prefixLen = offset[1] + inners, err := getInnerInstructions(innerInstructions, prefixLen) + if err != nil { + return dlmmPositionCloseEvent{}, increaseOffset(offset), false, fmt.Errorf("meteora dlmm close position 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 := dlmmDecodePositionCloseEvent(innerInstr.Data) + if !ok { + continue + } + if offset[1] == 0 { + offset[0] += 1 + } else { + offset[1] = uint(innerIndex) + 1 + prefixLen + } + return event, offset, true, nil + } + return dlmmPositionCloseEvent{}, increaseOffset(offset), false, nil +} + func dlmmDecodeAddLiquidityEvent(data []byte) (dlmmAddLiquidityEvent, bool) { switch { case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmAddLiquidityEventDiscriminator[:]): @@ -1320,6 +1536,48 @@ func dlmmDecodeRemoveLiquidityEvent(data []byte) (dlmmRemoveLiquidityEvent, bool } } +func dlmmDecodePositionCreateEvent(data []byte) (dlmmPositionCreateEvent, bool) { + switch { + case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmPositionCreateEventDiscriminator[:]): + var event dlmmPositionCreateEvent + if err := agbinary.NewBorshDecoder(data[8:]).Decode(&event); err != nil { + return dlmmPositionCreateEvent{}, false + } + return event, true + case len(data) >= 16 && + bytes.Equal(data[:8], eventDiscriminator[:]) && + bytes.Equal(data[8:16], meteoraDlmmPositionCreateEventDiscriminator[:]): + var event dlmmPositionCreateEvent + if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil { + return dlmmPositionCreateEvent{}, false + } + return event, true + default: + return dlmmPositionCreateEvent{}, false + } +} + +func dlmmDecodePositionCloseEvent(data []byte) (dlmmPositionCloseEvent, bool) { + switch { + case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmPositionCloseEventDiscriminator[:]): + var event dlmmPositionCloseEvent + if err := agbinary.NewBorshDecoder(data[8:]).Decode(&event); err != nil { + return dlmmPositionCloseEvent{}, false + } + return event, true + case len(data) >= 16 && + bytes.Equal(data[:8], eventDiscriminator[:]) && + bytes.Equal(data[8:16], meteoraDlmmPositionCloseEventDiscriminator[:]): + var event dlmmPositionCloseEvent + if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil { + return dlmmPositionCloseEvent{}, false + } + return event, true + default: + return dlmmPositionCloseEvent{}, false + } +} + func dlmmDecodeClaimFeeEvent(data []byte) (dlmmClaimFeeInnerEvent, bool) { switch { case len(data) >= 16 && @@ -1378,6 +1636,40 @@ func dlmmDecodeRebalancingEvent(data []byte) (dlmmRebalancingEvent, bool) { } } +func dlmmPositionCreateInstructionAccounts(result *RawTx, discriminator [8]byte, accounts []int) (pool, position, owner solana.PublicKey, err error) { + switch discriminator { + case meteoraDlmmInitializePositionDiscriminator, meteoraDlmmInitializePosition2Discriminator: + if len(accounts) < 4 { + return solana.PublicKey{}, solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("accounts too short, expected at least 4") + } + return result.accountList[accounts[2]], result.accountList[accounts[1]], result.accountList[accounts[3]], nil + case meteoraDlmmInitializePositionByOperatorDiscriminator, meteoraDlmmInitializePositionPdaDiscriminator: + if len(accounts) < 5 { + return solana.PublicKey{}, solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("accounts too short, expected at least 5") + } + return result.accountList[accounts[3]], result.accountList[accounts[2]], result.accountList[accounts[4]], nil + default: + return solana.PublicKey{}, solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("unsupported create position discriminator") + } +} + +func dlmmPositionCloseInstructionAccounts(result *RawTx, discriminator [8]byte, accounts []int) (pool, position, owner solana.PublicKey, err error) { + switch discriminator { + case meteoraDlmmClosePositionDiscriminator: + if len(accounts) < 5 { + return solana.PublicKey{}, solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("accounts too short, expected at least 5") + } + return result.accountList[accounts[1]], result.accountList[accounts[0]], result.accountList[accounts[4]], nil + case meteoraDlmmClosePosition2Discriminator, meteoraDlmmClosePositionIfEmptyDiscriminator: + if len(accounts) < 2 { + return solana.PublicKey{}, solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("accounts too short, expected at least 2") + } + return solana.PublicKey{}, result.accountList[accounts[0]], result.accountList[accounts[1]], nil + default: + return solana.PublicKey{}, solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("unsupported close position discriminator") + } +} + func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, error) { if len(accounts) < 13 { return dlmmSwapAccounts{}, fmt.Errorf("accounts too short, expected at least 13") @@ -1731,6 +2023,13 @@ func dlmmCommonRemoveBp(reduction []dlmmBinLiquidityReduction) int32 { return int32(bpsToRemove) } +func dlmmPositionUpperBinId(lowerBinId, width int32) int32 { + if width <= 0 { + return lowerBinId + } + return lowerBinId + width - 1 +} + func dlmmMinMaxBinIdFromDistribution(dist []dlmmBinLiquidityDistribution) (int32, int32) { if len(dist) == 0 { return 0, 0 diff --git a/tx.go b/tx.go index 5933c37..f0c3dde 100644 --- a/tx.go +++ b/tx.go @@ -53,6 +53,7 @@ type Swap struct { EndBinId int32 RemoveBp int32 BinChanges []DlmmBinLiquidityChange + PositionAccount solana.PublicKey ConsumeUnit uint64 }