This commit is contained in:
bijianing97
2026-01-15 17:44:39 +08:00
parent b76d2efc88
commit 6b4cadb118
3 changed files with 736 additions and 0 deletions

10
meta.go
View File

@@ -74,6 +74,16 @@ var (
meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact") meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact")
meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2") meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2")
meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap") meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap")
meteoraDlmmAddLiquidityDiscriminator = calculateDiscriminator("global:add_liquidity")
meteoraDlmmAddLiquidity2Discriminator = calculateDiscriminator("global:add_liquidity2")
meteoraDlmmAddLiquidityByStrategyDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy")
meteoraDlmmAddLiquidityByStrategy2Discriminator = calculateDiscriminator("global:add_liquidity_by_strategy2")
meteoraDlmmRemoveLiquidityDiscriminator = calculateDiscriminator("global:remove_liquidity")
meteoraDlmmRemoveLiquidity2Discriminator = calculateDiscriminator("global:remove_liquidity2")
meteoraDlmmRemoveLiquidityByRangeDiscriminator = calculateDiscriminator("global:remove_liquidity_by_range")
meteoraDlmmRemoveLiquidityByRange2Discriminator = calculateDiscriminator("global:remove_liquidity_by_range2")
meteoraDlmmAddLiquidityEventDiscriminator = calculateDiscriminator("event:AddLiquidity")
meteoraDlmmRemoveLiquidityEventDiscriminator = calculateDiscriminator("event:RemoveLiquidity")
) )
// Program PumpAmm program ID // Program PumpAmm program ID

View File

@@ -39,6 +39,118 @@ type dlmmSwapEvent struct {
HostFee uint64 HostFee uint64
} }
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
}
type dlmmBinLiquidityDistribution struct {
BinId int32
DistributionX uint16
DistributionY uint16
}
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 {
AmountX uint64
AmountY uint64
ActiveID int32
MaxActiveBinSlippage int32
StrategyParameters dlmmStrategyParameters
}
type dlmmAddLiquidityArgs struct {
LiquidityParameter dlmmLiquidityParameter
}
type dlmmAddLiquidity2Args struct {
LiquidityParameter dlmmLiquidityParameter
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
type dlmmAddLiquidityByStrategyArgs struct {
LiquidityParameter dlmmLiquidityParameterByStrategy
}
type dlmmAddLiquidityByStrategy2Args struct {
LiquidityParameter dlmmLiquidityParameterByStrategy
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
type dlmmRemoveLiquidityArgs struct {
BinLiquidityRemoval []dlmmBinLiquidityReduction
}
type dlmmRemoveLiquidity2Args struct {
BinLiquidityRemoval []dlmmBinLiquidityReduction
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
type dlmmRemoveLiquidityByRangeArgs struct {
FromBinId int32
ToBinId int32
BpsToRemove uint16
}
type dlmmRemoveLiquidityByRange2Args struct {
FromBinId int32
ToBinId int32
BpsToRemove uint16
RemainingAccountsInfo dlmmRemainingAccountsInfo
}
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
}
type dlmmSwapAccounts struct { type dlmmSwapAccounts struct {
poolIdx int poolIdx int
reserveXIdx int reserveXIdx int
@@ -53,6 +165,20 @@ type dlmmSwapAccounts struct {
tokenYProgramIdx int tokenYProgramIdx int
} }
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
}
var meteoraDlmmEventAuthority = func() solana.PublicKey { var meteoraDlmmEventAuthority = func() solana.PublicKey {
key, _, err := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, meteoraDlmmProgram) key, _, err := solana.FindProgramAddress([][]byte{[]byte("__event_authority")}, meteoraDlmmProgram)
if err != nil { if err != nil {
@@ -77,6 +203,12 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI
return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset) return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset)
case meteoraDlmmSwap2Discriminator, meteoraDlmmSwapExactOut2Discriminator, meteoraDlmmSwapWithPriceImpact2Discriminator: case meteoraDlmmSwap2Discriminator, meteoraDlmmSwapExactOut2Discriminator, meteoraDlmmSwapWithPriceImpact2Discriminator:
return metaoradlmmSwap2Parser(tx, instruction, innerInstructions, offset) return metaoradlmmSwap2Parser(tx, instruction, innerInstructions, offset)
case meteoraDlmmAddLiquidityDiscriminator, meteoraDlmmAddLiquidity2Discriminator,
meteoraDlmmAddLiquidityByStrategyDiscriminator, meteoraDlmmAddLiquidityByStrategy2Discriminator:
return metaoradlmmAddLiquidityParser(tx, instruction, innerInstructions, offset)
case meteoraDlmmRemoveLiquidityDiscriminator, meteoraDlmmRemoveLiquidity2Discriminator,
meteoraDlmmRemoveLiquidityByRangeDiscriminator, meteoraDlmmRemoveLiquidityByRange2Discriminator:
return metaoradlmmRemoveLiquidityParser(tx, instruction, innerInstructions, offset)
default: default:
return nil, increaseOffset(offset), InstructionIgnoredError return nil, increaseOffset(offset), InstructionIgnoredError
} }
@@ -264,6 +396,362 @@ func metaoradlmmSwap2Parser(tx *Tx, instruction Instruction, innerInstructions I
return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset) return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset)
} }
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]
}
}
}
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 (
amountX uint64
amountY uint64
binDist []dlmmBinLiquidityDistribution
startBinId int32
endBinId int32
hasRange bool
)
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)
hasRange = len(binDist) > 0
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)
hasRange = len(binDist) > 0
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
hasRange = true
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
hasRange = true
default:
return nil, increaseOffset(offset), InstructionIgnoredError
}
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])
}
addEvent, nextOffset, err := dlmmAddLiquidityEventFromInnerInstructions(innerInstructions, instruction, offset)
if err != nil {
return nil, nextOffset, err
}
offset = nextOffset
amountX = addEvent.Amounts[0]
amountY = addEvent.Amounts[1]
binChanges := []DlmmBinLiquidityChange(nil)
if len(binDist) > 0 {
binChanges = dlmmBinChangesFromDistribution(amountX, amountY, binDist)
} else if hasRange {
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, 0)
}
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,
Event: "add_liquidity",
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,
StartBinId: startBinId,
EndBinId: endBinId,
BinChanges: binChanges,
}
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]
}
}
}
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 (
binChanges []DlmmBinLiquidityChange
startBinId int32
endBinId int32
)
switch discriminator {
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])
}
binChanges = dlmmBinChangesFromReduction(args.BinLiquidityRemoval)
startBinId, endBinId = dlmmMinMaxBinIdFromReduction(args.BinLiquidityRemoval)
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])
}
binChanges = dlmmBinChangesFromReduction(args.BinLiquidityRemoval)
startBinId, endBinId = dlmmMinMaxBinIdFromReduction(args.BinLiquidityRemoval)
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
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, args.BpsToRemove)
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
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, args.BpsToRemove)
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,
Event: "remove_liquidity",
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,
StartBinId: startBinId,
EndBinId: endBinId,
BinChanges: binChanges,
}
return []Swap{swap}, offset, nil
}
func dlmmSelectBaseQuote(tokenX, tokenY solana.PublicKey) (baseMint solana.PublicKey, quoteMint solana.PublicKey, baseIsX bool) { func dlmmSelectBaseQuote(tokenX, tokenY solana.PublicKey) (baseMint solana.PublicKey, quoteMint solana.PublicKey, baseIsX bool) {
priority := []solana.PublicKey{wSolMint, usdcMint, usd1Mint} priority := []solana.PublicKey{wSolMint, usdcMint, usd1Mint}
for _, mint := range priority { for _, mint := range priority {
@@ -301,6 +789,54 @@ func dlmmSwapEventFromInnerInstructions(innerInstructions InnerInstructions, ins
return dlmmSwapEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm swap event not found, offset, %d, %d", offset[0], prefixLen) return dlmmSwapEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm swap event not found, offset, %d, %d", offset[0], prefixLen)
} }
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 {
offset[1] += uint(innerIndex) + 1 + prefixLen
}
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 {
offset[1] += uint(innerIndex) + 1 + prefixLen
}
return event, offset, nil
}
return dlmmRemoveLiquidityEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm remove liquidity event not found, offset, %d, %d", offset[0], prefixLen)
}
func dlmmDecodeSwapEvent(data []byte) (dlmmSwapEvent, bool) { func dlmmDecodeSwapEvent(data []byte) (dlmmSwapEvent, bool) {
switch { switch {
case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmSwapEventDiscriminator[:]): case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmSwapEventDiscriminator[:]):
@@ -322,6 +858,48 @@ func dlmmDecodeSwapEvent(data []byte) (dlmmSwapEvent, bool) {
} }
} }
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
}
}
func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, error) { func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, error) {
if len(accounts) < 13 { if len(accounts) < 13 {
return dlmmSwapAccounts{}, fmt.Errorf("accounts too short, expected at least 13") return dlmmSwapAccounts{}, fmt.Errorf("accounts too short, expected at least 13")
@@ -385,6 +963,60 @@ func resolveDlmmSwapAccounts(result *RawTx, accounts []int) (dlmmSwapAccounts, e
return dlmmSwapAccounts{}, fmt.Errorf("accounts layout invalid") return dlmmSwapAccounts{}, fmt.Errorf("accounts layout invalid")
} }
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
}
func dlmmTokenDecimals(result *RawTx, accountIndex int) (uint8, bool) { func dlmmTokenDecimals(result *RawTx, accountIndex int) (uint8, bool) {
for _, meta := range result.Meta.PostTokenBalances { for _, meta := range result.Meta.PostTokenBalances {
if meta.AccountIndex == accountIndex { if meta.AccountIndex == accountIndex {
@@ -468,3 +1100,89 @@ func dlmmTokenBalanceMeta(result *RawTx, accountIndex int) (TokenBalance, bool)
} }
return TokenBalance{}, false return TokenBalance{}, false
} }
func dlmmBinChangesFromDistribution(amountX, amountY uint64, dist []dlmmBinLiquidityDistribution) []DlmmBinLiquidityChange {
if len(dist) == 0 {
return nil
}
totalX := decimal.NewFromUint64(amountX)
totalY := decimal.NewFromUint64(amountY)
denom := decimal.NewFromInt(10000)
changes := make([]DlmmBinLiquidityChange, 0, len(dist))
for _, item := range dist {
x := totalX.Mul(decimal.NewFromInt(int64(item.DistributionX))).Div(denom).Truncate(0)
y := totalY.Mul(decimal.NewFromInt(int64(item.DistributionY))).Div(denom).Truncate(0)
changes = append(changes, DlmmBinLiquidityChange{
BinId: item.BinId,
AmountX: x,
AmountY: y,
})
}
return changes
}
func dlmmBinChangesFromReduction(reduction []dlmmBinLiquidityReduction) []DlmmBinLiquidityChange {
if len(reduction) == 0 {
return nil
}
changes := make([]DlmmBinLiquidityChange, 0, len(reduction))
for _, item := range reduction {
changes = append(changes, DlmmBinLiquidityChange{
BinId: item.BinId,
BpsToRemove: item.BpsToRemove,
})
}
return changes
}
func dlmmBinChangesFromRange(startBinId, endBinId int32, bpsToRemove uint16) []DlmmBinLiquidityChange {
if startBinId > endBinId {
startBinId, endBinId = endBinId, startBinId
}
count := int(endBinId-startBinId) + 1
if count <= 0 {
return nil
}
changes := make([]DlmmBinLiquidityChange, 0, count)
for binId := startBinId; binId <= endBinId; binId++ {
changes = append(changes, DlmmBinLiquidityChange{
BinId: binId,
BpsToRemove: bpsToRemove,
})
}
return changes
}
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
}
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
}

8
tx.go
View File

@@ -37,6 +37,14 @@ type Swap struct {
//For meteora dlmm //For meteora dlmm
StartBinId int32 StartBinId int32
EndBinId int32 EndBinId int32
BinChanges []DlmmBinLiquidityChange
}
type DlmmBinLiquidityChange struct {
BinId int32
AmountX decimal.Decimal
AmountY decimal.Decimal
BpsToRemove uint16
} }
type platformInfo struct { type platformInfo struct {