Compare commits

...

18 Commits

Author SHA1 Message Date
bijianing97
fb8d93f426 Update dlmm fee 2026-04-11 08:34:21 +08:00
bijianing97
0cc843b370 Update dlmm fee 2026-04-11 08:27:34 +08:00
bijianing97
d9a214b4b4 Add dlmm add liquidity one side function 2026-03-25 11:34:46 +08:00
thloyi
047b549d0f option ComputeUnitsConsumed 2026-03-23 20:20:21 +08:00
bijianing97
9327eab010 Fix dlmm parser 2026-03-23 15:30:43 +08:00
bijianing97
0ef57cf79a Add dlmm add_liquidity_by_weight 2026-03-20 17:06:37 +08:00
cachalots
03030d817d update 2026-03-20 11:41:44 +08:00
bijianing97
401dca225a Add dlmm open and close 2026-03-19 14:10:14 +08:00
bijianing97
db8c8727f4 Add dlmm start and end bin 2026-03-18 14:38:25 +08:00
bijianing97
09de6ba649 Add dlmm claim fee and update dlmm add and remove 2026-03-16 11:37:19 +08:00
bijianing97
7a82990770 Update meteoradlmm remove and add enum 2026-03-16 10:14:50 +08:00
bijianing97
e82bcb3c07 Merge remote-tracking branch 'origin' 2026-03-12 16:22:09 +08:00
bijianing97
a74f769064 Add raydiumv4 swapv2 2026-03-12 16:21:38 +08:00
thloyi
1e276e8bd2 [tx] swap inner idx 2026-03-12 13:49:34 +08:00
thloyi
eb2bde98ac update parsed tx: add swap at instr idx 2026-03-12 13:49:34 +08:00
66f0d247f5 chore: add astralane fee address 2026-03-12 12:08:06 +08:00
879b7fefad chore: add PlatformDexScreener 2026-03-12 11:52:58 +08:00
149dfae378 chore: add trojan fee address 2026-03-10 11:23:15 +08:00
10 changed files with 2214 additions and 144 deletions

121
cmd/rpc_parse/main.go Normal file
View 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()
}
}

View File

@@ -41,6 +41,12 @@ var platformFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("9yMwSPk9mrXSN7yDHUuZurAh1sjbJsfpUqjZ7SvVtdco"): PlatformTrojan,
solana.MustPublicKeyFromBase58("92Med3qeK7duC5iiYsHX38H2f2twJfRsSx93oNrza2VH"): PlatformTrojan,
solana.MustPublicKeyFromBase58("65gDv7pZQCZELsNpNYSFEBtNFpWZAbxmRFB6BGMqFkHH"): PlatformTrojan,
solana.MustPublicKeyFromBase58("8jgg7moFJkHyTtAv9M6RBSPMp2oXeXhuiUMKW8YbYCWn"): PlatformTrojan,
solana.MustPublicKeyFromBase58("BJgbYMZgqm79gNrmm31tV3L8GQorw91XFm4m7evyfPjr"): PlatformTrojan,
solana.MustPublicKeyFromBase58("BWgb8wR1FEGiu1jCDSKuHKf752W27b4iN6SvoNCiK4qp"): PlatformTrojan,
solana.MustPublicKeyFromBase58("GV4Bt6ehW5x5dqtaWAJBSnz8uum5Z2Rp9P2Tr5iVuQn5"): PlatformTrojan,
solana.MustPublicKeyFromBase58("2jwHNxavSoMZMEDbT1eV9PcPt5dDcayCqM6MkgaPpmWQ"): PlatformTrojan,
solana.MustPublicKeyFromBase58("66N1M2aaDSdJFZ1d7YoVN4EU45ju6XiscapLVHn5FLms"): PlatformTrojan,
solana.MustPublicKeyFromBase58("4mih95RmBqfHYvEfqq6uGGLp1Fr3gVS3VNSEa3JVRfQK"): PlatformRaybot,
solana.MustPublicKeyFromBase58("3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N"): PlatformMoonshot,
solana.MustPublicKeyFromBase58("3kxSQybWEeQZsMuNWMRJH4TxrhwoDwfv41TNMLRzFP5A"): PlatformMEVX,
@@ -61,6 +67,7 @@ var platformFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("HkJYryz2BNeMQfuuSWDYktWt5fZLV26eK6nqu7EJycoG"): PlatformAxiom,
solana.MustPublicKeyFromBase58("BfFX9rUm8qTZiZjmeq9BktWVTNuG3YWMc5AvkrCKJike"): PlatformAxiom,
solana.MustPublicKeyFromBase58("2ApLdwLrGayEmxgpLX9BTR47Q2QprfMg5SpjrLeaK8s7"): PlatformAxiom,
solana.MustPublicKeyFromBase58("AVahywMVNRYzdgWrufSWrtdGXAeNEvfpJFxhVFK516mT"): PlatformDexScreener,
}
var mevAgentFeeAddresses = map[solana.PublicKey]string{
@@ -178,6 +185,7 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("soyascXFW5wEEYiwfEmHy2pNwomqzvggJosGVD6TJdY"): MevAgentSoyas,
solana.MustPublicKeyFromBase58("soyasDBdKjADwPz3xk82U3TNPRDKEWJj7wWLajNHZ1L"): MevAgentSoyas,
solana.MustPublicKeyFromBase58("soyasE2abjBAynmHbGWgEwk4ctBy7JMTUCNrMbjcnyH"): MevAgentSoyas,
solana.MustPublicKeyFromBase58("soyasF3QPWPAKKmgA3GjfWax1kmTT1aoqSGxPzVLNUQ"): MevAgentSoyas,
solana.MustPublicKeyFromBase58("ste11JV3MLMM7x7EJUM2sXcJC1H7F4jBLnP9a9PG8PH"): MevAgentStellium,
solana.MustPublicKeyFromBase58("ste11MWPjXCRfQryCshzi86SGhuXjF4Lv6xMXD2AoSt"): MevAgentStellium,
solana.MustPublicKeyFromBase58("ste11p5x8tJ53H1NbNQsRBg1YNRd4GcVpxtDw8PBpmb"): MevAgentStellium,
@@ -190,6 +198,7 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("ASde6y8pBCU1aityWHRpqT7pEAcEonjCgFUMeh5egRes"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("ASUv6G8Cj6zt71UAqD1aVtDC3CRn6FFddqF17ZiegrES"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("ASY4mvCtrACKFK8Jiuvqcu8fad9gGTzvfm5zp4megRes"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("astraubkDw81n4LuutzSQ8uzHCv4BhPVhfvTcYv8SKC"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("astraEJ2fEj8Xmy6KLG7B3VfbKfsHXhHrNdCQx7iGJK"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("B1ooMsWjc4SUVVuLyCu1ig2RdomQnHKgMzBMfmSo3DK"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("B1ooMZfUJmAvppzc5cr7eYG8Cenig4FbQGBytr4DGCh"): MevAgentAstralane,
@@ -201,6 +210,14 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("B1ooMauwuJPhHsXqt3uj7B92CAFG8kaD1Q2iGEmGYnx"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("B1ooMdjcY7zemxDWiH8jVZPxEMdHnE5AraWPHdHQoPj"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("B1ooMKzu6siJzQutP6a6oLiY3fpzgQnBZsAjxuAm9qo"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("AstrA1ejL4UeXC2SBP4cpeEmtcFPZVLxx3XGKXyCW6to"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("AsTra79FET4aCKWspPqeSFvjJNyp96SvAnrmyAxqg5b7"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("AsTRADtvb6tTmrsqULQ9Wji9PigDMjhfEMza6zkynEvV"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("AsTRAEoyMofR3vUPpf9k68Gsfb6ymTZttEtsAbv8Bk4d"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("AStrAJv2RN2hKCHxwUMtqmSxgdcNZbihCwc1mCSnG83W"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("Astran35aiQUF57XZsmkWMtNCtXGLzs8upfiqXxth2bz"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("AStRAnpi6kFrKypragExgeRoJ1QnKH7pbSjLAKQVWUum"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("ASTRaoF93eYt73TYvwtsv6fMWHWbGmMUZfVZPo3CRU9C"): MevAgentAstralane,
solana.MustPublicKeyFromBase58("Gu2UGEfze3Gg5cHuEC4jGbyCufgpev75RkVvBdKKtf12"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("E8wD3SMD1trozPrvSN9F6SyuUXD7rrFDuR3WexGziKG5"): MevAgent0slot,
solana.MustPublicKeyFromBase58("18hCV7f9CPmZRAH3QCNZaGHhHeNSfisQKeKuFkQsPLY"): MevAgent0slot,
@@ -319,6 +336,51 @@ var mevAgentFeeAddresses = map[solana.PublicKey]string{
solana.MustPublicKeyFromBase58("bgH7YhymSykyvMa3nAZpzvrn73owJHU5iB75S1aiLT9"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("pfngGVVQLiVRFbLWw3Ektiv17ef9NiRZbcgdAhh4ZEW"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("nEFs3jph8HJt7honu3k7XtGUufMnwAvSXmXcKSPxryP"): MevAgentNozomi,
solana.MustPublicKeyFromBase58("Fa1con11xLjPddfzRwRUB16sbFZggp2JeJkCeWREyR8X"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con11TM1RuAQzbQzYjTy4Ekfap9Lnc9fnEbQYEd6Q"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con113Bvi76nS5AzUiRDC2fqjfzkNMUNRLgQybMYt"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con1QGHJK232s8yZpzZZwqPexnAKcoyKj626LNsMv"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con1zUzb6qJVFz5tNkPq1Ahm8H1qKW7Q48252QbkQ"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con16d3MSwd3SAiwvr2LwgkpE7ot8zntbpuec8HAx"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con1i7mpa7Qc6epYJ6r4P9AbU77DFFz173r59Df1x"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con18nWn8TdAGL7JX8PertfMUGVSc899NawokJ4Bq"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con1GKusK2EqsfzrDzGPaYZSxQtFGzJiRMMU9Zm2g"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Fa1con1RDwVwM9VrJ53CwVefD3VU9c58EMpDawV7fLMi"): MevagentFa1con,
solana.MustPublicKeyFromBase58("Sp1x2AqpQckPLaWnWCJUNg8k6qQexfaEWcSRKf5JcDV"): MevagentBlocksprint,
solana.MustPublicKeyFromBase58("Sp4JHSh9cksfzXbgK7Pq2ovtn8LirLQydaJKTsiNT77"): MevagentBlocksprint,
solana.MustPublicKeyFromBase58("Sp1xMS2cbw83SZDNr4AGqkBYYLjb3LvVnmDSrTMaHkr"): MevagentBlocksprint,
solana.MustPublicKeyFromBase58("SpagSJmnh8E9cGT5Y431xPPaS2c1xLREGGCWN9yDeUf"): MevagentBlocksprint,
solana.MustPublicKeyFromBase58("SpWrza9E63MQuHeGnnfzmtLVCs3pBdjyKPXUABPo9nq"): MevagentBlocksprint,
solana.MustPublicKeyFromBase58("moon17L6BgxXRX5uHKudAmqVF96xia9h8ygcmG2sL3F"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moon26Sek222Md7ZydcAGxoKG832DK36CkLrS3PQY4c"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moon7fwyajcVstMoBnVy7UBcTx87SBtNoGGAaH2Cb8V"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonBtH9HvLHjLqi9ivyrMVKgFUsSfrz9BwQ9khhn1u"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonCJg8476LNFLptX1qrK8PdRsA1HD1R6XWyu9MB93"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonF2sz7qwAtdETnrgxNbjonnhGGjd6r4W4UC9284s"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonKfftMiGSak3cezvhEqvkPSzwrmQxQHXuspC96yj"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonQBUKBpkifLcTd78bfxxt4PYLwmJ5admLW6cBBs8"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonXwpKwoVkMegt5Bc776cSW793X1irL5hHV1vJ3JA"): MevAgentMoon,
solana.MustPublicKeyFromBase58("moonZ6u9E2fgk6eWd82621eLPHt9zuJuYECXAYjMY1C"): MevAgentMoon,
solana.MustPublicKeyFromBase58("SpEEdz8S1KorkMZqjMUxfxrmWwofmp6ReNP2Nx6CUmq"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("SpeeDy3GJM4wcrQmk1itRFWgidvxX4rwjTLMv78wwjE"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("SPeEdva37vW8vRtqgYjprQs1g3965icfVN5Rt7SMAyh"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("speEdrSEpox5GUfHWcBc7tQjRuSfUin2yvB7qoYvvJh"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("SPeEDmkHkN3A2roSZf6aZyEMsmrGqTHKqwP51y2Y4rV"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("SpeedLdTJXh2RKpXEaP8JCxkWoUVXhtdPQ1EnxBJMxc"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("SpEediGKLbbXndSYTzwmz6Z3NDgHQLDcTDEvGFkSMH9"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("speede8xCcUq2Tiv1efXeTuE3k9TDNq8TnGKaKSc6J4"): MevAgentSpeedlanding,
solana.MustPublicKeyFromBase58("harkEpXoJv5qVzHaN7HSuUAd6PHjyMcFMcDYBMDJCEQ"): MevAgentAllenhark,
solana.MustPublicKeyFromBase58("harkm2BTWxZuszoNpZnfe84jRbQTg6KGHaQBmWzDGQQ"): MevAgentAllenhark,
solana.MustPublicKeyFromBase58("harkR2YJ4Dpt4UDJTcBirjnSPBhNpQFcoFkNpCkVqNk"): MevAgentAllenhark,
solana.MustPublicKeyFromBase58("t3QLYyXH4vZYbEifLqjD581t5dPVhq9LABxWceySzL2"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t46SqGmwStEffUMp1fr2xmv5uyR85TB9annJuLKLf83"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t1TcSg9biJsz4NjKjhopK8QZzPS4KzBgFSszu5QTGgF"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t2XFAFBaUkCzxJwEbLWFX9PKFjfBCp2tSyFtx5z4RZM"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t55hdzzftxWkYy3J8t32C9RRcZDuMZ4LDuBmbTzJFkU"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t6UtTQLUGHJJrzxAb8PBBZdZKra8SWUqvTv9zPnxKNz"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t7qUQU35sLpPydh42BcPmtEfTWW8gBe4Ry3gjwVnokJ"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t8pPgarSK3TnuLbbHmoE1RCQdLxfxuPqNTyFjBKahok"): MevAgentRaiden,
solana.MustPublicKeyFromBase58("t96GGdw3MiaGR993XN8PSsRpKGXx56t5Wf6zcF1hBpY"): MevAgentRaiden,
}
var entryContractAddresses = map[solana.PublicKey]string{

35
enum.go
View File

@@ -1,20 +1,26 @@
package pump_parser
const (
MevAgentJito = "jito"
MevAgent0slot = "0slot"
MevAgentBlocxRoute = "blocxroute"
MevAgentNozomi = "nozomi"
MevAgentNextBlock = "nextblock"
MevAgentHelius = "helius"
MevAgentNode1 = "node1"
MevAgentFlashBlock = "flashBlock"
MevAgentUnknown = "unknown"
MevAgentBlockRazor = "blockrazor"
MevAgentFast = "fast"
MevAgentSoyas = "soyas"
MevAgentStellium = "stellium"
MevAgentAstralane = "astralane"
MevAgentJito = "jito"
MevAgent0slot = "0slot"
MevAgentBlocxRoute = "blocxroute"
MevAgentNozomi = "nozomi"
MevAgentNextBlock = "nextblock"
MevAgentHelius = "helius"
MevAgentNode1 = "node1"
MevAgentFlashBlock = "flashBlock"
MevAgentUnknown = "unknown"
MevAgentBlockRazor = "blockrazor"
MevAgentFast = "fast"
MevAgentSoyas = "soyas"
MevAgentStellium = "stellium"
MevAgentAstralane = "astralane"
MevagentFa1con = "fa1con"
MevagentBlocksprint = "blocksprint"
MevAgentMoon = "moon"
MevAgentSpeedlanding = "speedlanding"
MevAgentAllenhark = "allenhark"
MevAgentRaiden = "raiden"
)
const (
@@ -75,6 +81,7 @@ const (
PlatformMaestro = "maestro"
PlatformBonkBot = "bonkbot"
PlatformPadre = "padre"
PlatformDexScreener = "dexscreener"
// used to flag transactions impersonating platform users
PlatformFake = "fake"

69
meta.go
View File

@@ -68,25 +68,50 @@ var (
)
var (
meteoraInitializeLbPairDiscriminator = calculateDiscriminator("global:initialize_lb_pair2")
meteoraInitializeLbPairEventDiscriminator = calculateDiscriminator("event:LbPairCreate")
meteoraDlmmSwapDiscriminator = calculateDiscriminator("global:swap")
meteoraDlmmSwap2Discriminator = calculateDiscriminator("global:swap2")
meteoraDlmmSwapExactOutDiscriminator = calculateDiscriminator("global:swap_exact_out")
meteoraDlmmSwapExactOut2Discriminator = calculateDiscriminator("global:swap_exact_out2")
meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact")
meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2")
meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap")
meteoraDlmmAddLiquidityDiscriminator = calculateDiscriminator("global:add_liquidity")
meteoraDlmmAddLiquidity2Discriminator = calculateDiscriminator("global:add_liquidity2")
meteoraDlmmAddLiquidityByStrategyDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy")
meteoraDlmmAddLiquidityByStrategy2Discriminator = calculateDiscriminator("global:add_liquidity_by_strategy2")
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")
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")
meteoraDlmmSwapExactOutDiscriminator = calculateDiscriminator("global:swap_exact_out")
meteoraDlmmSwapExactOut2Discriminator = calculateDiscriminator("global:swap_exact_out2")
meteoraDlmmSwapWithPriceImpactDiscriminator = calculateDiscriminator("global:swap_with_price_impact")
meteoraDlmmSwapWithPriceImpact2Discriminator = calculateDiscriminator("global:swap_with_price_impact2")
meteoraDlmmInitializePositionDiscriminator = calculateDiscriminator("global:initialize_position")
meteoraDlmmInitializePosition2Discriminator = calculateDiscriminator("global:initialize_position2")
meteoraDlmmInitializePositionByOperatorDiscriminator = calculateDiscriminator("global:initialize_position_by_operator")
meteoraDlmmInitializePositionPdaDiscriminator = calculateDiscriminator("global:initialize_position_pda")
meteoraDlmmClosePositionDiscriminator = calculateDiscriminator("global:close_position")
meteoraDlmmClosePosition2Discriminator = calculateDiscriminator("global:close_position2")
meteoraDlmmClosePositionIfEmptyDiscriminator = calculateDiscriminator("global:close_position_if_empty")
meteoraDlmmSwapEventDiscriminator = calculateDiscriminator("event:Swap")
meteoraDlmmAddLiquidityDiscriminator = calculateDiscriminator("global:add_liquidity")
meteoraDlmmAddLiquidity2Discriminator = calculateDiscriminator("global:add_liquidity2")
meteoraDlmmAddLiquidityByStrategyDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy")
meteoraDlmmAddLiquidityByStrategy2Discriminator = calculateDiscriminator("global:add_liquidity_by_strategy2")
meteoraDlmmAddLiquidityByWeightDiscriminator = calculateDiscriminator("global:add_liquidity_by_weight")
meteoraDlmmAddLiquidityOneSideDiscriminator = calculateDiscriminator("global:add_liquidity_one_side")
meteoraDlmmAddLiquidityOneSidePreciseDiscriminator = calculateDiscriminator("global:add_liquidity_one_side_precise")
meteoraDlmmAddLiquidityOneSidePrecise2Discriminator = calculateDiscriminator("global:add_liquidity_one_side_precise2")
meteoraDlmmAddLiquidityByStrategyOneSideDiscriminator = calculateDiscriminator("global:add_liquidity_by_strategy_one_side")
meteoraDlmmClaimFeeDiscriminator = calculateDiscriminator("global:claim_fee")
meteoraDlmmClaimFee2Discriminator = calculateDiscriminator("global:claim_fee2")
meteoraDlmmRebalanceLiquidityDiscriminator = calculateDiscriminator("global:rebalance_liquidity")
meteoraDlmmRemoveAllLiquidityDiscriminator = calculateDiscriminator("global:remove_all_liquidity")
meteoraDlmmRemoveLiquidityDiscriminator = calculateDiscriminator("global:remove_liquidity")
meteoraDlmmRemoveLiquidity2Discriminator = calculateDiscriminator("global:remove_liquidity2")
meteoraDlmmRemoveLiquidityByRangeDiscriminator = calculateDiscriminator("global:remove_liquidity_by_range")
meteoraDlmmRemoveLiquidityByRange2Discriminator = calculateDiscriminator("global:remove_liquidity_by_range2")
meteoraDlmmAddLiquidityEventDiscriminator = calculateDiscriminator("event:AddLiquidity")
meteoraDlmmClaimFeeEventDiscriminator = calculateDiscriminator("event:ClaimFee")
meteoraDlmmClaimFee2EventDiscriminator = calculateDiscriminator("event:ClaimFee2")
meteoraDlmmPositionCloseEventDiscriminator = calculateDiscriminator("event:PositionClose")
meteoraDlmmPositionCreateEventDiscriminator = calculateDiscriminator("event:PositionCreate")
meteoraDlmmRebalancingEventDiscriminator = calculateDiscriminator("event:Rebalancing")
meteoraDlmmRemoveLiquidityEventDiscriminator = calculateDiscriminator("event:RemoveLiquidity")
)
var (
@@ -200,8 +225,10 @@ var (
const (
raydiumV4InitializePoolDiscriminator = uint8(1)
raydiumV4SwapBaseInDiscriminator = uint8(9)
raydiumV4SwapBaseOutDiscriminator = uint8(11)
raydiumV4SwapBaseInDiscriminator = uint8(9)
raydiumV4SwapBaseOutDiscriminator = uint8(11)
raydiumV4SwapBaseInV2Discriminator = uint8(16)
raydiumV4SwapBaseOutV2Discriminator = uint8(17)
raydiumV4AddLiquidityDiscriminator = uint8(3)
raydiumV4RemoveLiquidityDiscriminator = uint8(4)

File diff suppressed because it is too large Load Diff

424
metaoradlmm_test.go Normal file
View File

@@ -0,0 +1,424 @@
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)
}
}
func TestDlmmSwapFeeBpsString(t *testing.T) {
t.Parallel()
feeBps := agbinary.Uint128{Lo: 12345}
if got := dlmmSwapFeeBpsString(feeBps); got != "12345" {
t.Fatalf("dlmmSwapFeeBpsString() = %s, want 12345", got)
}
}

View File

@@ -127,6 +127,9 @@ func (tx *Tx) Parser() error {
//fmt.Printf("parser failed tx error: %s, block: %d tx: %s\n", err, tx.Block, tx.GetTxHash())
}
if len(swaps) > 0 {
for i := range swaps {
swaps[i].InstrIdx = tx.Err.Index
}
tx.Swaps = swaps
}
for i, instr := range tx.rawTx.Transaction.Message.Instructions {
@@ -172,6 +175,7 @@ func (tx *Tx) Parser() error {
swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9))
}
}
swap.InstrIdx = uint8(i)
tx.Swaps = append(tx.Swaps, swap)
}
@@ -219,6 +223,8 @@ func (tx *Tx) Parser() error {
swap.AfterSOLBalance = decimal.NewFromUint64(tx.rawTx.Meta.PostBalances[userIdx]).Div(decimal.NewFromInt(1e9))
}
}
swap.InstrIdx = uint8(i)
swap.InnerIdx = uint8(j)
tx.Swaps = append(tx.Swaps, swap)
}
// tx.Swaps = append(tx.Swaps, swaps...)

View File

@@ -872,7 +872,9 @@ func ConvertYellowstoneGrpcTransactionToSolanaTransaction(y *pb.SubscribeUpdateT
}
sTx.Meta.Fee = meta.Fee
//sTx.Meta.InnerInstructions = meta.InnerInstructions
sTx.Meta.ComputeUnitsConsumed = *meta.ComputeUnitsConsumed
if meta.ComputeUnitsConsumed != nil {
sTx.Meta.ComputeUnitsConsumed = *meta.ComputeUnitsConsumed
}
for _, innerInstr := range meta.InnerInstructions {
var instrs []Instruction
for _, instr := range innerInstr.Instructions {

View File

@@ -29,6 +29,8 @@ func raydiumV4Parser(tx *Tx, instruction Instruction, innerInstructions InnerIns
return raydiumv4WithdrawPNLParser(tx, instruction, innerInstructions, offset)
case raydiumV4SwapBaseInDiscriminator, raydiumV4SwapBaseOutDiscriminator:
return raydiumv4SwapParser(tx, instruction, innerInstructions, offset)
case raydiumV4SwapBaseInV2Discriminator, raydiumV4SwapBaseOutV2Discriminator:
return raydiumv4SwapV2Parser(tx, instruction, innerInstructions, offset)
default:
return nil, increaseOffset(offset), InstructionIgnoredError
@@ -397,3 +399,99 @@ func raydiumv4SwapParser(tx *Tx, instruction Instruction, innerInstructions Inne
},
}, offset, nil
}
func raydiumv4SwapV2Parser(tx *Tx, instruction Instruction, innerInstructions InnerInstructions, offset [2]uint) ([]Swap, [2]uint, error) {
accountsLen := len(instruction.Accounts)
if accountsLen != 8 {
return nil, increaseOffset(offset), fmt.Errorf("invalid number of accounts for raydiumv4 swapv2 instruction, offset %d, %d", offset[0], offset[1])
}
var entryContract = tx.rawTx.accountList[tx.rawTx.Transaction.Message.Instructions[offset[0]].ProgramIDIndex]
ammAccount := tx.rawTx.accountList[instruction.Accounts[1]]
user := tx.rawTx.accountList[instruction.Accounts[7]]
userSourceTokenAccount := tx.rawTx.accountList[instruction.Accounts[5]]
userDestinationTokenAccount := tx.rawTx.accountList[instruction.Accounts[6]]
baseVaultIdx := instruction.Accounts[3]
quoteVaultIdx := instruction.Accounts[4]
baseTokenbalance, err := getTokenBalanceAfterTx(tx.rawTx, baseVaultIdx)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get base vault balance after tx: %v", err)
}
quoteTokenbalance, err := getTokenBalanceAfterTx(tx.rawTx, quoteVaultIdx)
if err != nil {
return nil, increaseOffset(offset), fmt.Errorf("failed to get quote vault balance after tx: %v", err)
}
inners, err := getInnerInstructions(innerInstructions, offset[1])
if err != nil {
return nil, increaseOffset(offset), err
}
var nextIndex int
var srcFound, destFound bool
var baseAmount, quoteAmount decimal.Decimal
var event string
for i, inner := range inners {
from, to, amount, err := parseTokenTransfer(tx.rawTx, inner)
if err != nil {
continue
}
if from.Equals(userSourceTokenAccount) && !srcFound {
if to.Equals(tx.rawTx.accountList[baseVaultIdx]) {
event = "sell"
baseAmount = decimal.NewFromUint64(amount)
srcFound = true
} else if to.Equals(tx.rawTx.accountList[quoteVaultIdx]) {
event = "buy"
quoteAmount = decimal.NewFromUint64(amount)
srcFound = true
}
} else if to.Equals(userDestinationTokenAccount) && !destFound {
if from.Equals(tx.rawTx.accountList[quoteVaultIdx]) {
event = "sell"
quoteAmount = decimal.NewFromUint64(amount)
destFound = true
} else if from.Equals(tx.rawTx.accountList[baseVaultIdx]) {
event = "buy"
baseAmount = decimal.NewFromUint64(amount)
destFound = true
}
}
if srcFound && destFound {
nextIndex = i + 1
break
}
}
if !srcFound || !destFound {
return nil, increaseOffset(offset), fmt.Errorf("raydiumv4 failed to find token transfer inner instruction for swapv2, offset %d, %d", offset[0], offset[1])
}
offset[1] += uint(nextIndex + 1)
userBase := getAccountBalanceAfterTx(tx.rawTx, instruction.Accounts[5])
userQuote := getAccountBalanceAfterTx(tx.rawTx, instruction.Accounts[6])
baseReserve, _ := decimal.NewFromString(baseTokenbalance.UITokenAmount.Amount)
quoteReserve, _ := decimal.NewFromString(quoteTokenbalance.UITokenAmount.Amount)
return []Swap{
{
Program: SolProgramRaydiumV4,
Event: event,
Pool: ammAccount,
BaseMint: baseTokenbalance.MintAccount,
QuoteMint: quoteTokenbalance.MintAccount,
BaseTokenProgram: baseTokenbalance.ProgramIDAccount,
QuoteTokenProgram: quoteTokenbalance.ProgramIDAccount,
BaseMintDecimals: uint8(baseTokenbalance.UITokenAmount.Decimals),
QuoteMintDecimals: uint8(quoteTokenbalance.UITokenAmount.Decimals),
User: user,
BaseAmount: baseAmount,
QuoteAmount: quoteAmount,
BaseReserve: baseReserve,
QuoteReserve: quoteReserve,
Mayhem: false,
UserBaseBalance: userBase,
UserQuoteBalance: userQuote,
EntryContract: entryContract,
},
}, offset, nil
}

25
tx.go
View File

@@ -12,6 +12,9 @@ type Swap struct {
TxIndex int
InstrIdx uint8
InnerIdx uint8
Pool solana.PublicKey
BaseMint solana.PublicKey
QuoteMint solana.PublicKey
@@ -45,20 +48,22 @@ type Swap struct {
AfterSOLBalance decimal.Decimal
//For meteora dlmm
StartBinId int32
EndBinId int32
BinChanges []DlmmBinLiquidityChange
ActiveBinId int32
StartBinId int32
EndBinId int32
RemoveBp int32
PositionAccount solana.PublicKey
FeeAmount decimal.Decimal
FeeBps string
LpFeeAmount decimal.Decimal
FeeSide string
FeeMint solana.PublicKey
FeeTokenProgram solana.PublicKey
FeeMintDecimals uint8
ConsumeUnit uint64
}
type DlmmBinLiquidityChange struct {
BinId int32
AmountX decimal.Decimal
AmountY decimal.Decimal
BpsToRemove uint16
}
type platformInfo struct {
Platform string
PlatformFee decimal.Decimal