Add MetaOra DLMM parser
This commit is contained in:
19
meta.go
19
meta.go
@@ -35,8 +35,11 @@ var pumpMigrateEventDiscriminator = calculateDiscriminator("event:CompletePumpAm
|
||||
var pumpBuyEventDiscriminator = [8]byte{189, 219, 127, 211, 78, 230, 97, 238}
|
||||
|
||||
var (
|
||||
pumpAmmProgram = solana.MustPublicKeyFromBase58("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA")
|
||||
wSolMint = solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112")
|
||||
pumpAmmProgram = solana.MustPublicKeyFromBase58("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA")
|
||||
wSolMint = solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112")
|
||||
usdcMint = solana.MustPublicKeyFromBase58("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
|
||||
usd1Mint = solana.MustPublicKeyFromBase58("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB")
|
||||
meteoraDlmmProgram = solana.MustPublicKeyFromBase58("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo")
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -63,6 +66,16 @@ var (
|
||||
pumpAmmDepositEventDiscriminator = calculateDiscriminator("event:DepositEvent")
|
||||
)
|
||||
|
||||
var (
|
||||
meteoraDlmmSwapDiscriminator = calculateDiscriminator("global:swap")
|
||||
meteoraDlmmSwap2Discriminator = calculateDiscriminator("global:swap2")
|
||||
meteoraDlmmSwapExactOutDiscriminator = calculateDiscriminator("global:swap_exact_out")
|
||||
meteoraDlmmSwapExactOut2Discriminator = calculateDiscriminator("global:swap_exact_out2")
|
||||
meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact")
|
||||
meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2")
|
||||
meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap")
|
||||
)
|
||||
|
||||
// Program PumpAmm program ID
|
||||
|
||||
var budgGetProgram = solana.MustPublicKeyFromBase58("ComputeBudget111111111111111111111111111111")
|
||||
@@ -75,3 +88,5 @@ var createAccountWithSeedDiscriminator = uint32(3)
|
||||
var systemProgram = solana.MustPublicKeyFromBase58("11111111111111111111111111111111")
|
||||
|
||||
var raydiumLaunchLabProgramID = solana.MustPublicKeyFromBase58("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj")
|
||||
|
||||
var eventDiscriminator = [8]byte{228, 69, 165, 46, 81, 203, 154, 29}
|
||||
|
||||
468
metaoradlmm.go
Normal file
468
metaoradlmm.go
Normal file
@@ -0,0 +1,468 @@
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
offset[1] += 1
|
||||
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 {
|
||||
case meteoraDlmmSwapDiscriminator, meteoraDlmmSwapExactOutDiscriminator, meteoraDlmmSwapWithPriceImpactDiscriminator:
|
||||
return metaoradlmmSwapParser(tx, instruction, innerInstructions, offset)
|
||||
case meteoraDlmmSwap2Discriminator, meteoraDlmmSwapExactOut2Discriminator, meteoraDlmmSwapWithPriceImpact2Discriminator:
|
||||
return metaoradlmmSwap2Parser(tx, instruction, innerInstructions, offset)
|
||||
default:
|
||||
return nil, increaseOffset(offset), InstructionIgnoredError
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decode := instruction.Data
|
||||
if len(decode) < 8 {
|
||||
offset[1] += 1
|
||||
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])
|
||||
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])
|
||||
}
|
||||
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])
|
||||
}
|
||||
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])
|
||||
}
|
||||
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]
|
||||
|
||||
swapEvent, nextOffset, err := dlmmSwapEventFromInnerInstructions(innerInstructions, instruction, offset)
|
||||
if err != nil {
|
||||
return nil, nextOffset, err
|
||||
}
|
||||
offset = nextOffset
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
BaseReserve: baseReserve,
|
||||
QuoteReserve: quoteReserve,
|
||||
UserBaseBalance: userBase,
|
||||
UserQuoteBalance: userQuote,
|
||||
EntryContract: entryContract,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func dlmmSwapEventFromInnerInstructions(innerInstructions InnerInstructions, instruction Instruction, offset [2]uint) (dlmmSwapEvent, [2]uint, error) {
|
||||
var prefixLen = offset[1]
|
||||
inners, err := getInnerInstructions(innerInstructions, prefixLen)
|
||||
if err != nil {
|
||||
return dlmmSwapEvent{}, 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
|
||||
}
|
||||
event, ok := dlmmDecodeSwapEvent(innerInstr.Data)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if offset[1] == 0 {
|
||||
offset[0] += 1
|
||||
} else {
|
||||
offset[1] += uint(innerIndex) + 1 + prefixLen
|
||||
}
|
||||
return event, offset, nil
|
||||
}
|
||||
return dlmmSwapEvent{}, increaseOffset(offset), fmt.Errorf("meteora dlmm swap event not found, offset, %d, %d", offset[0], prefixLen)
|
||||
}
|
||||
|
||||
func dlmmDecodeSwapEvent(data []byte) (dlmmSwapEvent, bool) {
|
||||
switch {
|
||||
case len(data) >= 8 && bytes.Equal(data[:8], meteoraDlmmSwapEventDiscriminator[:]):
|
||||
var event dlmmSwapEvent
|
||||
if err := agbinary.NewBorshDecoder(data[8:]).Decode(&event); err != nil {
|
||||
return dlmmSwapEvent{}, false
|
||||
}
|
||||
return event, true
|
||||
case len(data) >= 16 &&
|
||||
bytes.Equal(data[:8], eventDiscriminator[:]) &&
|
||||
bytes.Equal(data[8:16], meteoraDlmmSwapEventDiscriminator[:]):
|
||||
var event dlmmSwapEvent
|
||||
if err := agbinary.NewBorshDecoder(data[16:]).Decode(&event); err != nil {
|
||||
return dlmmSwapEvent{}, false
|
||||
}
|
||||
return event, true
|
||||
default:
|
||||
return dlmmSwapEvent{}, false
|
||||
}
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
programPos := eventAuthorityPos + 1
|
||||
if programPos >= len(accounts) {
|
||||
continue
|
||||
}
|
||||
if !accountList[accounts[eventAuthorityPos]].Equals(meteoraDlmmEventAuthority) {
|
||||
continue
|
||||
}
|
||||
if !accountList[accounts[programPos]].Equals(meteoraDlmmProgram) {
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
)
|
||||
|
||||
var swapPrograms = map[solana.PublicKey]swapParser{
|
||||
pumpAmmProgram: pumpAmmParser,
|
||||
pumpProgram: pumpParser,
|
||||
pumpAmmProgram: pumpAmmParser,
|
||||
pumpProgram: pumpParser,
|
||||
meteoraDlmmProgram: metaoradlmmParser,
|
||||
}
|
||||
|
||||
var actionPrograms = map[solana.PublicKey]actionParser{
|
||||
|
||||
Reference in New Issue
Block a user