Update dlmm fee
This commit is contained in:
121
cmd/rpc_parse/main.go
Normal file
121
cmd/rpc_parse/main.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/gagliardetto/solana-go/rpc"
|
||||
pump_parser "github.com/thloyi/pump-parser"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const rpcURL = "https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"
|
||||
txHash := os.Getenv("TX_HASH")
|
||||
if txHash == "" {
|
||||
txHash = "2AhpL5KhVmG3D38CwMzrHuRyTucEQ43GzBXL2mo5WiugdZMVmK1dtX8brGe3sxvvFDY6iSSviJTvqCtr4UL3Pc7J"
|
||||
}
|
||||
|
||||
if txHash == "" {
|
||||
fmt.Fprintln(os.Stderr, "txHash is empty; set it in cmd/rpc_parse/main.go")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sig, err := solana.SignatureFromBase58(txHash)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid txHash: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
client := rpc.New(rpcURL)
|
||||
maxSupportedVersion := uint64(0)
|
||||
out, err := client.GetTransaction(context.Background(), sig, &rpc.GetTransactionOpts{
|
||||
Encoding: solana.EncodingBase64,
|
||||
Commitment: rpc.CommitmentConfirmed,
|
||||
MaxSupportedTransactionVersion: &maxSupportedVersion,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "rpc getTransaction error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if out == nil || out.Transaction == nil || out.Meta == nil {
|
||||
fmt.Fprintln(os.Stderr, "rpc getTransaction returned empty response")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
rawBinary := out.Transaction.GetBinary()
|
||||
if len(rawBinary) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "rpc getTransaction returned empty transaction data")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
txWithMeta := rpc.TransactionWithMeta{
|
||||
Slot: out.Slot,
|
||||
BlockTime: out.BlockTime,
|
||||
Transaction: rpc.DataBytesOrJSONFromBytes(rawBinary),
|
||||
Meta: out.Meta,
|
||||
Version: out.Version,
|
||||
}
|
||||
|
||||
var blockTime *uint64
|
||||
if out.BlockTime != nil {
|
||||
bt := uint64(*out.BlockTime)
|
||||
blockTime = &bt
|
||||
}
|
||||
|
||||
rawTx, err := pump_parser.FromRpcTransactionWithMeta(txWithMeta, blockTime, out.Slot, 0)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "convert rpc transaction error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pump_parser.EnableAllParsers()
|
||||
|
||||
parsed, err := pump_parser.ParseRawTx(rawTx)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "parse raw tx error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(parsed.Swaps) == 0 {
|
||||
fmt.Println("no swaps parsed from tx")
|
||||
return
|
||||
}
|
||||
|
||||
for i, swap := range parsed.Swaps {
|
||||
fmt.Printf("swap[%d]\n", i)
|
||||
fmt.Printf(" program: %s\n", swap.Program)
|
||||
fmt.Printf(" event: %s\n", swap.Event)
|
||||
fmt.Printf(" pool: %s\n", swap.Pool)
|
||||
fmt.Printf(" user: %s\n", swap.User)
|
||||
fmt.Printf(" base_mint: %s (decimals=%d)\n", swap.BaseMint, swap.BaseMintDecimals)
|
||||
fmt.Printf(" quote_mint: %s (decimals=%d)\n", swap.QuoteMint, swap.QuoteMintDecimals)
|
||||
fmt.Printf(" base_amount: %s\n", swap.BaseAmount.String())
|
||||
fmt.Printf(" quote_amount: %s\n", swap.QuoteAmount.String())
|
||||
if !swap.FeeAmount.IsZero() || swap.FeeSide != "" {
|
||||
fmt.Printf(" fee_amount: %s\n", swap.FeeAmount.String())
|
||||
fmt.Printf(" lp_fee_amount: %s\n", swap.LpFeeAmount.String())
|
||||
fmt.Printf(" fee_side: %s\n", swap.FeeSide)
|
||||
fmt.Printf(" fee_mint: %s (decimals=%d)\n", swap.FeeMint, swap.FeeMintDecimals)
|
||||
fmt.Printf(" fee_token_program: %s\n", swap.FeeTokenProgram)
|
||||
}
|
||||
fmt.Printf(" base_reserve: %s\n", swap.BaseReserve.String())
|
||||
fmt.Printf(" quote_reserve: %s\n", swap.QuoteReserve.String())
|
||||
fmt.Printf(" base_token_program: %s\n", swap.BaseTokenProgram)
|
||||
fmt.Printf(" quote_token_program: %s\n", swap.QuoteTokenProgram)
|
||||
fmt.Printf(" entry_contract: %s\n", swap.EntryContract)
|
||||
fmt.Printf(" user_base_balance: %s\n", swap.UserBaseBalance.String())
|
||||
fmt.Printf(" user_quote_balance: %s\n", swap.UserQuoteBalance.String())
|
||||
fmt.Printf(" active_bin_id: %d\n", swap.ActiveBinId)
|
||||
fmt.Printf(" start_bin_id: %d\n", swap.StartBinId)
|
||||
fmt.Printf(" end_bin_id: %d\n", swap.EndBinId)
|
||||
fmt.Printf(" remove_bp: %d\n", swap.RemoveBp)
|
||||
fmt.Printf(" position_account: %s\n", swap.PositionAccount)
|
||||
if swap.Mayhem {
|
||||
fmt.Printf(" mayhem: true\n")
|
||||
} else {
|
||||
fmt.Printf(" mayhem: false\n")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
6
meta.go
6
meta.go
@@ -68,7 +68,11 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
meteoraInitializeLbPairDiscriminator = calculateDiscriminator("global:initialize_lb_pair2")
|
||||
meteoraInitializeCustomizablePermissionlessLbPairDiscriminator = calculateDiscriminator("global:initialize_customizable_permissionless_lb_pair")
|
||||
meteoraInitializeCustomizablePermissionlessLbPair2Discriminator = calculateDiscriminator("global:initialize_customizable_permissionless_lb_pair2")
|
||||
meteoraInitializeLbPairDiscriminator = calculateDiscriminator("global:initialize_lb_pair")
|
||||
meteoraInitializeLbPair2Discriminator = calculateDiscriminator("global:initialize_lb_pair2")
|
||||
meteoraInitializePermissionLbPairDiscriminator = calculateDiscriminator("global:initialize_permission_lb_pair")
|
||||
meteoraInitializeLbPairEventDiscriminator = calculateDiscriminator("event:LbPairCreate")
|
||||
meteoraDlmmSwapDiscriminator = calculateDiscriminator("global:swap")
|
||||
meteoraDlmmSwap2Discriminator = calculateDiscriminator("global:swap2")
|
||||
|
||||
366
metaoradlmm.go
366
metaoradlmm.go
@@ -66,6 +66,13 @@ type dlmmPositionCloseEvent struct {
|
||||
Owner solana.PublicKey
|
||||
}
|
||||
|
||||
type dlmmLbPairCreateEvent struct {
|
||||
LbPair solana.PublicKey
|
||||
BinStep uint16
|
||||
TokenX solana.PublicKey
|
||||
TokenY solana.PublicKey
|
||||
}
|
||||
|
||||
type dlmmClaimFeeInnerEvent struct {
|
||||
LbPair solana.PublicKey
|
||||
Position solana.PublicKey
|
||||
@@ -340,7 +347,11 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI
|
||||
|
||||
discriminator := *(*[8]byte)(decode[:8])
|
||||
switch discriminator {
|
||||
case meteoraInitializeLbPairDiscriminator:
|
||||
case meteoraInitializeCustomizablePermissionlessLbPairDiscriminator,
|
||||
meteoraInitializeCustomizablePermissionlessLbPair2Discriminator,
|
||||
meteoraInitializeLbPairDiscriminator,
|
||||
meteoraInitializeLbPair2Discriminator,
|
||||
meteoraInitializePermissionLbPairDiscriminator:
|
||||
return metaoradlmmInitializeParser(tx, instruction, innerInstructions, offset)
|
||||
case meteoraDlmmInitializePositionDiscriminator, meteoraDlmmInitializePosition2Discriminator,
|
||||
meteoraDlmmInitializePositionByOperatorDiscriminator, meteoraDlmmInitializePositionPdaDiscriminator:
|
||||
@@ -369,53 +380,131 @@ func metaoradlmmParser(tx *Tx, instruction Instruction, innerInstructions InnerI
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
func metaoradlmmInitializeParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
market := tx.rawTx.accountList[instruction.Accounts[0]]
|
||||
token0 := tx.rawTx.accountList[instruction.Accounts[2]]
|
||||
token1 := tx.rawTx.accountList[instruction.Accounts[3]]
|
||||
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])
|
||||
}
|
||||
|
||||
entryContract := tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
|
||||
|
||||
var baseDecimals uint8
|
||||
var quoteDecimals uint8
|
||||
findMintDecimals := func(mint solana.PublicKey) uint8 {
|
||||
for _, acc := range tx.rawTx.Meta.PostTokenBalances {
|
||||
if acc.MintAccount.Equals(token0) {
|
||||
baseDecimals = uint8(acc.UITokenAmount.Decimals)
|
||||
}
|
||||
if acc.MintAccount.Equals(token1) {
|
||||
quoteDecimals = uint8(acc.UITokenAmount.Decimals)
|
||||
if acc.MintAccount.Equals(mint) {
|
||||
return uint8(acc.UITokenAmount.Decimals)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
swap := Swap{
|
||||
Program: SolProgramMeteoraDLMM,
|
||||
Event: "create",
|
||||
Pool: market,
|
||||
BaseMint: token0,
|
||||
QuoteMint: token1,
|
||||
BaseTokenProgram: tx.rawTx.accountList[instruction.Accounts[11]],
|
||||
QuoteTokenProgram: tx.rawTx.accountList[instruction.Accounts[12]],
|
||||
Pool: accounts.pool,
|
||||
BaseMint: accounts.token0,
|
||||
QuoteMint: accounts.token1,
|
||||
BaseTokenProgram: accounts.baseTokenProgram,
|
||||
QuoteTokenProgram: accounts.quoteTokenProgram,
|
||||
Creator: tx.rawTx.accountList[0],
|
||||
BaseMintDecimals: baseDecimals,
|
||||
QuoteMintDecimals: quoteDecimals,
|
||||
User: tx.rawTx.accountList[instruction.Accounts[8]],
|
||||
BaseMintDecimals: findMintDecimals(accounts.token0),
|
||||
QuoteMintDecimals: findMintDecimals(accounts.token1),
|
||||
User: accounts.user,
|
||||
EntryContract: entryContract,
|
||||
}
|
||||
var prefixLen = offset[1]
|
||||
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||
createEvent, nextOffset, found, err := dlmmLbPairCreateEventFromInnerInstructions(innerInstructions, instruction, offset)
|
||||
if err != nil {
|
||||
return nil, increaseOffset(offset), fmt.Errorf("pump create get inner instructions error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||
return nil, nextOffset, err
|
||||
}
|
||||
var programIndex = instruction.ProgramIDIndex
|
||||
|
||||
for innerIndex, innerInstr := range inners {
|
||||
if innerInstr.ProgramIDIndex == programIndex && len(innerInstr.Data) >= 16 && bytes.Equal(innerInstr.Data[:8], pumpEventDiscriminator[:]) && bytes.Equal(innerInstr.Data[8:16], meteoraInitializeLbPairEventDiscriminator[:]) {
|
||||
if offset[1] == 0 {
|
||||
offset[0] += 1
|
||||
} else {
|
||||
offset[1] = uint(innerIndex) + 1 + prefixLen
|
||||
if found {
|
||||
offset = nextOffset
|
||||
if !createEvent.LbPair.IsZero() {
|
||||
swap.Pool = createEvent.LbPair
|
||||
}
|
||||
break
|
||||
if !createEvent.TokenX.IsZero() {
|
||||
swap.BaseMint = createEvent.TokenX
|
||||
}
|
||||
if !createEvent.TokenY.IsZero() {
|
||||
swap.QuoteMint = createEvent.TokenY
|
||||
}
|
||||
swap.BaseMintDecimals = findMintDecimals(swap.BaseMint)
|
||||
swap.QuoteMintDecimals = findMintDecimals(swap.QuoteMint)
|
||||
}
|
||||
return []Swap{swap}, offset, nil
|
||||
}
|
||||
@@ -729,6 +818,18 @@ func metaoradlmmSwapParser(tx *Tx, instruction Instruction, innerInstructions In
|
||||
userQuote = userQuote.Add(decimal.NewFromUint64(solAmount))
|
||||
}
|
||||
}
|
||||
feeAmount, feeSide, feeMint, feeTokenProgram, feeDecimals := dlmmSwapFeeInfo(
|
||||
baseIsX,
|
||||
swapForY,
|
||||
swapEvent.Fee,
|
||||
baseMint,
|
||||
quoteMint,
|
||||
baseTokenProgram,
|
||||
quoteTokenProgram,
|
||||
baseDecimals,
|
||||
quoteDecimals,
|
||||
)
|
||||
lpFeeAmount := dlmmSwapLpFeeAmount(swapEvent.Fee, swapEvent.ProtocolFee, swapEvent.HostFee)
|
||||
|
||||
swap := Swap{
|
||||
Program: SolProgramMeteoraDLMM,
|
||||
@@ -744,6 +845,12 @@ func metaoradlmmSwapParser(tx *Tx, instruction Instruction, innerInstructions In
|
||||
User: eventUser,
|
||||
BaseAmount: baseAmount,
|
||||
QuoteAmount: quoteAmount,
|
||||
FeeAmount: feeAmount,
|
||||
LpFeeAmount: lpFeeAmount,
|
||||
FeeSide: feeSide,
|
||||
FeeMint: feeMint,
|
||||
FeeTokenProgram: feeTokenProgram,
|
||||
FeeMintDecimals: feeDecimals,
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
UserBaseBalance: userBase,
|
||||
@@ -761,6 +868,35 @@ func metaoradlmmSwap2Parser(tx *Tx, instruction Instruction, innerInstructions I
|
||||
return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
|
||||
result := tx.rawTx
|
||||
|
||||
@@ -788,7 +924,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
weightDist []dlmmBinLiquidityDistributionByWeight
|
||||
startBinId int32
|
||||
endBinId int32
|
||||
hasRange bool
|
||||
oneSide bool
|
||||
)
|
||||
|
||||
@@ -802,7 +937,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
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 {
|
||||
@@ -812,7 +946,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
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 {
|
||||
@@ -822,7 +955,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
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 {
|
||||
@@ -832,7 +964,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
amountY = args.LiquidityParameter.AmountY
|
||||
startBinId = args.LiquidityParameter.StrategyParameters.MinBinId
|
||||
endBinId = args.LiquidityParameter.StrategyParameters.MaxBinId
|
||||
hasRange = true
|
||||
case meteoraDlmmAddLiquidityByWeightDiscriminator:
|
||||
var args dlmmAddLiquidityByWeightArgs
|
||||
if err := agbinary.NewBorshDecoder(decode[8:]).Decode(&args); err != nil {
|
||||
@@ -842,7 +973,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
amountY = args.LiquidityParameter.AmountY
|
||||
weightDist = args.LiquidityParameter.BinLiquidityDist
|
||||
startBinId, endBinId = dlmmMinMaxBinIdFromWeightDistribution(weightDist)
|
||||
hasRange = len(weightDist) > 0
|
||||
case meteoraDlmmAddLiquidityOneSideDiscriminator:
|
||||
var args dlmmAddLiquidityOneSideArgs
|
||||
if err := agbinary.NewBorshDecoder(decode[8:]).Decode(&args); err != nil {
|
||||
@@ -850,7 +980,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
}
|
||||
weightDist = args.LiquidityParameter.BinLiquidityDist
|
||||
startBinId, endBinId = dlmmMinMaxBinIdFromWeightDistribution(weightDist)
|
||||
hasRange = len(weightDist) > 0
|
||||
oneSide = true
|
||||
case meteoraDlmmAddLiquidityOneSidePreciseDiscriminator:
|
||||
var args dlmmAddLiquidityOneSidePreciseArgs
|
||||
@@ -858,7 +987,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
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)
|
||||
hasRange = len(args.Parameter.Bins) > 0
|
||||
oneSide = true
|
||||
case meteoraDlmmAddLiquidityOneSidePrecise2Discriminator:
|
||||
var args dlmmAddLiquidityOneSidePrecise2Args
|
||||
@@ -866,7 +994,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
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)
|
||||
hasRange = len(args.LiquidityParameter.Bins) > 0
|
||||
oneSide = true
|
||||
case meteoraDlmmAddLiquidityByStrategyOneSideDiscriminator:
|
||||
var args dlmmAddLiquidityByStrategyOneSideArgs
|
||||
@@ -875,7 +1002,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
}
|
||||
startBinId = args.LiquidityParameter.StrategyParameters.MinBinId
|
||||
endBinId = args.LiquidityParameter.StrategyParameters.MaxBinId
|
||||
hasRange = true
|
||||
oneSide = true
|
||||
default:
|
||||
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||
@@ -890,7 +1016,7 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
amountY = addEvent.Amounts[1]
|
||||
|
||||
if oneSide {
|
||||
swaps, err := dlmmBuildOneSideAddSwap(tx, instruction, addEvent, weightDist, startBinId, endBinId, hasRange, entryContract)
|
||||
swaps, err := dlmmBuildOneSideAddSwap(tx, instruction, addEvent, startBinId, endBinId, entryContract)
|
||||
if err != nil {
|
||||
return nil, offset, err
|
||||
}
|
||||
@@ -902,16 +1028,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
return nil, increaseOffset(offset), fmt.Errorf("meteora dlmm add liquidity accounts parse error: %v, offset, %d, %d", err, offset[0], offset[1])
|
||||
}
|
||||
|
||||
binChanges := []DlmmBinLiquidityChange(nil)
|
||||
if len(binDist) > 0 {
|
||||
binChanges = dlmmBinChangesFromDistribution(amountX, amountY, binDist)
|
||||
} else if len(weightDist) > 0 {
|
||||
// Weight-only params do not preserve per-side amounts for each bin, so keep the affected range only.
|
||||
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, 0)
|
||||
} else if hasRange {
|
||||
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, 0)
|
||||
}
|
||||
|
||||
pool := result.accountList[accounts.poolIdx]
|
||||
tokenXMint := result.accountList[accounts.tokenXMintIdx]
|
||||
tokenYMint := result.accountList[accounts.tokenYMintIdx]
|
||||
@@ -942,7 +1058,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
baseAmount = amountYDec
|
||||
quoteAmount = amountXDec
|
||||
}
|
||||
|
||||
eventUser := result.accountList[accounts.userIdx]
|
||||
if !addEvent.From.IsZero() {
|
||||
eventUser = addEvent.From
|
||||
@@ -1003,7 +1118,6 @@ func metaoradlmmAddLiquidityParser(tx *Tx, instruction Instruction, innerInstruc
|
||||
ActiveBinId: addEvent.ActiveBinId,
|
||||
StartBinId: startBinId,
|
||||
EndBinId: endBinId,
|
||||
BinChanges: binChanges,
|
||||
PositionAccount: result.accountList[accounts.positionIdx],
|
||||
}
|
||||
|
||||
@@ -1031,7 +1145,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
|
||||
discriminator := *(*[8]byte)(decode[:8])
|
||||
var (
|
||||
binChanges []DlmmBinLiquidityChange
|
||||
startBinId int32
|
||||
endBinId int32
|
||||
removeBp int32
|
||||
@@ -1045,7 +1158,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
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)
|
||||
removeBp = dlmmCommonRemoveBp(args.BinLiquidityRemoval)
|
||||
case meteoraDlmmRemoveLiquidity2Discriminator:
|
||||
@@ -1053,7 +1165,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
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)
|
||||
removeBp = dlmmCommonRemoveBp(args.BinLiquidityRemoval)
|
||||
case meteoraDlmmRemoveLiquidityByRangeDiscriminator:
|
||||
@@ -1064,7 +1175,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
startBinId = args.FromBinId
|
||||
endBinId = args.ToBinId
|
||||
removeBp = int32(args.BpsToRemove)
|
||||
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, args.BpsToRemove)
|
||||
case meteoraDlmmRemoveLiquidityByRange2Discriminator:
|
||||
var args dlmmRemoveLiquidityByRange2Args
|
||||
if err := agbinary.NewBorshDecoder(decode[8:]).Decode(&args); err != nil {
|
||||
@@ -1073,7 +1183,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
startBinId = args.FromBinId
|
||||
endBinId = args.ToBinId
|
||||
removeBp = int32(args.BpsToRemove)
|
||||
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, args.BpsToRemove)
|
||||
default:
|
||||
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
@@ -1119,7 +1228,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
baseAmount = amountYDec
|
||||
quoteAmount = amountXDec
|
||||
}
|
||||
|
||||
eventUser := result.accountList[accounts.userIdx]
|
||||
if !removeEvent.From.IsZero() {
|
||||
eventUser = removeEvent.From
|
||||
@@ -1181,7 +1289,6 @@ func metaoradlmmRemoveLiquidityParser(tx *Tx, instruction Instruction, innerInst
|
||||
StartBinId: startBinId,
|
||||
EndBinId: endBinId,
|
||||
RemoveBp: removeBp,
|
||||
BinChanges: binChanges,
|
||||
PositionAccount: result.accountList[accounts.positionIdx],
|
||||
}
|
||||
|
||||
@@ -1425,7 +1532,6 @@ func metaoradlmmRebalanceLiquidityParser(tx *Tx, instruction Instruction, innerI
|
||||
ActiveBinId: event.ActiveBinId,
|
||||
StartBinId: event.OldMinBinId,
|
||||
EndBinId: event.OldMaxBinId,
|
||||
BinChanges: dlmmBinChangesFromRange(event.OldMinBinId, event.OldMaxBinId, 0),
|
||||
PositionAccount: result.accountList[accounts.positionIdx],
|
||||
})
|
||||
}
|
||||
@@ -1451,7 +1557,6 @@ func metaoradlmmRebalanceLiquidityParser(tx *Tx, instruction Instruction, innerI
|
||||
ActiveBinId: event.ActiveBinId,
|
||||
StartBinId: event.NewMinBinId,
|
||||
EndBinId: event.NewMaxBinId,
|
||||
BinChanges: dlmmBinChangesFromRange(event.NewMinBinId, event.NewMaxBinId, 0),
|
||||
PositionAccount: result.accountList[accounts.positionIdx],
|
||||
})
|
||||
}
|
||||
@@ -1633,6 +1738,51 @@ func dlmmPositionCloseEventFromInnerInstructions(innerInstructions InnerInstruct
|
||||
return dlmmPositionCloseEvent{}, increaseOffset(offset), false, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func dlmmDecodeAddLiquidityEvent(data []byte) (dlmmAddLiquidityEvent, bool) {
|
||||
switch {
|
||||
case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmAddLiquidityEventDiscriminator[:]):
|
||||
@@ -1973,10 +2123,8 @@ func dlmmBuildOneSideAddSwap(
|
||||
tx *Tx,
|
||||
instruction Instruction,
|
||||
addEvent dlmmAddLiquidityEvent,
|
||||
weightDist []dlmmBinLiquidityDistributionByWeight,
|
||||
startBinId int32,
|
||||
endBinId int32,
|
||||
hasRange bool,
|
||||
entryContract solana.PublicKey,
|
||||
) ([]Swap, error) {
|
||||
result := tx.rawTx
|
||||
@@ -1999,13 +2147,6 @@ func dlmmBuildOneSideAddSwap(
|
||||
}
|
||||
}
|
||||
|
||||
binChanges := []DlmmBinLiquidityChange(nil)
|
||||
if len(weightDist) > 0 {
|
||||
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, 0)
|
||||
} else if hasRange {
|
||||
binChanges = dlmmBinChangesFromRange(startBinId, endBinId, 0)
|
||||
}
|
||||
|
||||
eventUser := result.accountList[accounts.userIdx]
|
||||
if !addEvent.From.IsZero() {
|
||||
eventUser = addEvent.From
|
||||
@@ -2024,7 +2165,6 @@ func dlmmBuildOneSideAddSwap(
|
||||
ActiveBinId: addEvent.ActiveBinId,
|
||||
StartBinId: startBinId,
|
||||
EndBinId: endBinId,
|
||||
BinChanges: binChanges,
|
||||
PositionAccount: positionAccount,
|
||||
}
|
||||
|
||||
@@ -2255,56 +2395,50 @@ func dlmmTokenBalanceMeta(result *RawTx, accountIndex int) (TokenBalance, bool)
|
||||
return TokenBalance{}, false
|
||||
}
|
||||
|
||||
func dlmmBinChangesFromDistribution(amountX, amountY uint64, dist []dlmmBinLiquidityDistribution) []DlmmBinLiquidityChange {
|
||||
if len(dist) == 0 {
|
||||
func dlmmAllocateByWeights(total uint64, weights []uint64) []decimal.Decimal {
|
||||
if len(weights) == 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,
|
||||
})
|
||||
|
||||
sumWeights := uint64(0)
|
||||
for _, weight := range weights {
|
||||
sumWeights += weight
|
||||
}
|
||||
return changes
|
||||
if sumWeights == 0 {
|
||||
sumWeights = uint64(len(weights))
|
||||
weights = append([]uint64(nil), weights...)
|
||||
for i := range weights {
|
||||
weights[i] = 1
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return allocations
|
||||
}
|
||||
|
||||
func dlmmBinChangesFromReduction(reduction []dlmmBinLiquidityReduction) []DlmmBinLiquidityChange {
|
||||
if len(reduction) == 0 {
|
||||
return nil
|
||||
func dlmmApplySignedAllocation(values []decimal.Decimal, negative bool) []decimal.Decimal {
|
||||
if !negative {
|
||||
return values
|
||||
}
|
||||
changes := make([]DlmmBinLiquidityChange, 0, len(reduction))
|
||||
for _, item := range reduction {
|
||||
changes = append(changes, DlmmBinLiquidityChange{
|
||||
BinId: item.BinId,
|
||||
BpsToRemove: item.BpsToRemove,
|
||||
})
|
||||
out := make([]decimal.Decimal, len(values))
|
||||
for i, value := range values {
|
||||
out[i] = value.Neg()
|
||||
}
|
||||
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
|
||||
return out
|
||||
}
|
||||
|
||||
func dlmmMinMaxBinIDFromCompressedDeposits(bins []dlmmCompressedBinDepositAmount) (startBinID, endBinID int32) {
|
||||
|
||||
415
metaoradlmm_test.go
Normal file
415
metaoradlmm_test.go
Normal file
@@ -0,0 +1,415 @@
|
||||
package pump_parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
agbinary "github.com/gagliardetto/binary"
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func testPublicKey(seed byte) solana.PublicKey {
|
||||
buf := make([]byte, solana.PublicKeyLength)
|
||||
for i := range buf {
|
||||
buf[i] = seed
|
||||
}
|
||||
return solana.PublicKeyFromBytes(buf)
|
||||
}
|
||||
|
||||
func seqInts(n int) []int {
|
||||
out := make([]int, n)
|
||||
for i := range out {
|
||||
out[i] = i
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mustBorshEncode(t *testing.T, value any) []byte {
|
||||
t.Helper()
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := agbinary.NewBorshEncoder(&buf).Encode(value); err != nil {
|
||||
t.Fatalf("borsh encode failed: %v", err)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func TestMeteoraDlmmInitializeParserCompatibility(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
discriminator [8]byte
|
||||
accountCount int
|
||||
wantPoolPos int
|
||||
wantBaseMintPos int
|
||||
wantQuoteMintPos int
|
||||
wantUserPos int
|
||||
wantBaseProgramPos int
|
||||
wantQuoteProgramPos int
|
||||
}{
|
||||
{
|
||||
name: "initialize_lb_pair",
|
||||
discriminator: meteoraInitializeLbPairDiscriminator,
|
||||
accountCount: 14,
|
||||
wantPoolPos: 0,
|
||||
wantBaseMintPos: 2,
|
||||
wantQuoteMintPos: 3,
|
||||
wantUserPos: 8,
|
||||
wantBaseProgramPos: 9,
|
||||
wantQuoteProgramPos: 9,
|
||||
},
|
||||
{
|
||||
name: "initialize_lb_pair2",
|
||||
discriminator: meteoraInitializeLbPair2Discriminator,
|
||||
accountCount: 16,
|
||||
wantPoolPos: 0,
|
||||
wantBaseMintPos: 2,
|
||||
wantQuoteMintPos: 3,
|
||||
wantUserPos: 8,
|
||||
wantBaseProgramPos: 11,
|
||||
wantQuoteProgramPos: 12,
|
||||
},
|
||||
{
|
||||
name: "initialize_customizable_permissionless_lb_pair",
|
||||
discriminator: meteoraInitializeCustomizablePermissionlessLbPairDiscriminator,
|
||||
accountCount: 14,
|
||||
wantPoolPos: 0,
|
||||
wantBaseMintPos: 2,
|
||||
wantQuoteMintPos: 3,
|
||||
wantUserPos: 8,
|
||||
wantBaseProgramPos: 9,
|
||||
wantQuoteProgramPos: 9,
|
||||
},
|
||||
{
|
||||
name: "initialize_customizable_permissionless_lb_pair2",
|
||||
discriminator: meteoraInitializeCustomizablePermissionlessLbPair2Discriminator,
|
||||
accountCount: 17,
|
||||
wantPoolPos: 0,
|
||||
wantBaseMintPos: 2,
|
||||
wantQuoteMintPos: 3,
|
||||
wantUserPos: 8,
|
||||
wantBaseProgramPos: 11,
|
||||
wantQuoteProgramPos: 12,
|
||||
},
|
||||
{
|
||||
name: "initialize_permission_lb_pair",
|
||||
discriminator: meteoraInitializePermissionLbPairDiscriminator,
|
||||
accountCount: 17,
|
||||
wantPoolPos: 1,
|
||||
wantBaseMintPos: 3,
|
||||
wantQuoteMintPos: 4,
|
||||
wantUserPos: 8,
|
||||
wantBaseProgramPos: 11,
|
||||
wantQuoteProgramPos: 12,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
accountList := make([]solana.PublicKey, 32)
|
||||
for i := range accountList {
|
||||
accountList[i] = testPublicKey(byte(i + 1))
|
||||
}
|
||||
programIndex := 30
|
||||
accountList[programIndex] = meteoraDlmmProgram
|
||||
|
||||
instruction := Instruction{
|
||||
Accounts: seqInts(tc.accountCount),
|
||||
Data: solana.Base58(tc.discriminator[:]),
|
||||
ProgramIDIndex: programIndex,
|
||||
}
|
||||
|
||||
rawTx := &RawTx{
|
||||
accountList: accountList,
|
||||
Meta: Meta{
|
||||
PostTokenBalances: []TokenBalance{
|
||||
{
|
||||
MintAccount: accountList[tc.wantBaseMintPos],
|
||||
UITokenAmount: UITokenAmount{
|
||||
Decimals: 6,
|
||||
},
|
||||
},
|
||||
{
|
||||
MintAccount: accountList[tc.wantQuoteMintPos],
|
||||
UITokenAmount: UITokenAmount{
|
||||
Decimals: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Transaction: Transaction{
|
||||
Message: Message{
|
||||
Instructions: []Instruction{instruction},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tx := &Tx{rawTx: rawTx}
|
||||
|
||||
swaps, _, err := metaoradlmmParser(tx, instruction, InnerInstructions{}, [2]uint{0, 0})
|
||||
if err != nil {
|
||||
t.Fatalf("metaoradlmmParser() error = %v", err)
|
||||
}
|
||||
if len(swaps) != 1 {
|
||||
t.Fatalf("metaoradlmmParser() swaps len = %d, want 1", len(swaps))
|
||||
}
|
||||
|
||||
swap := swaps[0]
|
||||
if !swap.Pool.Equals(accountList[tc.wantPoolPos]) {
|
||||
t.Fatalf("swap.Pool = %s, want %s", swap.Pool, accountList[tc.wantPoolPos])
|
||||
}
|
||||
if !swap.BaseMint.Equals(accountList[tc.wantBaseMintPos]) {
|
||||
t.Fatalf("swap.BaseMint = %s, want %s", swap.BaseMint, accountList[tc.wantBaseMintPos])
|
||||
}
|
||||
if !swap.QuoteMint.Equals(accountList[tc.wantQuoteMintPos]) {
|
||||
t.Fatalf("swap.QuoteMint = %s, want %s", swap.QuoteMint, accountList[tc.wantQuoteMintPos])
|
||||
}
|
||||
if !swap.User.Equals(accountList[tc.wantUserPos]) {
|
||||
t.Fatalf("swap.User = %s, want %s", swap.User, accountList[tc.wantUserPos])
|
||||
}
|
||||
if !swap.BaseTokenProgram.Equals(accountList[tc.wantBaseProgramPos]) {
|
||||
t.Fatalf("swap.BaseTokenProgram = %s, want %s", swap.BaseTokenProgram, accountList[tc.wantBaseProgramPos])
|
||||
}
|
||||
if !swap.QuoteTokenProgram.Equals(accountList[tc.wantQuoteProgramPos]) {
|
||||
t.Fatalf("swap.QuoteTokenProgram = %s, want %s", swap.QuoteTokenProgram, accountList[tc.wantQuoteProgramPos])
|
||||
}
|
||||
if swap.BaseMintDecimals != 6 {
|
||||
t.Fatalf("swap.BaseMintDecimals = %d, want 6", swap.BaseMintDecimals)
|
||||
}
|
||||
if swap.QuoteMintDecimals != 9 {
|
||||
t.Fatalf("swap.QuoteMintDecimals = %d, want 9", swap.QuoteMintDecimals)
|
||||
}
|
||||
if !swap.EntryContract.Equals(meteoraDlmmProgram) {
|
||||
t.Fatalf("swap.EntryContract = %s, want %s", swap.EntryContract, meteoraDlmmProgram)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDlmmDecodeLbPairCreateEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
event := dlmmLbPairCreateEvent{
|
||||
LbPair: testPublicKey(90),
|
||||
BinStep: 42,
|
||||
TokenX: testPublicKey(91),
|
||||
TokenY: testPublicKey(92),
|
||||
}
|
||||
|
||||
body := mustBorshEncode(t, event)
|
||||
|
||||
barePayload := append(append([]byte{}, meteoraInitializeLbPairEventDiscriminator[:]...), body...)
|
||||
decodedBare, ok := dlmmDecodeLbPairCreateEvent(barePayload)
|
||||
if !ok {
|
||||
t.Fatalf("dlmmDecodeLbPairCreateEvent() failed for bare payload")
|
||||
}
|
||||
if decodedBare != event {
|
||||
t.Fatalf("decoded bare event = %+v, want %+v", decodedBare, event)
|
||||
}
|
||||
|
||||
anchorPayload := append(append(append([]byte{}, eventDiscriminator[:]...), meteoraInitializeLbPairEventDiscriminator[:]...), body...)
|
||||
decodedAnchor, ok := dlmmDecodeLbPairCreateEvent(anchorPayload)
|
||||
if !ok {
|
||||
t.Fatalf("dlmmDecodeLbPairCreateEvent() failed for anchor payload")
|
||||
}
|
||||
if decodedAnchor != event {
|
||||
t.Fatalf("decoded anchor event = %+v, want %+v", decodedAnchor, event)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeteoraDlmmInitializeParserUsesLbPairCreateEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
accountList := make([]solana.PublicKey, 32)
|
||||
for i := range accountList {
|
||||
accountList[i] = testPublicKey(byte(i + 1))
|
||||
}
|
||||
programIndex := 30
|
||||
accountList[programIndex] = meteoraDlmmProgram
|
||||
|
||||
instruction := Instruction{
|
||||
Accounts: seqInts(16),
|
||||
Data: solana.Base58(meteoraInitializeLbPair2Discriminator[:]),
|
||||
ProgramIDIndex: programIndex,
|
||||
}
|
||||
|
||||
event := dlmmLbPairCreateEvent{
|
||||
LbPair: testPublicKey(111),
|
||||
BinStep: 25,
|
||||
TokenX: testPublicKey(112),
|
||||
TokenY: testPublicKey(113),
|
||||
}
|
||||
innerEventData := append(
|
||||
append(append([]byte{}, eventDiscriminator[:]...), meteoraInitializeLbPairEventDiscriminator[:]...),
|
||||
mustBorshEncode(t, event)...,
|
||||
)
|
||||
|
||||
rawTx := &RawTx{
|
||||
accountList: accountList,
|
||||
Meta: Meta{
|
||||
PostTokenBalances: []TokenBalance{
|
||||
{
|
||||
MintAccount: accountList[2],
|
||||
UITokenAmount: UITokenAmount{
|
||||
Decimals: 6,
|
||||
},
|
||||
},
|
||||
{
|
||||
MintAccount: accountList[3],
|
||||
UITokenAmount: UITokenAmount{
|
||||
Decimals: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
InnerInstructions: []InnerInstructions{
|
||||
{
|
||||
Index: 0,
|
||||
Instructions: []Instruction{
|
||||
{
|
||||
ProgramIDIndex: programIndex,
|
||||
Data: solana.Base58(innerEventData),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Transaction: Transaction{
|
||||
Message: Message{
|
||||
Instructions: []Instruction{instruction},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tx := &Tx{rawTx: rawTx}
|
||||
|
||||
swaps, nextOffset, err := metaoradlmmParser(tx, instruction, rawTx.Meta.InnerInstructions[0], [2]uint{0, 0})
|
||||
if err != nil {
|
||||
t.Fatalf("metaoradlmmParser() error = %v", err)
|
||||
}
|
||||
if len(swaps) != 1 {
|
||||
t.Fatalf("metaoradlmmParser() swaps len = %d, want 1", len(swaps))
|
||||
}
|
||||
|
||||
swap := swaps[0]
|
||||
if !swap.Pool.Equals(event.LbPair) {
|
||||
t.Fatalf("swap.Pool = %s, want event %s", swap.Pool, event.LbPair)
|
||||
}
|
||||
if !swap.BaseMint.Equals(event.TokenX) {
|
||||
t.Fatalf("swap.BaseMint = %s, want event %s", swap.BaseMint, event.TokenX)
|
||||
}
|
||||
if !swap.QuoteMint.Equals(event.TokenY) {
|
||||
t.Fatalf("swap.QuoteMint = %s, want event %s", swap.QuoteMint, event.TokenY)
|
||||
}
|
||||
if nextOffset != ([2]uint{1, 0}) {
|
||||
t.Fatalf("nextOffset = %#v, want [2]uint{1, 0}", nextOffset)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDlmmSwapFeeInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseMint := testPublicKey(1)
|
||||
quoteMint := testPublicKey(2)
|
||||
baseProgram := testPublicKey(3)
|
||||
quoteProgram := testPublicKey(4)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
baseIsX bool
|
||||
swapForY bool
|
||||
wantFeeSide string
|
||||
wantFeeMint solana.PublicKey
|
||||
wantFeeProg solana.PublicKey
|
||||
wantDecimals uint8
|
||||
}{
|
||||
{
|
||||
name: "x is base and input is x",
|
||||
baseIsX: true,
|
||||
swapForY: true,
|
||||
wantFeeSide: "base",
|
||||
wantFeeMint: baseMint,
|
||||
wantFeeProg: baseProgram,
|
||||
wantDecimals: 6,
|
||||
},
|
||||
{
|
||||
name: "x is base and input is y",
|
||||
baseIsX: true,
|
||||
swapForY: false,
|
||||
wantFeeSide: "quote",
|
||||
wantFeeMint: quoteMint,
|
||||
wantFeeProg: quoteProgram,
|
||||
wantDecimals: 9,
|
||||
},
|
||||
{
|
||||
name: "y is base and input is x",
|
||||
baseIsX: false,
|
||||
swapForY: true,
|
||||
wantFeeSide: "quote",
|
||||
wantFeeMint: quoteMint,
|
||||
wantFeeProg: quoteProgram,
|
||||
wantDecimals: 9,
|
||||
},
|
||||
{
|
||||
name: "y is base and input is y",
|
||||
baseIsX: false,
|
||||
swapForY: false,
|
||||
wantFeeSide: "base",
|
||||
wantFeeMint: baseMint,
|
||||
wantFeeProg: baseProgram,
|
||||
wantDecimals: 6,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
feeAmount, feeSide, feeMint, feeProgram, feeDecimals := dlmmSwapFeeInfo(
|
||||
tc.baseIsX,
|
||||
tc.swapForY,
|
||||
123,
|
||||
baseMint,
|
||||
quoteMint,
|
||||
baseProgram,
|
||||
quoteProgram,
|
||||
6,
|
||||
9,
|
||||
)
|
||||
if !feeAmount.Equal(decimal.NewFromInt(123)) {
|
||||
t.Fatalf("feeAmount = %s, want 123", feeAmount)
|
||||
}
|
||||
if feeSide != tc.wantFeeSide {
|
||||
t.Fatalf("feeSide = %s, want %s", feeSide, tc.wantFeeSide)
|
||||
}
|
||||
if !feeMint.Equals(tc.wantFeeMint) {
|
||||
t.Fatalf("feeMint = %s, want %s", feeMint, tc.wantFeeMint)
|
||||
}
|
||||
if !feeProgram.Equals(tc.wantFeeProg) {
|
||||
t.Fatalf("feeProgram = %s, want %s", feeProgram, tc.wantFeeProg)
|
||||
}
|
||||
if feeDecimals != tc.wantDecimals {
|
||||
t.Fatalf("feeDecimals = %d, want %d", feeDecimals, tc.wantDecimals)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDlmmSwapLpFeeAmount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lpFee := dlmmSwapLpFeeAmount(100, 15, 5)
|
||||
if !lpFee.Equal(decimal.NewFromInt(80)) {
|
||||
t.Fatalf("lpFee = %s, want 80", lpFee)
|
||||
}
|
||||
|
||||
lpFee = dlmmSwapLpFeeAmount(10, 8, 5)
|
||||
if !lpFee.IsZero() {
|
||||
t.Fatalf("lpFee should floor at zero, got %s", lpFee)
|
||||
}
|
||||
}
|
||||
14
tx.go
14
tx.go
@@ -30,6 +30,12 @@ type Swap struct {
|
||||
User solana.PublicKey
|
||||
BaseAmount decimal.Decimal
|
||||
QuoteAmount decimal.Decimal
|
||||
FeeAmount decimal.Decimal
|
||||
LpFeeAmount decimal.Decimal
|
||||
FeeSide string
|
||||
FeeMint solana.PublicKey
|
||||
FeeTokenProgram solana.PublicKey
|
||||
FeeMintDecimals uint8
|
||||
|
||||
BaseReserve decimal.Decimal
|
||||
QuoteReserve decimal.Decimal
|
||||
@@ -52,19 +58,11 @@ type Swap struct {
|
||||
StartBinId int32
|
||||
EndBinId int32
|
||||
RemoveBp int32
|
||||
BinChanges []DlmmBinLiquidityChange
|
||||
PositionAccount solana.PublicKey
|
||||
|
||||
ConsumeUnit uint64
|
||||
}
|
||||
|
||||
type DlmmBinLiquidityChange struct {
|
||||
BinId int32
|
||||
AmountX decimal.Decimal
|
||||
AmountY decimal.Decimal
|
||||
BpsToRemove uint16
|
||||
}
|
||||
|
||||
type platformInfo struct {
|
||||
Platform string
|
||||
PlatformFee decimal.Decimal
|
||||
|
||||
Reference in New Issue
Block a user