2026-01-07 16:41:49 +08:00
package pump_parser
import (
"bytes"
"fmt"
agbinary "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
"github.com/shopspring/decimal"
)
type meteoraDlmmSwapArgs struct {
AmountIn uint64
MinAmountOut uint64
}
type meteoraDlmmSwapExactOutArgs struct {
MaxInAmount uint64
OutAmount uint64
}
type meteoraDlmmSwapWithPriceImpactArgs struct {
AmountIn uint64
ActiveID * int32 ` bin:"optional" `
MaxPriceImpactBps uint16
}
type dlmmSwapEvent struct {
LbPair solana . PublicKey
From solana . PublicKey
StartBinId int32
EndBinId int32
AmountIn uint64
AmountOut uint64
SwapForY bool
Fee uint64
ProtocolFee uint64
FeeBps agbinary . Uint128
HostFee uint64
}
2026-01-15 17:44:39 +08:00
type dlmmAddLiquidityEvent struct {
LbPair solana . PublicKey
From solana . PublicKey
Position solana . PublicKey
Amounts [ 2 ] uint64
ActiveBinId int32
}
type dlmmRemoveLiquidityEvent struct {
LbPair solana . PublicKey
From solana . PublicKey
Position solana . PublicKey
Amounts [ 2 ] uint64
ActiveBinId int32
}
2026-03-19 14:10:14 +08:00
type dlmmPositionCreateEvent struct {
LbPair solana . PublicKey
Position solana . PublicKey
Owner solana . PublicKey
}
type dlmmPositionCloseEvent struct {
Position solana . PublicKey
Owner solana . PublicKey
}
2026-04-11 08:27:34 +08:00
type dlmmLbPairCreateEvent struct {
LbPair solana . PublicKey
BinStep uint16
TokenX solana . PublicKey
TokenY solana . PublicKey
}
2026-03-16 11:37:19 +08:00
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
}
2026-01-15 17:44:39 +08:00
type dlmmBinLiquidityDistribution struct {
BinId int32
DistributionX uint16
DistributionY uint16
}
2026-03-20 17:06:37 +08:00
type dlmmBinLiquidityDistributionByWeight struct {
BinId int32
Weight uint16
}
2026-01-15 17:44:39 +08:00
type dlmmBinLiquidityReduction struct {
BinId int32
BpsToRemove uint16
}
type dlmmLiquidityParameter struct {
AmountX uint64
AmountY uint64
BinLiquidityDist [ ] dlmmBinLiquidityDistribution
}
type dlmmStrategyParameters struct {
MinBinId int32
MaxBinId int32
StrategyType uint8
Parameters [ 64 ] byte
}
type dlmmLiquidityParameterByStrategy struct {
2026-01-15 17:45:17 +08:00
AmountX uint64
AmountY uint64
ActiveID int32
2026-01-15 17:44:39 +08:00
MaxActiveBinSlippage int32
2026-01-15 17:45:17 +08:00
StrategyParameters dlmmStrategyParameters
2026-01-15 17:44:39 +08:00
}
2026-03-20 17:06:37 +08:00
type dlmmLiquidityParameterByWeight struct {
AmountX uint64
AmountY uint64
ActiveID int32
MaxActiveBinSlippage int32
BinLiquidityDist [ ] dlmmBinLiquidityDistributionByWeight
}
2026-01-15 17:44:39 +08:00
type dlmmAddLiquidityArgs struct {
LiquidityParameter dlmmLiquidityParameter
}
type dlmmAddLiquidity2Args struct {
LiquidityParameter dlmmLiquidityParameter
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
type dlmmAddLiquidityByStrategyArgs struct {
LiquidityParameter dlmmLiquidityParameterByStrategy
}
type dlmmAddLiquidityByStrategy2Args struct {
LiquidityParameter dlmmLiquidityParameterByStrategy
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
2026-03-20 17:06:37 +08:00
type dlmmAddLiquidityByWeightArgs struct {
LiquidityParameter dlmmLiquidityParameterByWeight
}
2026-03-25 11:34:46 +08:00
type dlmmLiquidityOneSideParameter struct {
Amount uint64
ActiveID int32
MaxActiveBinSlippage int32
BinLiquidityDist [ ] dlmmBinLiquidityDistributionByWeight
}
type dlmmLiquidityParameterByStrategyOneSide struct {
Amount uint64
ActiveID int32
MaxActiveBinSlippage int32
StrategyParameters dlmmStrategyParameters
}
type dlmmAddLiquidityOneSideArgs struct {
LiquidityParameter dlmmLiquidityOneSideParameter
}
type dlmmAddLiquidityByStrategyOneSideArgs struct {
LiquidityParameter dlmmLiquidityParameterByStrategyOneSide
}
type dlmmCompressedBinDepositAmount struct {
BinID int32
Amount uint32
}
type dlmmAddLiquiditySingleSidePreciseParameter struct {
Bins [ ] dlmmCompressedBinDepositAmount
DecompressMultiplier uint64
}
type dlmmAddLiquiditySingleSidePreciseParameter2 struct {
Bins [ ] dlmmCompressedBinDepositAmount
DecompressMultiplier uint64
MaxAmount uint64
}
type dlmmAddLiquidityOneSidePreciseArgs struct {
Parameter dlmmAddLiquiditySingleSidePreciseParameter
}
type dlmmAddLiquidityOneSidePrecise2Args struct {
LiquidityParameter dlmmAddLiquiditySingleSidePreciseParameter2
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
2026-01-15 17:44:39 +08:00
type dlmmRemoveLiquidityArgs struct {
BinLiquidityRemoval [ ] dlmmBinLiquidityReduction
}
type dlmmRemoveLiquidity2Args struct {
2026-01-15 17:45:17 +08:00
BinLiquidityRemoval [ ] dlmmBinLiquidityReduction
2026-01-15 17:44:39 +08:00
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
type dlmmRemoveLiquidityByRangeArgs struct {
2026-01-15 17:45:17 +08:00
FromBinId int32
ToBinId int32
BpsToRemove uint16
2026-01-15 17:44:39 +08:00
}
type dlmmRemoveLiquidityByRange2Args struct {
2026-01-15 17:45:17 +08:00
FromBinId int32
ToBinId int32
BpsToRemove uint16
2026-01-15 17:44:39 +08:00
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
2026-03-19 14:10:14 +08:00
type dlmmInitializePositionArgs struct {
LowerBinId int32
Width int32
}
type dlmmInitializePositionByOperatorArgs struct {
LowerBinId int32
Width int32
FeeOwner solana . PublicKey
LockReleasePoint uint64
}
2026-01-15 17:44:39 +08:00
type dlmmRemainingAccountsInfo struct { }
func ( dlmmRemainingAccountsInfo ) UnmarshalWithDecoder ( decoder * agbinary . Decoder ) error {
count , err := decoder . ReadUint32 ( agbinary . LE )
if err != nil {
return err
}
for i := uint32 ( 0 ) ; i < count ; i ++ {
variant , err := decoder . ReadUint8 ( )
if err != nil {
return err
}
if variant == 3 {
if _ , err := decoder . ReadUint8 ( ) ; err != nil {
return err
}
}
if _ , err := decoder . ReadUint8 ( ) ; err != nil {
return err
}
}
return nil
}
2026-01-07 16:41:49 +08:00
type dlmmSwapAccounts struct {
poolIdx int
reserveXIdx int
reserveYIdx int
userTokenInIdx int
userTokenOutIdx int
tokenXMintIdx int
tokenYMintIdx int
oracleIdx int
userIdx int
tokenXProgramIdx int
tokenYProgramIdx int
}
2026-01-15 17:44:39 +08:00
type dlmmLiquidityAccounts struct {
positionIdx int
poolIdx int
userTokenXIdx int
userTokenYIdx int
reserveXIdx int
reserveYIdx int
tokenXMintIdx int
tokenYMintIdx int
userIdx int
tokenXProgramIdx int
tokenYProgramIdx int
}
2026-03-25 11:34:46 +08:00
type dlmmOneSideLiquidityAccounts struct {
positionIdx int
poolIdx int
userTokenIdx int
reserveIdx int
tokenMintIdx int
userIdx int
tokenProgramIdx int
}
2026-01-07 16:41:49 +08:00
var meteoraDlmmEventAuthority = func ( ) solana . PublicKey {
key , _ , err := solana . FindProgramAddress ( [ ] [ ] byte { [ ] byte ( "__event_authority" ) } , meteoraDlmmProgram )
if err != nil {
return solana . PublicKey { }
}
return key
} ( )
func metaoradlmmParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
if ! tx . rawTx . accountList [ instruction . ProgramIDIndex ] . Equals ( meteoraDlmmProgram ) {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm program instruction not found, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
decode := instruction . Data
if len ( decode ) < 8 {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm program instruction data too short, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
discriminator := * ( * [ 8 ] byte ) ( decode [ : 8 ] )
switch discriminator {
2026-04-11 08:27:34 +08:00
case meteoraInitializeCustomizablePermissionlessLbPairDiscriminator ,
meteoraInitializeCustomizablePermissionlessLbPair2Discriminator ,
meteoraInitializeLbPairDiscriminator ,
meteoraInitializeLbPair2Discriminator ,
meteoraInitializePermissionLbPairDiscriminator :
2026-02-02 17:59:47 +08:00
return metaoradlmmInitializeParser ( tx , instruction , innerInstructions , offset )
2026-03-19 14:10:14 +08:00
case meteoraDlmmInitializePositionDiscriminator , meteoraDlmmInitializePosition2Discriminator ,
meteoraDlmmInitializePositionByOperatorDiscriminator , meteoraDlmmInitializePositionPdaDiscriminator :
return metaoradlmmPositionCreateParser ( tx , instruction , innerInstructions , offset )
2026-01-07 16:41:49 +08:00
case meteoraDlmmSwapDiscriminator , meteoraDlmmSwapExactOutDiscriminator , meteoraDlmmSwapWithPriceImpactDiscriminator :
return metaoradlmmSwapParser ( tx , instruction , innerInstructions , offset )
case meteoraDlmmSwap2Discriminator , meteoraDlmmSwapExactOut2Discriminator , meteoraDlmmSwapWithPriceImpact2Discriminator :
return metaoradlmmSwap2Parser ( tx , instruction , innerInstructions , offset )
2026-01-15 17:44:39 +08:00
case meteoraDlmmAddLiquidityDiscriminator , meteoraDlmmAddLiquidity2Discriminator ,
2026-03-20 17:06:37 +08:00
meteoraDlmmAddLiquidityByStrategyDiscriminator , meteoraDlmmAddLiquidityByStrategy2Discriminator ,
2026-03-25 11:34:46 +08:00
meteoraDlmmAddLiquidityByWeightDiscriminator , meteoraDlmmAddLiquidityOneSideDiscriminator ,
meteoraDlmmAddLiquidityOneSidePreciseDiscriminator , meteoraDlmmAddLiquidityOneSidePrecise2Discriminator ,
meteoraDlmmAddLiquidityByStrategyOneSideDiscriminator :
2026-01-15 17:44:39 +08:00
return metaoradlmmAddLiquidityParser ( tx , instruction , innerInstructions , offset )
2026-03-16 11:37:19 +08:00
case meteoraDlmmClaimFeeDiscriminator , meteoraDlmmClaimFee2Discriminator :
return metaoradlmmClaimFeeParser ( tx , instruction , innerInstructions , offset )
case meteoraDlmmRebalanceLiquidityDiscriminator :
return metaoradlmmRebalanceLiquidityParser ( tx , instruction , innerInstructions , offset )
2026-03-25 11:34:46 +08:00
case meteoraDlmmRemoveAllLiquidityDiscriminator , meteoraDlmmRemoveLiquidityDiscriminator , meteoraDlmmRemoveLiquidity2Discriminator ,
2026-01-15 17:44:39 +08:00
meteoraDlmmRemoveLiquidityByRangeDiscriminator , meteoraDlmmRemoveLiquidityByRange2Discriminator :
return metaoradlmmRemoveLiquidityParser ( tx , instruction , innerInstructions , offset )
2026-03-19 14:10:14 +08:00
case meteoraDlmmClosePositionDiscriminator , meteoraDlmmClosePosition2Discriminator , meteoraDlmmClosePositionIfEmptyDiscriminator :
return metaoradlmmPositionCloseParser ( tx , instruction , innerInstructions , offset )
2026-01-07 16:41:49 +08:00
default :
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
}
2026-04-11 08:27:34 +08:00
type dlmmInitializeAccounts struct {
pool solana . PublicKey
token0 solana . PublicKey
token1 solana . PublicKey
baseTokenProgram solana . PublicKey
quoteTokenProgram solana . PublicKey
user solana . PublicKey
}
func resolveDlmmInitializeAccounts ( result * RawTx , data [ ] byte , accounts [ ] int ) ( dlmmInitializeAccounts , error ) {
if len ( data ) < 8 {
return dlmmInitializeAccounts { } , fmt . Errorf ( "instruction data too short" )
}
accountList := result . getAccountList ( )
resolveAt := func ( position int ) ( solana . PublicKey , error ) {
if position < 0 || position >= len ( accounts ) {
return solana . PublicKey { } , fmt . Errorf ( "accounts too short, missing position %d" , position )
}
accountIndex := accounts [ position ]
if accountIndex < 0 || accountIndex >= len ( accountList ) {
return solana . PublicKey { } , fmt . Errorf ( "account index out of range at position %d" , position )
}
return accountList [ accountIndex ] , nil
}
resolveCommon := func ( poolPos , token0Pos , token1Pos , userPos , baseTokenProgramPos , quoteTokenProgramPos int ) ( dlmmInitializeAccounts , error ) {
pool , err := resolveAt ( poolPos )
if err != nil {
return dlmmInitializeAccounts { } , err
}
token0 , err := resolveAt ( token0Pos )
if err != nil {
return dlmmInitializeAccounts { } , err
}
token1 , err := resolveAt ( token1Pos )
if err != nil {
return dlmmInitializeAccounts { } , err
}
baseTokenProgram , err := resolveAt ( baseTokenProgramPos )
if err != nil {
return dlmmInitializeAccounts { } , err
}
quoteTokenProgram , err := resolveAt ( quoteTokenProgramPos )
if err != nil {
return dlmmInitializeAccounts { } , err
}
user , err := resolveAt ( userPos )
if err != nil {
return dlmmInitializeAccounts { } , err
}
return dlmmInitializeAccounts {
pool : pool ,
token0 : token0 ,
token1 : token1 ,
baseTokenProgram : baseTokenProgram ,
quoteTokenProgram : quoteTokenProgram ,
user : user ,
} , nil
}
discriminator := * ( * [ 8 ] byte ) ( data [ : 8 ] )
switch discriminator {
case meteoraInitializeLbPairDiscriminator ,
meteoraInitializeCustomizablePermissionlessLbPairDiscriminator :
return resolveCommon ( 0 , 2 , 3 , 8 , 9 , 9 )
case meteoraInitializeLbPair2Discriminator ,
meteoraInitializeCustomizablePermissionlessLbPair2Discriminator :
return resolveCommon ( 0 , 2 , 3 , 8 , 11 , 12 )
case meteoraInitializePermissionLbPairDiscriminator :
return resolveCommon ( 1 , 3 , 4 , 8 , 11 , 12 )
default :
return dlmmInitializeAccounts { } , fmt . Errorf ( "unsupported initialize discriminator" )
}
}
2026-02-02 17:59:47 +08:00
func metaoradlmmInitializeParser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
2026-04-11 08:27:34 +08:00
accounts , err := resolveDlmmInitializeAccounts ( tx . rawTx , instruction . Data , instruction . Accounts )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm initialize accounts parse error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
2026-02-02 17:59:47 +08:00
entryContract := tx . rawTx . accountList [ tx . rawTx . Transaction . Message . Instructions [ offset [ 0 ] ] . ProgramIDIndex ]
2026-04-11 08:27:34 +08:00
findMintDecimals := func ( mint solana . PublicKey ) uint8 {
for _ , acc := range tx . rawTx . Meta . PostTokenBalances {
if acc . MintAccount . Equals ( mint ) {
return uint8 ( acc . UITokenAmount . Decimals )
}
2026-02-02 17:59:47 +08:00
}
2026-04-11 08:27:34 +08:00
return 0
2026-02-02 17:59:47 +08:00
}
2026-04-11 08:27:34 +08:00
2026-02-02 17:59:47 +08:00
swap := Swap {
Program : SolProgramMeteoraDLMM ,
Event : "create" ,
2026-04-11 08:27:34 +08:00
Pool : accounts . pool ,
BaseMint : accounts . token0 ,
QuoteMint : accounts . token1 ,
BaseTokenProgram : accounts . baseTokenProgram ,
QuoteTokenProgram : accounts . quoteTokenProgram ,
2026-02-02 17:59:47 +08:00
Creator : tx . rawTx . accountList [ 0 ] ,
2026-04-11 08:27:34 +08:00
BaseMintDecimals : findMintDecimals ( accounts . token0 ) ,
QuoteMintDecimals : findMintDecimals ( accounts . token1 ) ,
User : accounts . user ,
2026-02-02 17:59:47 +08:00
EntryContract : entryContract ,
}
2026-04-11 08:27:34 +08:00
createEvent , nextOffset , found , err := dlmmLbPairCreateEventFromInnerInstructions ( innerInstructions , instruction , offset )
2026-02-02 17:59:47 +08:00
if err != nil {
2026-04-11 08:27:34 +08:00
return nil , nextOffset , err
2026-02-02 17:59:47 +08:00
}
2026-04-11 08:27:34 +08:00
if found {
offset = nextOffset
if ! createEvent . LbPair . IsZero ( ) {
swap . Pool = createEvent . LbPair
}
if ! createEvent . TokenX . IsZero ( ) {
swap . BaseMint = createEvent . TokenX
2026-02-02 17:59:47 +08:00
}
2026-04-11 08:27:34 +08:00
if ! createEvent . TokenY . IsZero ( ) {
swap . QuoteMint = createEvent . TokenY
}
swap . BaseMintDecimals = findMintDecimals ( swap . BaseMint )
swap . QuoteMintDecimals = findMintDecimals ( swap . QuoteMint )
2026-02-02 17:59:47 +08:00
}
return [ ] Swap { swap } , offset , nil
}
2026-03-19 14:10:14 +08:00
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 ] )
}
2026-03-23 15:30:43 +08:00
createEvent , nextOffset , found , err := dlmmPositionCreateEventFromInnerInstructions ( innerInstructions , instruction , offset )
2026-03-19 14:10:14 +08:00
if err != nil {
return nil , nextOffset , err
}
2026-03-23 15:30:43 +08:00
if ! found {
return nil , nextOffset , InstructionIgnoredError
}
2026-03-19 14:10:14 +08:00
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
}
2026-01-07 16:41:49 +08:00
func metaoradlmmSwapParser ( 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 ]
2026-02-09 16:09:31 +08:00
break
2026-01-07 16:41:49 +08:00
}
}
}
decode := instruction . Data
if len ( decode ) < 8 {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm swap instruction data too short, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
discriminator := * ( * [ 8 ] byte ) ( decode [ : 8 ] )
2026-04-16 14:24:14 +08:00
var swapMode SwapMode
var fixedAmount decimal . Decimal
var limitAmount decimal . Decimal
2026-01-07 16:41:49 +08:00
switch discriminator {
case meteoraDlmmSwapDiscriminator , meteoraDlmmSwap2Discriminator :
var args meteoraDlmmSwapArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm swap decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
2026-04-16 14:24:14 +08:00
swapMode = SwapModeExactIn
fixedAmount = decimal . NewFromUint64 ( args . AmountIn )
limitAmount = decimal . NewFromUint64 ( args . MinAmountOut )
2026-01-07 16:41:49 +08:00
case meteoraDlmmSwapExactOutDiscriminator , meteoraDlmmSwapExactOut2Discriminator :
var args meteoraDlmmSwapExactOutArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm swap_exact_out decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
2026-04-16 14:24:14 +08:00
swapMode = SwapModeExactOut
fixedAmount = decimal . NewFromUint64 ( args . OutAmount )
limitAmount = decimal . NewFromUint64 ( args . MaxInAmount )
2026-01-07 16:41:49 +08:00
case meteoraDlmmSwapWithPriceImpactDiscriminator , meteoraDlmmSwapWithPriceImpact2Discriminator :
var args meteoraDlmmSwapWithPriceImpactArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm swap_with_price_impact decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
2026-04-16 14:24:14 +08:00
swapMode = SwapModeExactIn
fixedAmount = decimal . NewFromUint64 ( args . AmountIn )
2026-01-07 16:41:49 +08:00
default :
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
accounts , err := resolveDlmmSwapAccounts ( result , instruction . Accounts )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm swap accounts parse error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
pool := result . accountList [ accounts . poolIdx ]
reserveXIdx := accounts . reserveXIdx
reserveYIdx := accounts . reserveYIdx
userTokenInIdx := accounts . userTokenInIdx
userTokenOutIdx := accounts . userTokenOutIdx
tokenXMint := result . accountList [ accounts . tokenXMintIdx ]
tokenYMint := result . accountList [ accounts . tokenYMintIdx ]
userIdx := accounts . userIdx
tokenXProgram := result . accountList [ accounts . tokenXProgramIdx ]
tokenYProgram := result . accountList [ accounts . tokenYProgramIdx ]
2026-02-09 14:46:19 +08:00
var prefixLen = offset [ 1 ]
var swapEvent dlmmSwapEvent
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
2026-01-07 16:41:49 +08:00
if err != nil {
2026-02-09 14:46:19 +08:00
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm swap get inner instructions error: %v, offset, %d, %d" , err , offset [ 0 ] , prefixLen )
}
for innerIndex , innerInstr := range inners {
if innerInstr . ProgramIDIndex != instruction . ProgramIDIndex {
continue
}
if len ( innerInstr . Data ) < 16 || ! bytes . Equal ( innerInstr . Data [ : 8 ] , eventDiscriminator [ : ] ) || ! bytes . Equal ( innerInstr . Data [ 8 : 16 ] , meteoraDlmmSwapEventDiscriminator [ : ] ) {
continue
}
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
}
if err := agbinary . NewBorshDecoder ( innerInstr . Data [ 16 : ] ) . Decode ( & swapEvent ) ; err != nil {
return nil , offset , fmt . Errorf ( "meteora dlmm swap event decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
break
2026-01-07 16:41:49 +08:00
}
baseMint , quoteMint , baseIsX := dlmmSelectBaseQuote ( tokenXMint , tokenYMint )
baseTokenProgram := tokenXProgram
quoteTokenProgram := tokenYProgram
baseReserveIdx := reserveXIdx
quoteReserveIdx := reserveYIdx
if ! baseIsX {
baseTokenProgram = tokenYProgram
quoteTokenProgram = tokenXProgram
baseReserveIdx = reserveYIdx
quoteReserveIdx = reserveXIdx
}
swapForY := swapEvent . SwapForY
inputIsX := swapForY
amountIn := decimal . NewFromUint64 ( swapEvent . AmountIn )
amountOut := decimal . NewFromUint64 ( swapEvent . AmountOut )
event := "buy"
if baseIsX == inputIsX {
event = "sell"
}
userBaseIdx := userTokenOutIdx
userQuoteIdx := userTokenInIdx
if baseIsX == inputIsX {
userBaseIdx = userTokenInIdx
userQuoteIdx = userTokenOutIdx
}
baseAmount := amountOut
quoteAmount := amountIn
if baseIsX {
if swapForY {
baseAmount = amountIn
quoteAmount = amountOut
}
} else {
if ! swapForY {
baseAmount = amountIn
quoteAmount = amountOut
}
}
eventUser := result . accountList [ userIdx ]
if ! swapEvent . From . IsZero ( ) {
eventUser = swapEvent . From
}
if ! eventUser . IsOnCurve ( ) && ( entryContract . Equals ( okxDexRoutersV2 ) || entryContract . Equals ( okxAggregatorV2 ) ) {
userBaseAmount , ataIndex := tokenBalanceChange ( result , 0 , baseTokenProgram , baseMint )
if ! userBaseAmount . IsZero ( ) {
eventUser = result . accountList [ 0 ]
userIdx = 0
if ataIndex > 0 {
userBaseIdx = ataIndex
}
}
}
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 := GetTokenBalanceAfterTx ( result , userIdx , quoteTokenProgram , quoteMint )
if quoteMint . Equals ( wSolMint ) {
if solAmount , err := GetSolAfterTx ( result , userIdx ) ; err == nil {
userQuote = userQuote . Add ( decimal . NewFromUint64 ( solAmount ) )
}
}
2026-04-11 08:27:34 +08:00
feeAmount , feeSide , feeMint , feeTokenProgram , feeDecimals := dlmmSwapFeeInfo (
baseIsX ,
swapForY ,
swapEvent . Fee ,
baseMint ,
quoteMint ,
baseTokenProgram ,
quoteTokenProgram ,
baseDecimals ,
quoteDecimals ,
)
lpFeeAmount := dlmmSwapLpFeeAmount ( swapEvent . Fee , swapEvent . ProtocolFee , swapEvent . HostFee )
2026-01-07 16:41:49 +08:00
swap := Swap {
Program : SolProgramMeteoraDLMM ,
Event : event ,
Pool : pool ,
BaseMint : baseMint ,
QuoteMint : quoteMint ,
BaseTokenProgram : baseTokenProgram ,
QuoteTokenProgram : quoteTokenProgram ,
Creator : solana . PublicKey { } ,
BaseMintDecimals : baseDecimals ,
QuoteMintDecimals : quoteDecimals ,
User : eventUser ,
BaseAmount : baseAmount ,
QuoteAmount : quoteAmount ,
2026-04-11 08:27:34 +08:00
FeeAmount : feeAmount ,
2026-04-11 08:34:21 +08:00
FeeBps : dlmmSwapFeeBpsString ( swapEvent . FeeBps ) ,
2026-04-11 08:27:34 +08:00
LpFeeAmount : lpFeeAmount ,
FeeSide : feeSide ,
FeeMint : feeMint ,
FeeTokenProgram : feeTokenProgram ,
FeeMintDecimals : feeDecimals ,
2026-01-07 16:41:49 +08:00
BaseReserve : baseReserve ,
QuoteReserve : quoteReserve ,
UserBaseBalance : userBase ,
UserQuoteBalance : userQuote ,
EntryContract : entryContract ,
2026-03-18 14:38:25 +08:00
ActiveBinId : swapEvent . EndBinId ,
2026-01-07 18:21:27 +08:00
StartBinId : swapEvent . StartBinId ,
EndBinId : swapEvent . EndBinId ,
2026-01-07 16:41:49 +08:00
}
2026-04-16 14:24:14 +08:00
swap . SetSwapAmountInfo ( swapMode , fixedAmount , limitAmount )
2026-01-07 16:41:49 +08:00
return [ ] Swap { swap } , offset , nil
}
func metaoradlmmSwap2Parser ( tx * Tx , instruction Instruction , innerInstructions InnerInstructions , offset [ 2 ] uint ) ( [ ] Swap , [ 2 ] uint , error ) {
return metaoradlmmSwapParser ( tx , instruction , innerInstructions , offset )
}
2026-04-11 08:27:34 +08:00
func dlmmSwapFeeInfo (
baseIsX bool ,
swapForY bool ,
fee uint64 ,
baseMint solana . PublicKey ,
quoteMint solana . PublicKey ,
baseTokenProgram solana . PublicKey ,
quoteTokenProgram solana . PublicKey ,
baseDecimals uint8 ,
quoteDecimals uint8 ,
) ( decimal . Decimal , string , solana . PublicKey , solana . PublicKey , uint8 ) {
feeAmount := decimal . NewFromUint64 ( fee )
if baseIsX == swapForY {
return feeAmount , "base" , baseMint , baseTokenProgram , baseDecimals
}
return feeAmount , "quote" , quoteMint , quoteTokenProgram , quoteDecimals
}
func dlmmSwapLpFeeAmount ( fee , protocolFee , hostFee uint64 ) decimal . Decimal {
total := decimal . NewFromUint64 ( fee )
protocol := decimal . NewFromUint64 ( protocolFee )
host := decimal . NewFromUint64 ( hostFee )
lpFee := total . Sub ( protocol ) . Sub ( host )
if lpFee . IsNegative ( ) {
return decimal . Zero
}
return lpFee
}
2026-04-11 08:34:21 +08:00
func dlmmSwapFeeBpsString ( feeBps agbinary . Uint128 ) string {
return feeBps . DecimalString ( )
}
2026-01-15 17:44:39 +08:00
func metaoradlmmAddLiquidityParser ( 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 ]
2026-02-09 16:09:31 +08:00
break
2026-01-15 17:44:39 +08:00
}
}
}
decode := instruction . Data
if len ( decode ) < 8 {
offset [ 1 ] += 1
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity instruction data too short, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
discriminator := * ( * [ 8 ] byte ) ( decode [ : 8 ] )
var (
2026-01-15 17:45:17 +08:00
amountX uint64
amountY uint64
binDist [ ] dlmmBinLiquidityDistribution
2026-03-20 17:06:37 +08:00
weightDist [ ] dlmmBinLiquidityDistributionByWeight
2026-01-15 17:44:39 +08:00
startBinId int32
endBinId int32
2026-03-25 11:34:46 +08:00
oneSide bool
2026-01-15 17:44:39 +08:00
)
switch discriminator {
case meteoraDlmmAddLiquidityDiscriminator :
var args dlmmAddLiquidityArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
amountX = args . LiquidityParameter . AmountX
amountY = args . LiquidityParameter . AmountY
binDist = args . LiquidityParameter . BinLiquidityDist
startBinId , endBinId = dlmmMinMaxBinIdFromDistribution ( binDist )
case meteoraDlmmAddLiquidity2Discriminator :
var args dlmmAddLiquidity2Args
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity2 decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
amountX = args . LiquidityParameter . AmountX
amountY = args . LiquidityParameter . AmountY
binDist = args . LiquidityParameter . BinLiquidityDist
startBinId , endBinId = dlmmMinMaxBinIdFromDistribution ( binDist )
case meteoraDlmmAddLiquidityByStrategyDiscriminator :
var args dlmmAddLiquidityByStrategyArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity by strategy decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
amountX = args . LiquidityParameter . AmountX
amountY = args . LiquidityParameter . AmountY
startBinId = args . LiquidityParameter . StrategyParameters . MinBinId
endBinId = args . LiquidityParameter . StrategyParameters . MaxBinId
case meteoraDlmmAddLiquidityByStrategy2Discriminator :
var args dlmmAddLiquidityByStrategy2Args
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity by strategy2 decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
amountX = args . LiquidityParameter . AmountX
amountY = args . LiquidityParameter . AmountY
startBinId = args . LiquidityParameter . StrategyParameters . MinBinId
endBinId = args . LiquidityParameter . StrategyParameters . MaxBinId
2026-03-20 17:06:37 +08:00
case meteoraDlmmAddLiquidityByWeightDiscriminator :
var args dlmmAddLiquidityByWeightArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity by weight decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
amountX = args . LiquidityParameter . AmountX
amountY = args . LiquidityParameter . AmountY
weightDist = args . LiquidityParameter . BinLiquidityDist
startBinId , endBinId = dlmmMinMaxBinIdFromWeightDistribution ( weightDist )
2026-03-25 11:34:46 +08:00
case meteoraDlmmAddLiquidityOneSideDiscriminator :
var args dlmmAddLiquidityOneSideArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity one side decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
weightDist = args . LiquidityParameter . BinLiquidityDist
startBinId , endBinId = dlmmMinMaxBinIdFromWeightDistribution ( weightDist )
oneSide = true
case meteoraDlmmAddLiquidityOneSidePreciseDiscriminator :
var args dlmmAddLiquidityOneSidePreciseArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity one side precise decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId , endBinId = dlmmMinMaxBinIDFromCompressedDeposits ( args . Parameter . Bins )
oneSide = true
case meteoraDlmmAddLiquidityOneSidePrecise2Discriminator :
var args dlmmAddLiquidityOneSidePrecise2Args
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity one side precise2 decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId , endBinId = dlmmMinMaxBinIDFromCompressedDeposits ( args . LiquidityParameter . Bins )
oneSide = true
case meteoraDlmmAddLiquidityByStrategyOneSideDiscriminator :
var args dlmmAddLiquidityByStrategyOneSideArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity by strategy one side decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId = args . LiquidityParameter . StrategyParameters . MinBinId
endBinId = args . LiquidityParameter . StrategyParameters . MaxBinId
oneSide = true
2026-01-15 17:44:39 +08:00
default :
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
addEvent , nextOffset , err := dlmmAddLiquidityEventFromInnerInstructions ( innerInstructions , instruction , offset )
if err != nil {
return nil , nextOffset , err
}
offset = nextOffset
amountX = addEvent . Amounts [ 0 ]
amountY = addEvent . Amounts [ 1 ]
2026-03-25 11:34:46 +08:00
if oneSide {
2026-04-11 08:27:34 +08:00
swaps , err := dlmmBuildOneSideAddSwap ( tx , instruction , addEvent , startBinId , endBinId , entryContract )
2026-03-25 11:34:46 +08:00
if err != nil {
return nil , offset , err
}
return swaps , offset , nil
}
accounts , err := resolveDlmmLiquidityAccounts ( result , instruction . Accounts )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity accounts parse error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
2026-01-15 17:44:39 +08:00
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
if ! baseIsX {
baseTokenProgram = tokenYProgram
quoteTokenProgram = tokenXProgram
baseReserveIdx = accounts . reserveYIdx
quoteReserveIdx = accounts . reserveXIdx
userBaseIdx = accounts . userTokenYIdx
userQuoteIdx = accounts . userTokenXIdx
}
amountXDec := decimal . NewFromUint64 ( amountX )
amountYDec := decimal . NewFromUint64 ( amountY )
baseAmount := amountXDec
quoteAmount := amountYDec
if ! baseIsX {
baseAmount = amountYDec
quoteAmount = amountXDec
}
eventUser := result . accountList [ accounts . userIdx ]
if ! addEvent . From . IsZero ( ) {
eventUser = addEvent . From
}
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 ,
2026-03-16 10:14:50 +08:00
Event : "add" ,
2026-01-15 17:44:39 +08:00
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 ,
2026-03-18 14:38:25 +08:00
ActiveBinId : addEvent . ActiveBinId ,
2026-01-15 17:44:39 +08:00
StartBinId : startBinId ,
EndBinId : endBinId ,
2026-03-19 14:10:14 +08:00
PositionAccount : result . accountList [ accounts . positionIdx ] ,
2026-01-15 17:44:39 +08:00
}
return [ ] Swap { swap } , offset , nil
}
func metaoradlmmRemoveLiquidityParser ( 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 ]
2026-02-09 16:09:31 +08:00
break
2026-01-15 17:44:39 +08:00
}
}
}
decode := instruction . Data
if len ( decode ) < 8 {
offset [ 1 ] += 1
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity instruction data too short, offset, %d, %d" , offset [ 0 ] , offset [ 1 ] )
}
discriminator := * ( * [ 8 ] byte ) ( decode [ : 8 ] )
var (
startBinId int32
endBinId int32
2026-03-18 14:38:25 +08:00
removeBp int32
2026-01-15 17:44:39 +08:00
)
switch discriminator {
2026-03-25 11:34:46 +08:00
case meteoraDlmmRemoveAllLiquidityDiscriminator :
removeBp = 10000
2026-01-15 17:44:39 +08:00
case meteoraDlmmRemoveLiquidityDiscriminator :
var args dlmmRemoveLiquidityArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId , endBinId = dlmmMinMaxBinIdFromReduction ( args . BinLiquidityRemoval )
2026-03-18 14:38:25 +08:00
removeBp = dlmmCommonRemoveBp ( args . BinLiquidityRemoval )
2026-01-15 17:44:39 +08:00
case meteoraDlmmRemoveLiquidity2Discriminator :
var args dlmmRemoveLiquidity2Args
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity2 decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId , endBinId = dlmmMinMaxBinIdFromReduction ( args . BinLiquidityRemoval )
2026-03-18 14:38:25 +08:00
removeBp = dlmmCommonRemoveBp ( args . BinLiquidityRemoval )
2026-01-15 17:44:39 +08:00
case meteoraDlmmRemoveLiquidityByRangeDiscriminator :
var args dlmmRemoveLiquidityByRangeArgs
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity by range decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId = args . FromBinId
endBinId = args . ToBinId
2026-03-18 14:38:25 +08:00
removeBp = int32 ( args . BpsToRemove )
2026-01-15 17:44:39 +08:00
case meteoraDlmmRemoveLiquidityByRange2Discriminator :
var args dlmmRemoveLiquidityByRange2Args
if err := agbinary . NewBorshDecoder ( decode [ 8 : ] ) . Decode ( & args ) ; err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity by range2 decode error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
startBinId = args . FromBinId
endBinId = args . ToBinId
2026-03-18 14:38:25 +08:00
removeBp = int32 ( args . BpsToRemove )
2026-01-15 17:44:39 +08:00
default :
return nil , increaseOffset ( offset ) , InstructionIgnoredError
}
accounts , err := resolveDlmmLiquidityAccounts ( result , instruction . Accounts )
if err != nil {
return nil , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity accounts parse error: %v, offset, %d, %d" , err , offset [ 0 ] , offset [ 1 ] )
}
removeEvent , nextOffset , err := dlmmRemoveLiquidityEventFromInnerInstructions ( 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
if ! baseIsX {
baseTokenProgram = tokenYProgram
quoteTokenProgram = tokenXProgram
baseReserveIdx = accounts . reserveYIdx
quoteReserveIdx = accounts . reserveXIdx
userBaseIdx = accounts . userTokenYIdx
userQuoteIdx = accounts . userTokenXIdx
}
amountXDec := decimal . NewFromUint64 ( removeEvent . Amounts [ 0 ] )
amountYDec := decimal . NewFromUint64 ( removeEvent . Amounts [ 1 ] )
baseAmount := amountXDec
quoteAmount := amountYDec
if ! baseIsX {
baseAmount = amountYDec
quoteAmount = amountXDec
}
eventUser := result . accountList [ accounts . userIdx ]
if ! removeEvent . From . IsZero ( ) {
eventUser = removeEvent . From
}
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 ,
2026-03-16 10:14:50 +08:00
Event : "remove" ,
2026-01-15 17:44:39 +08:00
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 ,
2026-03-18 14:38:25 +08:00
ActiveBinId : removeEvent . ActiveBinId ,
2026-01-15 17:44:39 +08:00
StartBinId : startBinId ,
EndBinId : endBinId ,
2026-03-18 14:38:25 +08:00
RemoveBp : removeBp ,
2026-03-19 14:10:14 +08:00
PositionAccount : result . accountList [ accounts . positionIdx ] ,
2026-01-15 17:44:39 +08:00
}
return [ ] Swap { swap } , offset , nil
}
2026-03-16 11:37:19 +08:00
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 ,
2026-03-19 14:10:14 +08:00
PositionAccount : result . accountList [ accounts . positionIdx ] ,
2026-03-16 11:37:19 +08:00
}
if claimEvent . HasActiveBin {
2026-03-18 14:38:25 +08:00
swap . ActiveBinId = claimEvent . ActiveBinId
2026-03-16 11:37:19 +08:00
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 ,
2026-03-18 14:38:25 +08:00
ActiveBinId : event . ActiveBinId ,
2026-03-16 11:37:19 +08:00
StartBinId : event . OldMinBinId ,
EndBinId : event . OldMaxBinId ,
2026-03-19 14:10:14 +08:00
PositionAccount : result . accountList [ accounts . positionIdx ] ,
2026-03-16 11:37:19 +08:00
} )
}
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 ,
2026-03-18 14:38:25 +08:00
ActiveBinId : event . ActiveBinId ,
2026-03-16 11:37:19 +08:00
StartBinId : event . NewMinBinId ,
EndBinId : event . NewMaxBinId ,
2026-03-19 14:10:14 +08:00
PositionAccount : result . accountList [ accounts . positionIdx ] ,
2026-03-16 11:37:19 +08:00
} )
}
if len ( swaps ) == 0 {
return nil , offset , InstructionIgnoredError
}
return swaps , offset , nil
}
2026-01-07 16:41:49 +08:00
func dlmmSelectBaseQuote ( tokenX , tokenY solana . PublicKey ) ( baseMint solana . PublicKey , quoteMint solana . PublicKey , baseIsX bool ) {
priority := [ ] solana . PublicKey { wSolMint , usdcMint , usd1Mint }
for _ , mint := range priority {
if tokenX . Equals ( mint ) {
return tokenY , tokenX , false
}
if tokenY . Equals ( mint ) {
return tokenX , tokenY , true
}
}
return tokenX , tokenY , true
}
2026-01-15 17:44:39 +08:00
func dlmmAddLiquidityEventFromInnerInstructions ( innerInstructions InnerInstructions , instruction Instruction , offset [ 2 ] uint ) ( dlmmAddLiquidityEvent , [ 2 ] uint , error ) {
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return dlmmAddLiquidityEvent { } , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add 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 := dlmmDecodeAddLiquidityEvent ( innerInstr . Data )
if ! ok {
continue
}
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2026-01-15 17:44:39 +08:00
}
return event , offset , nil
}
return dlmmAddLiquidityEvent { } , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm add liquidity event not found, offset, %d, %d" , offset [ 0 ] , prefixLen )
}
func dlmmRemoveLiquidityEventFromInnerInstructions ( innerInstructions InnerInstructions , instruction Instruction , offset [ 2 ] uint ) ( dlmmRemoveLiquidityEvent , [ 2 ] uint , error ) {
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return dlmmRemoveLiquidityEvent { } , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove 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 := dlmmDecodeRemoveLiquidityEvent ( innerInstr . Data )
if ! ok {
continue
}
if offset [ 1 ] == 0 {
offset [ 0 ] += 1
} else {
2026-02-09 14:46:19 +08:00
offset [ 1 ] = uint ( innerIndex ) + 1 + prefixLen
2026-01-15 17:44:39 +08:00
}
return event , offset , nil
}
return dlmmRemoveLiquidityEvent { } , increaseOffset ( offset ) , fmt . Errorf ( "meteora dlmm remove liquidity event not found, offset, %d, %d" , offset [ 0 ] , prefixLen )
}
2026-03-16 11:37:19 +08:00
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 )
}
2026-03-23 15:30:43 +08:00
func dlmmPositionCreateEventFromInnerInstructions ( innerInstructions InnerInstructions , instruction Instruction , offset [ 2 ] uint ) ( dlmmPositionCreateEvent , [ 2 ] uint , bool , error ) {
2026-03-19 14:10:14 +08:00
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
2026-03-23 15:30:43 +08:00
return dlmmPositionCreateEvent { } , increaseOffset ( offset ) , false , fmt . Errorf ( "meteora dlmm create position get inner instructions error: %v, offset, %d, %d" , err , offset [ 0 ] , prefixLen )
2026-03-19 14:10:14 +08:00
}
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
}
2026-03-23 15:30:43 +08:00
return event , offset , true , nil
2026-03-19 14:10:14 +08:00
}
2026-03-23 15:30:43 +08:00
return dlmmPositionCreateEvent { } , increaseOffset ( offset ) , false , nil
2026-03-19 14:10:14 +08:00
}
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
}
2026-04-11 08:27:34 +08:00
func dlmmLbPairCreateEventFromInnerInstructions ( innerInstructions InnerInstructions , instruction Instruction , offset [ 2 ] uint ) ( dlmmLbPairCreateEvent , [ 2 ] uint , bool , error ) {
var prefixLen = offset [ 1 ]
inners , err := getInnerInstructions ( innerInstructions , prefixLen )
if err != nil {
return dlmmLbPairCreateEvent { } , increaseOffset ( offset ) , false , fmt . Errorf ( "meteora dlmm create 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 := dlmmDecodeLbPairCreateEvent ( 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 dlmmLbPairCreateEvent { } , increaseOffset ( offset ) , false , nil
}
func dlmmDecodeLbPairCreateEvent ( data [ ] byte ) ( dlmmLbPairCreateEvent , bool ) {
switch {
case len ( data ) >= 8 && bytes . Equal ( data [ : 8 ] , meteoraInitializeLbPairEventDiscriminator [ : ] ) :
var event dlmmLbPairCreateEvent
if err := agbinary . NewBorshDecoder ( data [ 8 : ] ) . Decode ( & event ) ; err != nil {
return dlmmLbPairCreateEvent { } , false
}
return event , true
case len ( data ) >= 16 &&
bytes . Equal ( data [ : 8 ] , eventDiscriminator [ : ] ) &&
bytes . Equal ( data [ 8 : 16 ] , meteoraInitializeLbPairEventDiscriminator [ : ] ) :
var event dlmmLbPairCreateEvent
if err := agbinary . NewBorshDecoder ( data [ 16 : ] ) . Decode ( & event ) ; err != nil {
return dlmmLbPairCreateEvent { } , false
}
return event , true
default :
return dlmmLbPairCreateEvent { } , false
}
}
2026-01-15 17:44:39 +08:00
func dlmmDecodeAddLiquidityEvent ( data [ ] byte ) ( dlmmAddLiquidityEvent , bool ) {
switch {
case len ( data ) >= 8 && bytes . Equal ( data [ : 8 ] , meteoraDlmmAddLiquidityEventDiscriminator [ : ] ) :
var event dlmmAddLiquidityEvent
if err := agbinary . NewBorshDecoder ( data [ 8 : ] ) . Decode ( & event ) ; err != nil {
return dlmmAddLiquidityEvent { } , false
}
return event , true
case len ( data ) >= 16 &&
bytes . Equal ( data [ : 8 ] , eventDiscriminator [ : ] ) &&
bytes . Equal ( data [ 8 : 16 ] , meteoraDlmmAddLiquidityEventDiscriminator [ : ] ) :
var event dlmmAddLiquidityEvent
if err := agbinary . NewBorshDecoder ( data [ 16 : ] ) . Decode ( & event ) ; err != nil {
return dlmmAddLiquidityEvent { } , false
}
return event , true
default :
return dlmmAddLiquidityEvent { } , false
}
}
func dlmmDecodeRemoveLiquidityEvent ( data [ ] byte ) ( dlmmRemoveLiquidityEvent , bool ) {
switch {
case len ( data ) >= 8 && bytes . Equal ( data [ : 8 ] , meteoraDlmmRemoveLiquidityEventDiscriminator [ : ] ) :
var event dlmmRemoveLiquidityEvent
if err := agbinary . NewBorshDecoder ( data [ 8 : ] ) . Decode ( & event ) ; err != nil {
return dlmmRemoveLiquidityEvent { } , false
}
return event , true
case len ( data ) >= 16 &&
bytes . Equal ( data [ : 8 ] , eventDiscriminator [ : ] ) &&
bytes . Equal ( data [ 8 : 16 ] , meteoraDlmmRemoveLiquidityEventDiscriminator [ : ] ) :
var event dlmmRemoveLiquidityEvent
if err := agbinary . NewBorshDecoder ( data [ 16 : ] ) . Decode ( & event ) ; err != nil {
return dlmmRemoveLiquidityEvent { } , false
}
return event , true
default :
return dlmmRemoveLiquidityEvent { } , false
}
}
2026-03-19 14:10:14 +08:00
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
}
}
2026-03-16 11:37:19 +08:00
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
}
}
2026-03-19 14:10:14 +08:00
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" )
}
}
2026-01-07 16:41:49 +08:00
func resolveDlmmSwapAccounts ( result * RawTx , accounts [ ] int ) ( dlmmSwapAccounts , error ) {
if len ( accounts ) < 13 {
return dlmmSwapAccounts { } , fmt . Errorf ( "accounts too short, expected at least 13" )
}
accountList := result . accountList
basePosCandidates := [ ] int { 1 , 2 }
for _ , basePos := range basePosCandidates {
if basePos + 6 >= len ( accounts ) {
continue
}
oraclePos := basePos + 6
userPos := oraclePos + 1
hostFeePresent := true
if userPos < len ( accounts ) && dlmmIsSigner ( result , accounts [ userPos ] ) {
hostFeePresent = false
} else {
userPos = oraclePos + 2
}
if userPos + 2 >= len ( accounts ) {
continue
}
tokenXProgramPos := userPos + 1
tokenYProgramPos := userPos + 2
eventAuthorityPos := tokenYProgramPos + 1
if eventAuthorityPos < len ( accounts ) && accountList [ accounts [ eventAuthorityPos ] ] . Equals ( solana . MemoProgramID ) {
eventAuthorityPos ++
}
2026-04-27 14:36:03 +08:00
if eventAuthorityPos >= len ( accounts ) {
2026-01-07 16:41:49 +08:00
continue
}
if ! accountList [ accounts [ eventAuthorityPos ] ] . Equals ( meteoraDlmmEventAuthority ) {
continue
}
if hostFeePresent && oraclePos + 1 < len ( accounts ) && dlmmIsSigner ( result , accounts [ oraclePos + 1 ] ) {
continue
}
return dlmmSwapAccounts {
poolIdx : accounts [ 0 ] ,
reserveXIdx : accounts [ oraclePos - 6 ] ,
reserveYIdx : accounts [ oraclePos - 5 ] ,
userTokenInIdx : accounts [ oraclePos - 4 ] ,
userTokenOutIdx : accounts [ oraclePos - 3 ] ,
tokenXMintIdx : accounts [ oraclePos - 2 ] ,
tokenYMintIdx : accounts [ oraclePos - 1 ] ,
oracleIdx : accounts [ oraclePos ] ,
userIdx : accounts [ userPos ] ,
tokenXProgramIdx : accounts [ tokenXProgramPos ] ,
tokenYProgramIdx : accounts [ tokenYProgramPos ] ,
} , nil
}
return dlmmSwapAccounts { } , fmt . Errorf ( "accounts layout invalid" )
}
2026-01-15 17:44:39 +08:00
func resolveDlmmLiquidityAccounts ( result * RawTx , accounts [ ] int ) ( dlmmLiquidityAccounts , error ) {
if len ( accounts ) < 9 {
return dlmmLiquidityAccounts { } , fmt . Errorf ( "accounts too short, expected at least 9" )
}
accountList := result . accountList
eventAuthorityPos := - 1
for i , idx := range accounts {
if idx < 0 || idx >= len ( accountList ) {
continue
}
if accountList [ idx ] . Equals ( meteoraDlmmEventAuthority ) {
eventAuthorityPos = i
break
}
}
if eventAuthorityPos == - 1 {
return dlmmLiquidityAccounts { } , fmt . Errorf ( "event authority not found" )
}
if eventAuthorityPos + 1 >= len ( accounts ) || ! accountList [ accounts [ eventAuthorityPos + 1 ] ] . Equals ( meteoraDlmmProgram ) {
return dlmmLiquidityAccounts { } , fmt . Errorf ( "program id not found after event authority" )
}
tokenYProgramPos := eventAuthorityPos - 1
if tokenYProgramPos < 0 {
return dlmmLiquidityAccounts { } , fmt . Errorf ( "token program position invalid" )
}
if accountList [ accounts [ tokenYProgramPos ] ] . Equals ( solana . MemoProgramID ) {
tokenYProgramPos --
}
if tokenYProgramPos < 1 {
return dlmmLiquidityAccounts { } , fmt . Errorf ( "token program positions invalid" )
}
tokenXProgramPos := tokenYProgramPos - 1
userPos := tokenXProgramPos - 1
if userPos < 0 {
return dlmmLiquidityAccounts { } , fmt . Errorf ( "user position invalid" )
}
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 [ userPos ] ,
tokenXProgramIdx : accounts [ tokenXProgramPos ] ,
tokenYProgramIdx : accounts [ tokenYProgramPos ] ,
} , nil
}
2026-03-25 11:34:46 +08:00
func resolveDlmmOneSideLiquidityAccounts ( result * RawTx , accounts [ ] int ) ( dlmmOneSideLiquidityAccounts , error ) {
if len ( accounts ) < 10 {
return dlmmOneSideLiquidityAccounts { } , fmt . Errorf ( "accounts too short, expected at least 10" )
}
accountList := result . accountList
eventAuthorityPos := - 1
for i , idx := range accounts {
if idx < 0 || idx >= len ( accountList ) {
continue
}
if accountList [ idx ] . Equals ( meteoraDlmmEventAuthority ) {
eventAuthorityPos = i
break
}
}
if eventAuthorityPos == - 1 {
return dlmmOneSideLiquidityAccounts { } , fmt . Errorf ( "event authority not found" )
}
if eventAuthorityPos + 1 >= len ( accounts ) || ! accountList [ accounts [ eventAuthorityPos + 1 ] ] . Equals ( meteoraDlmmProgram ) {
return dlmmOneSideLiquidityAccounts { } , fmt . Errorf ( "program id not found after event authority" )
}
tokenProgramPos := eventAuthorityPos - 1
userPos := eventAuthorityPos - 2
if tokenProgramPos < 0 || userPos < 0 {
return dlmmOneSideLiquidityAccounts { } , fmt . Errorf ( "one side liquidity account positions invalid" )
}
if len ( accounts ) < 6 {
return dlmmOneSideLiquidityAccounts { } , fmt . Errorf ( "accounts too short for one side liquidity parsing" )
}
return dlmmOneSideLiquidityAccounts {
positionIdx : accounts [ 0 ] ,
poolIdx : accounts [ 1 ] ,
userTokenIdx : accounts [ 3 ] ,
reserveIdx : accounts [ 4 ] ,
tokenMintIdx : accounts [ 5 ] ,
userIdx : accounts [ userPos ] ,
tokenProgramIdx : accounts [ tokenProgramPos ] ,
} , nil
}
func dlmmBuildOneSideAddSwap (
tx * Tx ,
instruction Instruction ,
addEvent dlmmAddLiquidityEvent ,
startBinId int32 ,
endBinId int32 ,
entryContract solana . PublicKey ,
) ( [ ] Swap , error ) {
result := tx . rawTx
accounts , err := resolveDlmmOneSideLiquidityAccounts ( result , instruction . Accounts )
if err != nil {
return nil , err
}
knownMint := result . accountList [ accounts . tokenMintIdx ]
knownTokenProgram := result . accountList [ accounts . tokenProgramIdx ]
knownDecimals , ok := dlmmTokenDecimals ( result , accounts . reserveIdx )
if ! ok {
knownDecimals , _ = dlmmTokenDecimals ( result , accounts . userTokenIdx )
}
knownReserveBalance := getAccountBalanceAfterTx ( result , accounts . reserveIdx )
knownUserBalance := getAccountBalanceAfterTx ( result , accounts . userTokenIdx )
if knownMint . Equals ( wSolMint ) {
if solAmount , err := GetSolAfterTx ( result , accounts . userIdx ) ; err == nil {
knownUserBalance = knownUserBalance . Add ( decimal . NewFromUint64 ( solAmount ) )
}
}
eventUser := result . accountList [ accounts . userIdx ]
if ! addEvent . From . IsZero ( ) {
eventUser = addEvent . From
}
positionAccount := result . accountList [ accounts . positionIdx ]
if ! addEvent . Position . IsZero ( ) {
positionAccount = addEvent . Position
}
swap := Swap {
Program : SolProgramMeteoraDLMM ,
Event : "add" ,
Pool : result . accountList [ accounts . poolIdx ] ,
User : eventUser ,
EntryContract : entryContract ,
ActiveBinId : addEvent . ActiveBinId ,
StartBinId : startBinId ,
EndBinId : endBinId ,
PositionAccount : positionAccount ,
}
knownIsX := dlmmInferOneSideLiquidityAxis ( result , accounts , addEvent )
if knownIsX {
swap . BaseMint = knownMint
swap . BaseTokenProgram = knownTokenProgram
swap . BaseMintDecimals = knownDecimals
swap . BaseAmount = decimal . NewFromUint64 ( addEvent . Amounts [ 0 ] )
swap . BaseReserve = knownReserveBalance
swap . UserBaseBalance = knownUserBalance
if _ , exists := tx . Token [ knownMint ] ; ! exists && ! knownMint . Equals ( wSolMint ) {
tx . Token [ knownMint ] = TokenMeta {
Mint : knownMint ,
Decimals : knownDecimals ,
TokenProgram : knownTokenProgram ,
}
}
} else {
swap . QuoteMint = knownMint
swap . QuoteTokenProgram = knownTokenProgram
swap . QuoteMintDecimals = knownDecimals
swap . QuoteAmount = decimal . NewFromUint64 ( addEvent . Amounts [ 1 ] )
swap . QuoteReserve = knownReserveBalance
swap . UserQuoteBalance = knownUserBalance
if _ , exists := tx . Token [ knownMint ] ; ! exists && ! knownMint . Equals ( wSolMint ) {
tx . Token [ knownMint ] = TokenMeta {
Mint : knownMint ,
Decimals : knownDecimals ,
TokenProgram : knownTokenProgram ,
}
}
}
return [ ] Swap { swap } , nil
}
func dlmmInferOneSideLiquidityAxis ( result * RawTx , accounts dlmmOneSideLiquidityAccounts , addEvent dlmmAddLiquidityEvent ) bool {
knownAmount , ok := dlmmTokenDelta ( result , accounts . reserveIdx )
if ! ok || knownAmount . IsZero ( ) {
knownAmount , _ = dlmmTokenDelta ( result , accounts . userTokenIdx )
}
amountX := decimal . NewFromUint64 ( addEvent . Amounts [ 0 ] )
amountY := decimal . NewFromUint64 ( addEvent . Amounts [ 1 ] )
switch {
case ! knownAmount . IsZero ( ) && knownAmount . Equal ( amountX ) && ! knownAmount . Equal ( amountY ) :
return true
case ! knownAmount . IsZero ( ) && knownAmount . Equal ( amountY ) && ! knownAmount . Equal ( amountX ) :
return false
case addEvent . Amounts [ 0 ] > 0 && addEvent . Amounts [ 1 ] == 0 :
return true
case addEvent . Amounts [ 1 ] > 0 && addEvent . Amounts [ 0 ] == 0 :
return false
default :
return true
}
}
2026-03-16 11:37:19 +08:00
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
}
2026-01-07 16:41:49 +08:00
func dlmmTokenDecimals ( result * RawTx , accountIndex int ) ( uint8 , bool ) {
for _ , meta := range result . Meta . PostTokenBalances {
if meta . AccountIndex == accountIndex {
return uint8 ( meta . UITokenAmount . Decimals ) , true
}
}
for _ , meta := range result . Meta . PreTokenBalances {
if meta . AccountIndex == accountIndex {
return uint8 ( meta . UITokenAmount . Decimals ) , true
}
}
return 0 , false
}
func dlmmTokenDelta ( result * RawTx , accountIndex int ) ( decimal . Decimal , bool ) {
before , okBefore := dlmmTokenAmount ( result , accountIndex , false )
after , okAfter := dlmmTokenAmount ( result , accountIndex , true )
if ! okBefore && ! okAfter {
return decimal . Zero , false
}
if ! okBefore {
before = decimal . Zero
}
if ! okAfter {
after = decimal . Zero
}
return after . Sub ( before ) . Abs ( ) , true
}
func dlmmTokenDeltaSigned ( result * RawTx , accountIndex int ) ( decimal . Decimal , bool ) {
before , okBefore := dlmmTokenAmount ( result , accountIndex , false )
after , okAfter := dlmmTokenAmount ( result , accountIndex , true )
if ! okBefore && ! okAfter {
return decimal . Zero , false
}
if ! okBefore {
before = decimal . Zero
}
if ! okAfter {
after = decimal . Zero
}
return after . Sub ( before ) , true
}
func dlmmTokenAmount ( result * RawTx , accountIndex int , post bool ) ( decimal . Decimal , bool ) {
var balances [ ] TokenBalance
if post {
balances = result . Meta . PostTokenBalances
} else {
balances = result . Meta . PreTokenBalances
}
for _ , meta := range balances {
if meta . AccountIndex == accountIndex {
amount , err := decimal . NewFromString ( meta . UITokenAmount . Amount )
if err != nil {
return decimal . Zero , false
}
return amount , true
}
}
return decimal . Zero , false
}
func dlmmIsSigner ( result * RawTx , accountIndex int ) bool {
if accountIndex < 0 || accountIndex >= len ( result . Transaction . Message . AccountKeys ) {
return false
}
return accountIndex < result . Transaction . Message . Header . NumRequiredSignatures
}
func dlmmTokenBalanceMeta ( result * RawTx , accountIndex int ) ( TokenBalance , bool ) {
for _ , meta := range result . Meta . PostTokenBalances {
if meta . AccountIndex == accountIndex {
return meta , true
}
}
for _ , meta := range result . Meta . PreTokenBalances {
if meta . AccountIndex == accountIndex {
return meta , true
}
}
return TokenBalance { } , false
}
2026-01-15 17:44:39 +08:00
2026-04-11 08:27:34 +08:00
func dlmmAllocateByWeights ( total uint64 , weights [ ] uint64 ) [ ] decimal . Decimal {
if len ( weights ) == 0 {
2026-01-15 17:44:39 +08:00
return nil
}
2026-04-11 08:27:34 +08:00
sumWeights := uint64 ( 0 )
for _ , weight := range weights {
sumWeights += weight
2026-01-15 17:44:39 +08:00
}
2026-04-11 08:27:34 +08:00
if sumWeights == 0 {
sumWeights = uint64 ( len ( weights ) )
weights = append ( [ ] uint64 ( nil ) , weights ... )
for i := range weights {
weights [ i ] = 1
}
2026-01-15 17:44:39 +08:00
}
2026-04-11 08:27:34 +08:00
allocations := make ( [ ] decimal . Decimal , len ( weights ) )
remaining := total
for i , weight := range weights {
amount := uint64 ( 0 )
if i == len ( weights ) - 1 {
amount = remaining
} else if sumWeights > 0 {
amount = total * weight / sumWeights
if amount > remaining {
amount = remaining
}
remaining -= amount
}
allocations [ i ] = decimal . NewFromUint64 ( amount )
2026-01-15 17:44:39 +08:00
}
2026-04-11 08:27:34 +08:00
return allocations
}
func dlmmApplySignedAllocation ( values [ ] decimal . Decimal , negative bool ) [ ] decimal . Decimal {
if ! negative {
return values
2026-01-15 17:44:39 +08:00
}
2026-04-11 08:27:34 +08:00
out := make ( [ ] decimal . Decimal , len ( values ) )
for i , value := range values {
out [ i ] = value . Neg ( )
2026-01-15 17:44:39 +08:00
}
2026-04-11 08:27:34 +08:00
return out
2026-01-15 17:44:39 +08:00
}
2026-03-25 11:34:46 +08:00
func dlmmMinMaxBinIDFromCompressedDeposits ( bins [ ] dlmmCompressedBinDepositAmount ) ( startBinID , endBinID int32 ) {
if len ( bins ) == 0 {
return 0 , 0
}
startBinID = bins [ 0 ] . BinID
endBinID = bins [ 0 ] . BinID
for _ , bin := range bins [ 1 : ] {
if bin . BinID < startBinID {
startBinID = bin . BinID
}
if bin . BinID > endBinID {
endBinID = bin . BinID
}
}
return startBinID , endBinID
}
2026-03-18 14:38:25 +08:00
func dlmmCommonRemoveBp ( reduction [ ] dlmmBinLiquidityReduction ) int32 {
if len ( reduction ) == 0 {
return 0
}
bpsToRemove := reduction [ 0 ] . BpsToRemove
for _ , item := range reduction [ 1 : ] {
if item . BpsToRemove != bpsToRemove {
return 0
}
}
return int32 ( bpsToRemove )
}
2026-03-19 14:10:14 +08:00
func dlmmPositionUpperBinId ( lowerBinId , width int32 ) int32 {
if width <= 0 {
return lowerBinId
}
return lowerBinId + width - 1
}
2026-01-15 17:44:39 +08:00
func dlmmMinMaxBinIdFromDistribution ( dist [ ] dlmmBinLiquidityDistribution ) ( int32 , int32 ) {
if len ( dist ) == 0 {
return 0 , 0
}
min := dist [ 0 ] . BinId
max := dist [ 0 ] . BinId
for _ , item := range dist [ 1 : ] {
if item . BinId < min {
min = item . BinId
}
if item . BinId > max {
max = item . BinId
}
}
return min , max
}
2026-03-20 17:06:37 +08:00
func dlmmMinMaxBinIdFromWeightDistribution ( dist [ ] dlmmBinLiquidityDistributionByWeight ) ( int32 , int32 ) {
if len ( dist ) == 0 {
return 0 , 0
}
min := dist [ 0 ] . BinId
max := dist [ 0 ] . BinId
for _ , item := range dist [ 1 : ] {
if item . BinId < min {
min = item . BinId
}
if item . BinId > max {
max = item . BinId
}
}
return min , max
}
2026-01-15 17:44:39 +08:00
func dlmmMinMaxBinIdFromReduction ( reduction [ ] dlmmBinLiquidityReduction ) ( int32 , int32 ) {
if len ( reduction ) == 0 {
return 0 , 0
}
min := reduction [ 0 ] . BinId
max := reduction [ 0 ] . BinId
for _ , item := range reduction [ 1 : ] {
if item . BinId < min {
min = item . BinId
}
if item . BinId > max {
max = item . BinId
}
}
return min , max
}