Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b2a9af978 | ||
| cddfcc3eef | |||
| 6b35f61d95 | |||
| 2b93e7a5d4 | |||
|
|
edaed7d333 | ||
| 50d9212e2d | |||
| e90a2533fd | |||
| 84645d9c09 | |||
| b860ff1719 | |||
| c7e9354e07 | |||
| a1f9f4f9a9 | |||
| 21eba4264f | |||
| 17a3c9e89c | |||
| 8e734eb793 | |||
| 33c8a16015 | |||
| 62d48e5a22 | |||
|
|
3d2fd35344 |
@@ -7,7 +7,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/gagliardetto/solana-go/programs/address-lookup-table"
|
||||
addresslookuptable "github.com/gagliardetto/solana-go/programs/address-lookup-table"
|
||||
"github.com/gagliardetto/solana-go/rpc"
|
||||
|
||||
"github.com/samlior/libsam/v2/pkg/shreder"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
const (
|
||||
rpcURL = "https://staked.helius-rpc.com?api-key=5adcf1f9-5719-43d1-bf3f-c2d4e1e5f94d"
|
||||
txSignature = "4gzWkLRWNLbkBdvyCqg2M4unWA7yg4DdMg8dGTnapw2USsefd9TjXVArhv22qJE9gtex46NwXC4xp1FtNZ1TmjAM"
|
||||
txSignature = "4oSnHnDSscjmc6XX1rjXCEBavoLR9wkdZvGCAUn928iLWqrCwt2a6mgJpjP4NHqrCboUC82ugrjjEbNGNYAagkue"
|
||||
labelFilter = ""
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package shreder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
@@ -10,6 +11,8 @@ import (
|
||||
var bloomRouterProgramID = solana.MustPublicKeyFromBase58("b1oomGGqPKGD6errbyfbVMBuzSC8WtAAYo8MwNafWW1")
|
||||
var pumpFunAccount = solana.MustPublicKeyFromBase58("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf")
|
||||
|
||||
var bloomRouterSwapDiscriminator = []byte{0xf1, 0x57, 0x27, 0x38, 0x01, 0x4d, 0x0e, 0x63}
|
||||
|
||||
type bloomRouterArgs struct {
|
||||
Side uint16
|
||||
SolAmount uint64
|
||||
@@ -26,11 +29,84 @@ func parseBloomRouterInstruction(tx VersionedTransaction, instructionIndex int)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !bytes.Equal(instruction.Data[:8], bloomRouterSwapDiscriminator) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
findPumpFun := func() (solana.PublicKey, solana.PublicKey, error) {
|
||||
var mint solana.PublicKey
|
||||
foundPumpFun := false
|
||||
for i, acctIdx := range instruction.Accounts {
|
||||
key, err := tx.GetAccount(int(acctIdx))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, err
|
||||
}
|
||||
if key.Equals(pumpFunAccount) {
|
||||
if i+2 >= len(instruction.Accounts) {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("accounts too short for pumpfun mint, idx=%d len=%d", i, len(instruction.Accounts))
|
||||
}
|
||||
mintKey, err := tx.GetAccount(int(instruction.Accounts[i+2]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, err
|
||||
}
|
||||
mint = mintKey
|
||||
foundPumpFun = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundPumpFun {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, nil
|
||||
}
|
||||
return mint, wrappedSOL, nil
|
||||
}
|
||||
|
||||
findRaydiumLaunchLab := func(isBuy bool) (solana.PublicKey, solana.PublicKey, error) {
|
||||
offset := 0
|
||||
if isBuy {
|
||||
offset = 10
|
||||
} else {
|
||||
offset = 9
|
||||
}
|
||||
var base solana.PublicKey
|
||||
var quote solana.PublicKey
|
||||
foundRaydiumLaunchLab := false
|
||||
for i, acctIdx := range instruction.Accounts {
|
||||
key, err := tx.GetAccount(int(acctIdx))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, err
|
||||
}
|
||||
if key.Equals(raydiumLaunchLabProgramID) {
|
||||
if i+offset+1 >= len(instruction.Accounts) {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, fmt.Errorf("accounts too short for raydium launch lab mint, idx=%d len=%d", i, len(instruction.Accounts))
|
||||
}
|
||||
var err error
|
||||
base, err = tx.GetAccount(int(instruction.Accounts[i+offset]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, err
|
||||
}
|
||||
quote, err = tx.GetAccount(int(instruction.Accounts[i+offset+1]))
|
||||
if err != nil {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, err
|
||||
}
|
||||
foundRaydiumLaunchLab = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundRaydiumLaunchLab {
|
||||
return solana.PublicKey{}, solana.PublicKey{}, nil
|
||||
}
|
||||
return base, quote, nil
|
||||
}
|
||||
|
||||
var (
|
||||
amount uint64
|
||||
sol uint64
|
||||
exactIn bool
|
||||
event string
|
||||
program string
|
||||
base solana.PublicKey
|
||||
quote solana.PublicKey
|
||||
err error
|
||||
)
|
||||
|
||||
args, err := decodeBloomRouterArgs(instruction.Data)
|
||||
@@ -41,8 +117,39 @@ func parseBloomRouterInstruction(tx VersionedTransaction, instructionIndex int)
|
||||
case 0:
|
||||
event = "buy"
|
||||
exactIn = true
|
||||
program = "Pump"
|
||||
base, quote, err = findPumpFun()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 1:
|
||||
event = "sell"
|
||||
program = "Pump"
|
||||
base, quote, err = findPumpFun()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 0x0b00:
|
||||
event = "buy"
|
||||
exactIn = true
|
||||
program = "RaydiumLaunchLab"
|
||||
base, quote, err = findRaydiumLaunchLab(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !quote.Equals(wrappedSOL) {
|
||||
return nil, nil
|
||||
}
|
||||
case 0x0b01:
|
||||
event = "sell"
|
||||
program = "RaydiumLaunchLab"
|
||||
base, quote, err = findRaydiumLaunchLab(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !quote.Equals(wrappedSOL) {
|
||||
return nil, nil
|
||||
}
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
@@ -61,43 +168,17 @@ func parseBloomRouterInstruction(tx VersionedTransaction, instructionIndex int)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var mint solana.PublicKey
|
||||
foundPumpFun := false
|
||||
for i, acctIdx := range instruction.Accounts {
|
||||
key, err := tx.GetAccount(int(acctIdx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if key.Equals(pumpFunAccount) {
|
||||
if i+2 >= len(instruction.Accounts) {
|
||||
return nil, fmt.Errorf("accounts too short for pumpfun mint, idx=%d len=%d", i, len(instruction.Accounts))
|
||||
}
|
||||
mintKey, err := tx.GetAccount(int(instruction.Accounts[i+2]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mint = mintKey
|
||||
foundPumpFun = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundPumpFun {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return TxSignalBatch{&TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "bloomrouter",
|
||||
Maker: maker.String(),
|
||||
Token0Address: mint.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Address: base.String(),
|
||||
Token1Address: quote.String(),
|
||||
Token0Amount: formatTokenAmount(amount),
|
||||
Token1Amount: formatSolAmount(sol),
|
||||
Program: "Pump",
|
||||
Program: program,
|
||||
Event: event,
|
||||
ExactSOL: exactIn,
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: amount,
|
||||
Token1AmountUint64: sol,
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
package shreder
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/near/borsh-go"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var flasProgramID = solana.MustPublicKeyFromBase58("FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9")
|
||||
var (
|
||||
flasBuyTokensIX = []byte{0x00, 0x1, 0x1b}
|
||||
flasSellTokensIX = []byte{0x01, 0x1, 0x1a}
|
||||
flasAmmBuyTokensIX = []byte{0x00, 0x2, 0x2}
|
||||
flasAmmSellTokensIX = []byte{0x01, 0x2, 0x2}
|
||||
flasBuyTokensIXs = [][]byte{
|
||||
{0x00, 0x01, 0x21},
|
||||
{0x00, 0x01, 0x1b},
|
||||
}
|
||||
flasSellTokensIXs = [][]byte{
|
||||
{0x01, 0x01, 0x1a},
|
||||
}
|
||||
flasAmmBuyTokensIXs = [][]byte{
|
||||
{0x00, 0x02, 0x1f},
|
||||
{0x00, 0x02, 0x02},
|
||||
}
|
||||
flasAmmSellTokensIXs = [][]byte{
|
||||
{0x01, 0x02, 0x1f},
|
||||
{0x01, 0x02, 0x02},
|
||||
}
|
||||
flasBonkBuyTokensIXs = [][]byte{
|
||||
{0x00, 0x02, 0x07},
|
||||
}
|
||||
flasBonkSellTokensIXs = [][]byte{
|
||||
{0x01, 0x02, 0x07},
|
||||
}
|
||||
)
|
||||
|
||||
type flasArgs struct {
|
||||
@@ -22,6 +39,26 @@ type flasArgs struct {
|
||||
Placeholder [3]uint8
|
||||
}
|
||||
|
||||
func decodeFlasArgs(data []byte) (flasArgs, error) {
|
||||
if len(data) < 20 {
|
||||
return flasArgs{}, fmt.Errorf("data too short for args flas instruction, len: %d", len(data))
|
||||
}
|
||||
return flasArgs{
|
||||
Amount1: binary.LittleEndian.Uint64(data[1:9]),
|
||||
Amount2: binary.LittleEndian.Uint64(data[9:17]),
|
||||
Placeholder: [3]uint8{data[17], data[18], data[19]},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func matchFlasMethod(data []byte, methods [][]byte) bool {
|
||||
for _, method := range methods {
|
||||
if matchMethod(data, method) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseFlasInstruction(tx VersionedTransaction, instructionIndex int) (TxSignalBatch, error) {
|
||||
if instructionIndex >= len(tx.Instructions) {
|
||||
return nil, fmt.Errorf("instruction index out of bounds")
|
||||
@@ -38,21 +75,22 @@ func parseFlasInstruction(tx VersionedTransaction, instructionIndex int) (TxSign
|
||||
return nil, fmt.Errorf("data too short for args flas instruction, len: %d", len(instruction.Data))
|
||||
}
|
||||
methodData := instruction.Data[17:20]
|
||||
//if !matchMethod(methodData, flasBuyTokensIX) {
|
||||
// return nil, nil
|
||||
//}
|
||||
var (
|
||||
err error
|
||||
txSignal *TxSignal
|
||||
)
|
||||
if matchMethod(methodData, flasBuyTokensIX) {
|
||||
if matchFlasMethod(methodData, flasBuyTokensIXs) {
|
||||
txSignal, err = parseFlasBuy(tx, instructionIndex)
|
||||
} else if matchMethod(methodData, flasSellTokensIX) {
|
||||
} else if matchFlasMethod(methodData, flasSellTokensIXs) {
|
||||
txSignal, err = parseFlasSell(tx, instructionIndex)
|
||||
} else if matchMethod(methodData, flasAmmBuyTokensIX) {
|
||||
} else if matchFlasMethod(methodData, flasAmmBuyTokensIXs) {
|
||||
txSignal, err = parseFlasAmmBuy(tx, instructionIndex)
|
||||
} else if matchMethod(methodData, flasAmmSellTokensIX) {
|
||||
} else if matchFlasMethod(methodData, flasAmmSellTokensIXs) {
|
||||
txSignal, err = parseFlasAmmSell(tx, instructionIndex)
|
||||
} else if matchFlasMethod(methodData, flasBonkBuyTokensIXs) {
|
||||
txSignal, err = parseFlasBonkBuy(tx, instructionIndex)
|
||||
} else if matchFlasMethod(methodData, flasBonkSellTokensIXs) {
|
||||
txSignal, err = parseFlasBonkSell(tx, instructionIndex)
|
||||
}
|
||||
if txSignal != nil {
|
||||
return TxSignalBatch{txSignal}, err
|
||||
@@ -75,10 +113,16 @@ func parseFlasAmmSell(tx VersionedTransaction, instructionIndex int) (*TxSignal,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var args flasArgs
|
||||
if err := borsh.Deserialize(&args, instruction.Data[1:]); err != nil {
|
||||
args, err := decodeFlasArgs(instruction.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
|
||||
}
|
||||
token0Amount := formatTokenAmount(args.Amount1)
|
||||
token0AmountUint64 := args.Amount1
|
||||
if len(instruction.Accounts) == 52 {
|
||||
token0Amount = decimal.Zero
|
||||
token0AmountUint64 = 0
|
||||
}
|
||||
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
@@ -86,7 +130,7 @@ func parseFlasAmmSell(tx VersionedTransaction, instructionIndex int) (*TxSignal,
|
||||
Maker: user.String(),
|
||||
Token0Address: mint.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(args.Amount1),
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: formatSolAmount(args.Amount2),
|
||||
Program: "PumpAMM",
|
||||
Event: "sell",
|
||||
@@ -94,7 +138,7 @@ func parseFlasAmmSell(tx VersionedTransaction, instructionIndex int) (*TxSignal,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: args.Amount1,
|
||||
Token0AmountUint64: token0AmountUint64,
|
||||
Token1AmountUint64: args.Amount2,
|
||||
}, nil
|
||||
}
|
||||
@@ -114,8 +158,8 @@ func parseFlasAmmBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var args flasArgs
|
||||
if err := borsh.Deserialize(&args, instruction.Data[1:]); err != nil {
|
||||
args, err := decodeFlasArgs(instruction.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
|
||||
}
|
||||
|
||||
@@ -153,8 +197,8 @@ func parseFlasSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var args flasArgs
|
||||
if err := borsh.Deserialize(&args, instruction.Data[1:]); err != nil {
|
||||
args, err := decodeFlasArgs(instruction.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
|
||||
}
|
||||
|
||||
@@ -190,11 +234,8 @@ func parseFlasBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(instruction.Data) > 20 {
|
||||
instruction.Data = instruction.Data[:20]
|
||||
}
|
||||
var args flasArgs
|
||||
if err := borsh.Deserialize(&args, instruction.Data[1:]); err != nil {
|
||||
args, err := decodeFlasArgs(instruction.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
|
||||
}
|
||||
|
||||
@@ -216,3 +257,94 @@ func parseFlasBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, err
|
||||
Token1AmountUint64: args.Amount1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseFlasBonkBuy(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
|
||||
instruction := tx.Instructions[instructionIndex]
|
||||
if len(instruction.Accounts) < 17 {
|
||||
return nil, fmt.Errorf("accounts too short")
|
||||
}
|
||||
|
||||
base, err := tx.GetAccount(int(instruction.Accounts[15]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quote, err := tx.GetAccount(int(instruction.Accounts[16]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := tx.GetAccount(int(instruction.Accounts[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !quote.Equals(wrappedSOL) {
|
||||
// just ignore this
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
args, err := decodeFlasArgs(instruction.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
|
||||
}
|
||||
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "flas",
|
||||
Maker: user.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(args.Amount2),
|
||||
Token1Amount: formatSolAmount(args.Amount1),
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "buy",
|
||||
ExactSOL: true,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: args.Amount2,
|
||||
Token1AmountUint64: args.Amount1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseFlasBonkSell(tx VersionedTransaction, instructionIndex int) (*TxSignal, error) {
|
||||
instruction := tx.Instructions[instructionIndex]
|
||||
if len(instruction.Accounts) < 17 {
|
||||
return nil, fmt.Errorf("accounts too short")
|
||||
}
|
||||
|
||||
base, err := tx.GetAccount(int(instruction.Accounts[15]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quote, err := tx.GetAccount(int(instruction.Accounts[16]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := tx.GetAccount(int(instruction.Accounts[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !quote.Equals(wrappedSOL) {
|
||||
// just ignore this
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
args, err := decodeFlasArgs(instruction.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse buy tokens args: %w", err)
|
||||
}
|
||||
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "flas",
|
||||
Maker: user.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(args.Amount1),
|
||||
Token1Amount: formatSolAmount(args.Amount2),
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "sell",
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: args.Amount1,
|
||||
Token1AmountUint64: args.Amount2,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1253,6 +1253,38 @@ func findPumpFunMint(tx VersionedTransaction, accounts []uint8) (solana.PublicKe
|
||||
return solana.PublicKey{}, false, nil
|
||||
}
|
||||
|
||||
func findPreferredToken1Mint(tx VersionedTransaction, accounts []uint8) (solana.PublicKey, bool, error) {
|
||||
var stableMint solana.PublicKey
|
||||
sawSystemProgram := false
|
||||
|
||||
for _, acctIdx := range accounts {
|
||||
key, err := tx.GetAccount(int(acctIdx))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case key.Equals(solana.WrappedSol):
|
||||
return solana.WrappedSol, true, nil
|
||||
case isStableMint(key):
|
||||
if stableMint.IsZero() {
|
||||
stableMint = key
|
||||
} else if !stableMint.Equals(key) {
|
||||
return solana.PublicKey{}, false, nil
|
||||
}
|
||||
case key.Equals(solana.SystemProgramID):
|
||||
sawSystemProgram = true
|
||||
}
|
||||
}
|
||||
|
||||
if !stableMint.IsZero() {
|
||||
return stableMint, true, nil
|
||||
}
|
||||
if sawSystemProgram {
|
||||
return solana.WrappedSol, true, nil
|
||||
}
|
||||
return solana.PublicKey{}, false, nil
|
||||
}
|
||||
|
||||
func jupiterV6SourceDestMints(msg VersionedTransaction, instruction Instructions, disc []byte) (solana.PublicKey, solana.PublicKey, bool, error) {
|
||||
switch {
|
||||
case bytes.Equal(disc, jupiterRouteV2),
|
||||
@@ -1396,9 +1428,20 @@ func parseJupiterV6Instruction(tx VersionedTransaction, instructionIndex int) (T
|
||||
return nil, nil
|
||||
}
|
||||
if bytes.Equal(disc, jupiterRoute) {
|
||||
pumpMint, ok, err := findPumpFunMint(tx, instruction.Accounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
if len(instruction.Accounts) < 13 {
|
||||
return nil, nil
|
||||
}
|
||||
token1Mint, token1MintOK, err := findPreferredToken1Mint(tx, instruction.Accounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destMint, err := tx.GetAccount(int(instruction.Accounts[5]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1411,21 +1454,30 @@ func parseJupiterV6Instruction(tx VersionedTransaction, instructionIndex int) (T
|
||||
if !pumpKey.Equals(pumpProgramID) {
|
||||
return nil, nil
|
||||
}
|
||||
token0Mint, err := tx.GetAccount(int(instruction.Accounts[12]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token0Amount := decimal.Zero
|
||||
if routeIn > 0 {
|
||||
token0Amount = formatTokenAmount(routeIn)
|
||||
}
|
||||
token1Amount := decimal.Zero
|
||||
token1Address := wsolMint
|
||||
if destMint.Equals(solana.WrappedSol) || destMint.Equals(solana.SystemProgramID) {
|
||||
token1Address = wsolMint
|
||||
if routeOut > 0 {
|
||||
token1Amount = formatSolAmount(routeOut)
|
||||
}
|
||||
} else {
|
||||
token1Address = destMint.String()
|
||||
if routeOut > 0 {
|
||||
token1Amount = formatTokenAmount(routeOut)
|
||||
}
|
||||
}
|
||||
return TxSignalBatch{&TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.StaticAccountKeys[0].String(),
|
||||
Token0Address: token0Mint.String(),
|
||||
Token1Address: destMint.String(),
|
||||
Token0Address: pumpMint.String(),
|
||||
Token1Address: token1Address,
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: decimal.Zero,
|
||||
Token1Amount: token1Amount,
|
||||
Program: "Pump",
|
||||
Event: "sell",
|
||||
IsToken2022: false,
|
||||
@@ -1433,28 +1485,38 @@ func parseJupiterV6Instruction(tx VersionedTransaction, instructionIndex int) (T
|
||||
ExactSOL: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: routeIn,
|
||||
Token1AmountUint64: 0,
|
||||
Token1AmountUint64: routeOut,
|
||||
}}, nil
|
||||
}
|
||||
token0Amount := decimal.Zero
|
||||
if routeOut > 0 {
|
||||
token0Amount = formatTokenAmount(routeOut)
|
||||
}
|
||||
token1Amount := decimal.Zero
|
||||
token1Address := wsolMint
|
||||
if token1MintOK && isStableMint(token1Mint) {
|
||||
token1Address = token1Mint.String()
|
||||
if routeIn > 0 {
|
||||
token1Amount = formatTokenAmount(routeIn)
|
||||
}
|
||||
} else if routeIn > 0 {
|
||||
token1Amount = formatSolAmount(routeIn)
|
||||
}
|
||||
return TxSignalBatch{&TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.StaticAccountKeys[0].String(),
|
||||
Token0Address: destMint.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token1Address: token1Address,
|
||||
Token0Amount: token0Amount,
|
||||
Token1Amount: decimal.Zero,
|
||||
Token1Amount: token1Amount,
|
||||
Program: "Pump",
|
||||
Event: "buy",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
ExactSOL: true,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: routeOut,
|
||||
Token1AmountUint64: 0,
|
||||
Token1AmountUint64: routeIn,
|
||||
}}, nil
|
||||
}
|
||||
if wrappedCnt > 1 {
|
||||
|
||||
@@ -3,6 +3,8 @@ package shreder
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
)
|
||||
|
||||
func TestDecodeRouteV2Arg(t *testing.T) {
|
||||
@@ -90,3 +92,70 @@ func TestDecodeRouteArg(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestParseJupiterV6RouteDoesNotMisclassifyMeteoraDlmmAsPump(t *testing.T) {
|
||||
instrData, err := hex.DecodeString("e517cb977ae3ad2a0100000026640001b9fe480300000000187dbe35000000002c0100")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode instruction data: %v", err)
|
||||
}
|
||||
|
||||
accountStrs := []string{
|
||||
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
|
||||
"7Ubi7vPnj5E2WdMtpA21mQoZmpY2TnrDqWpHJg2j2k7z",
|
||||
"F7GdscGrjA8YwmiRaJNnbFAtmU6pmncwJX2Q6TNEVtWJ",
|
||||
"4tBgpAzd4QRCSKDkmSaRVqWpnKmRJjT5djjhaspNxUWR",
|
||||
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
||||
"CreiuhfwdWCN5mJbMJtA9bBpYQrQF2tCBuZwSPWfpump",
|
||||
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
||||
"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf",
|
||||
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
||||
"LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo",
|
||||
"6bh2zL833toG7TnbBn2HEi6vLBbMTEwREk6YPZYQThDK",
|
||||
"7Arfi1EzAiSMdbbwsEwUzndDGQzw1bHrVHir2cZmYi96",
|
||||
"8SQXJSUGrwRuh1fRtta2pGTXSKztboatnDjV9hdi7Kon",
|
||||
"G6SuCjTPhddLpUd7uUh5NJCpEhfg5oeN5vKxYmvnDX6i",
|
||||
"F7GdscGrjA8YwmiRaJNnbFAtmU6pmncwJX2Q6TNEVtWJ",
|
||||
"4tBgpAzd4QRCSKDkmSaRVqWpnKmRJjT5djjhaspNxUWR",
|
||||
"CreiuhfwdWCN5mJbMJtA9bBpYQrQF2tCBuZwSPWfpump",
|
||||
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
|
||||
"9CuRuTiaNQjKyX73iPGY2n3qUZmP2Fyk4RvXKaHEksqj",
|
||||
"LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo",
|
||||
"7Ubi7vPnj5E2WdMtpA21mQoZmpY2TnrDqWpHJg2j2k7z",
|
||||
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
|
||||
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
|
||||
"D1ZN9Wj1fRSUQfCjhvnu1hqDMT7hzjzBBpi12nVniYD6",
|
||||
"LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo",
|
||||
"BAxxa7cjHvqbSGpowcpinGxNKN66B9vwsMhSNzUWuS69",
|
||||
"99nHJNiPJBBupPfK7jdEsvx9KoHxSbeqSAfyn2qgboFA",
|
||||
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
||||
}
|
||||
|
||||
accounts := make([]solana.PublicKey, 0, len(accountStrs))
|
||||
accountIndexes := make([]uint8, 0, len(accountStrs))
|
||||
for i, account := range accountStrs {
|
||||
accounts = append(accounts, solana.MustPublicKeyFromBase58(account))
|
||||
accountIndexes = append(accountIndexes, uint8(i))
|
||||
}
|
||||
|
||||
tx := VersionedTransaction{
|
||||
Signatures: []solana.Signature{
|
||||
solana.MustSignatureFromBase58("3AJSh1Dv4MHQL8UKLiVRkbAX2D45VRtNro68am9Dd66kh89khuFQGtsf8x1yx6m3pGSXU8vagb7Q4YfGXsfMzgEy"),
|
||||
},
|
||||
StaticAccountKeys: accounts,
|
||||
Instructions: []Instructions{
|
||||
{
|
||||
ProgramIDIndex: 4,
|
||||
Accounts: accountIndexes,
|
||||
Data: instrData,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
signals, err := parseJupiterV6Instruction(tx, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("parseJupiterV6Instruction returned error: %v", err)
|
||||
}
|
||||
if len(signals) != 0 {
|
||||
t.Fatalf("expected no signal for Meteora DLMM route, got %+v", signals)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ func maestroMultiSwap2DexName(dexID uint8) string {
|
||||
return "OrcaWhirPool"
|
||||
case 9:
|
||||
return "MeteoraPools"
|
||||
|
||||
case 10:
|
||||
return "MeteoraDynamicBondingCurve"
|
||||
default:
|
||||
@@ -125,20 +124,7 @@ func parseMaestroInstruction(tx VersionedTransaction, idx int) (TxSignalBatch, e
|
||||
return parseMaestroInstructionDataAndAccounts(tx, ix.Data, ix.Accounts)
|
||||
}
|
||||
|
||||
func parseMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixData []byte, ixAccounts []uint8) (TxSignalBatch, error) {
|
||||
args, err := decodeMaestroMultiSwap2Args(ixData)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// only decode pump amm
|
||||
if len(args.Routes) != 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if args.Routes[0].DexID != 4 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func parsePumpAMMMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixAccounts []uint8, args *MaestroMultiSwap2Args) (TxSignalBatch, error) {
|
||||
var (
|
||||
event string
|
||||
token0Amount uint64
|
||||
@@ -255,3 +241,91 @@ func parseMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixData []by
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseRaydiumLaunchLabMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixAccounts []uint8, args *MaestroMultiSwap2Args) (TxSignalBatch, error) {
|
||||
var (
|
||||
event string
|
||||
token0Amount uint64
|
||||
token1Amount uint64
|
||||
|
||||
// pool solana.PublicKey
|
||||
baseMint solana.PublicKey
|
||||
quoteMint solana.PublicKey
|
||||
|
||||
err error
|
||||
)
|
||||
|
||||
routeFlag := args.Routes[0].RouteKeyIdx
|
||||
if routeFlag == 0x0b {
|
||||
event = "buy"
|
||||
token0Amount = args.MinAmountOut
|
||||
token1Amount = args.AmountIn
|
||||
if len(ixAccounts) < 18 {
|
||||
return nil, nil
|
||||
}
|
||||
quoteMint, err = tx.GetAccount(int(ixAccounts[7]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseMint, err = tx.GetAccount(int(ixAccounts[17]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if routeFlag == 0x08 {
|
||||
event = "sell"
|
||||
token0Amount = args.AmountIn
|
||||
token1Amount = args.MinAmountOut
|
||||
if len(ixAccounts) < 18 {
|
||||
return nil, nil
|
||||
}
|
||||
quoteMint, err = tx.GetAccount(int(ixAccounts[17]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseMint, err = tx.GetAccount(int(ixAccounts[7]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !quoteMint.Equals(wrappedSOL) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return TxSignalBatch{
|
||||
&TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Maker: tx.StaticAccountKeys[0].String(),
|
||||
Token0Address: baseMint.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(token0Amount),
|
||||
Token1Amount: formatTokenAmount(token1Amount),
|
||||
Event: event,
|
||||
Program: "RaydiumLaunchLab",
|
||||
ExactSOL: event == "buy",
|
||||
Token0AmountUint64: token0Amount,
|
||||
Token1AmountUint64: token1Amount,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixData []byte, ixAccounts []uint8) (TxSignalBatch, error) {
|
||||
args, err := decodeMaestroMultiSwap2Args(ixData)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
// only decode 1 route
|
||||
if len(args.Routes) != 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if args.Routes[0].DexID == 4 {
|
||||
return parsePumpAMMMaestroInstructionDataAndAccounts(tx, ixAccounts, args)
|
||||
} else if args.Routes[0].DexID == 3 {
|
||||
return parseRaydiumLaunchLabMaestroInstructionDataAndAccounts(tx, ixAccounts, args)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -29,6 +29,15 @@ type pumpBuyArgs struct {
|
||||
}
|
||||
|
||||
type pumpCreateCoinV2Args struct {
|
||||
Name string
|
||||
Symbol string
|
||||
Uri string
|
||||
Creator solana.PublicKey
|
||||
IsMayhemMode bool
|
||||
IsCashbackEnabled bool
|
||||
}
|
||||
|
||||
type legacyPumpCreateCoinV2Args struct {
|
||||
Name string
|
||||
Symbol string
|
||||
Uri string
|
||||
@@ -37,7 +46,6 @@ type pumpCreateCoinV2Args struct {
|
||||
}
|
||||
|
||||
func parsePumpInstruction(msg VersionedTransaction, instructionIndex int) (TxSignalBatch, error) {
|
||||
|
||||
instruction := msg.Instructions[instructionIndex]
|
||||
if len(instruction.Data) < 8 {
|
||||
return nil, fmt.Errorf("data is empty")
|
||||
@@ -114,7 +122,18 @@ func parsePumpCreateV2(tx VersionedTransaction, instruction Instructions) (*TxSi
|
||||
|
||||
var args pumpCreateCoinV2Args
|
||||
if err := borsh.Deserialize(&args, instruction.Data[8:]); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse create coin v2 args: %w", err)
|
||||
var legacyArgs legacyPumpCreateCoinV2Args
|
||||
if err := borsh.Deserialize(&legacyArgs, instruction.Data[8:]); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse create coin v2 args: %w", err)
|
||||
}
|
||||
args = pumpCreateCoinV2Args{
|
||||
Name: legacyArgs.Name,
|
||||
Symbol: legacyArgs.Symbol,
|
||||
Uri: legacyArgs.Uri,
|
||||
Creator: legacyArgs.Creator,
|
||||
IsMayhemMode: legacyArgs.IsMayhemMode,
|
||||
IsCashbackEnabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
return &TxSignal{
|
||||
@@ -129,6 +148,7 @@ func parsePumpCreateV2(tx VersionedTransaction, instruction Instructions) (*TxSi
|
||||
Event: "create",
|
||||
IsToken2022: tokenProgramKey.String() != tokenProgram,
|
||||
IsMayhemMode: args.IsMayhemMode,
|
||||
IsCashbackEnabled: args.IsCashbackEnabled,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: 0,
|
||||
Token1AmountUint64: 0,
|
||||
|
||||
204
pkg/shreder/program_raydiumlaunchlab.go
Normal file
204
pkg/shreder/program_raydiumlaunchlab.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package shreder
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var raydiumLaunchLabProgramID = solana.MustPublicKeyFromBase58("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj")
|
||||
var (
|
||||
raydiumLaunchLabInitializeV2PoolDiscriminator = []byte{67, 153, 175, 39, 218, 16, 38, 32}
|
||||
raydiumLaunchLabInitializeWithToken2022PoolDiscriminator = []byte{37, 190, 126, 222, 44, 154, 171, 17}
|
||||
raydiumLaunchLabSellExactInDiscriminator = []byte{0x95, 0x27, 0xde, 0x9b, 0xd3, 0x7c, 0x98, 0x1a}
|
||||
raydiumLaunchLabSellExactOutDiscriminator = []byte{0x5f, 0xc8, 0x47, 0x22, 0x08, 0x09, 0x0b, 0xa6}
|
||||
raydiumLaunchLabBuyExactInDiscriminator = []byte{0xfa, 0xea, 0x0d, 0x7b, 0xd5, 0x9c, 0x13, 0xec}
|
||||
raydiumLaunchLabBuyExactOutDiscriminator = []byte{0x18, 0xd3, 0x74, 0x28, 0x69, 0x03, 0x99, 0x38}
|
||||
)
|
||||
|
||||
func parseRaydiumLaunchLabInstruction(tx VersionedTransaction, instructionIndex int) (TxSignalBatch, error) {
|
||||
if instructionIndex >= len(tx.Instructions) {
|
||||
return nil, fmt.Errorf("instruction index out of bounds")
|
||||
}
|
||||
|
||||
instruction := tx.Instructions[instructionIndex]
|
||||
if len(instruction.Data) == 0 {
|
||||
return nil, fmt.Errorf("data is empty")
|
||||
}
|
||||
if len(instruction.Data) < 8 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
txSignal *TxSignal
|
||||
)
|
||||
if matchMethod(instruction.Data, raydiumLaunchLabBuyExactInDiscriminator) {
|
||||
txSignal, err = parseRaydiumLaunchLabSwap(tx, instruction, true, true)
|
||||
} else if matchMethod(instruction.Data, raydiumLaunchLabBuyExactOutDiscriminator) {
|
||||
txSignal, err = parseRaydiumLaunchLabSwap(tx, instruction, true, false)
|
||||
} else if matchMethod(instruction.Data, raydiumLaunchLabSellExactInDiscriminator) {
|
||||
txSignal, err = parseRaydiumLaunchLabSwap(tx, instruction, false, true)
|
||||
} else if matchMethod(instruction.Data, raydiumLaunchLabSellExactOutDiscriminator) {
|
||||
txSignal, err = parseRaydiumLaunchLabSwap(tx, instruction, false, false)
|
||||
} else if matchMethod(instruction.Data, raydiumLaunchLabInitializeV2PoolDiscriminator) {
|
||||
txSignal, err = parseRaydiumLaunchLabCreate(tx, instruction, false)
|
||||
} else if matchMethod(instruction.Data, raydiumLaunchLabInitializeWithToken2022PoolDiscriminator) {
|
||||
txSignal, err = parseRaydiumLaunchLabCreate(tx, instruction, true)
|
||||
}
|
||||
if txSignal != nil {
|
||||
return TxSignalBatch{txSignal}, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func parseRaydiumLaunchLabCreate(tx VersionedTransaction, instruction Instructions, isToken2022 bool) (*TxSignal, error) {
|
||||
if len(instruction.Accounts) < 10 {
|
||||
return nil, fmt.Errorf("accounts too short")
|
||||
}
|
||||
|
||||
base, err := tx.GetAccount(int(instruction.Accounts[6]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quote, err := tx.GetAccount(int(instruction.Accounts[7]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
creator, err := tx.GetAccount(int(instruction.Accounts[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !quote.Equals(wrappedSOL) {
|
||||
// just ignore this
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "raydiumlaunchlab",
|
||||
Maker: creator.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: decimal.Zero,
|
||||
Token1Amount: decimal.Zero,
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "create",
|
||||
IsToken2022: isToken2022,
|
||||
IsMayhemMode: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: 0,
|
||||
Token1AmountUint64: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseRaydiumLaunchLabSwap(tx VersionedTransaction, instruction Instructions, isBuy bool, exactIn bool) (*TxSignal, error) {
|
||||
if len(instruction.Accounts) < 11 {
|
||||
return nil, fmt.Errorf("accounts too short")
|
||||
}
|
||||
if len(instruction.Data) < 24 {
|
||||
return nil, fmt.Errorf("data too short for raydium launch lab swap args, len=%d", len(instruction.Data))
|
||||
}
|
||||
|
||||
base, err := tx.GetAccount(int(instruction.Accounts[9]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quote, err := tx.GetAccount(int(instruction.Accounts[10]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := tx.GetAccount(int(instruction.Accounts[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !quote.Equals(wrappedSOL) {
|
||||
// just ignore this
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
amountA := binary.LittleEndian.Uint64(instruction.Data[8:16])
|
||||
amountB := binary.LittleEndian.Uint64(instruction.Data[16:24])
|
||||
|
||||
if isBuy {
|
||||
if exactIn {
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "raydiumlaunchlab",
|
||||
Maker: user.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(amountB),
|
||||
Token1Amount: formatSolAmount(amountA),
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "buy",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: true,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: amountB,
|
||||
Token1AmountUint64: amountA,
|
||||
}, nil
|
||||
} else {
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "raydiumlaunchlab",
|
||||
Maker: user.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(amountA),
|
||||
Token1Amount: formatSolAmount(amountB),
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "buy",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: amountA,
|
||||
Token1AmountUint64: amountB,
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
if exactIn {
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "raydiumlaunchlab",
|
||||
Maker: user.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(amountA),
|
||||
Token1Amount: formatSolAmount(amountB),
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "sell",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: amountA,
|
||||
Token1AmountUint64: amountB,
|
||||
}, nil
|
||||
} else {
|
||||
return &TxSignal{
|
||||
TxHash: tx.Signatures[0].String(),
|
||||
Label: "raydiumlaunchlab",
|
||||
Maker: user.String(),
|
||||
Token0Address: base.String(),
|
||||
Token1Address: wsolMint,
|
||||
Token0Amount: formatTokenAmount(amountB),
|
||||
Token1Amount: formatSolAmount(amountA),
|
||||
Program: "RaydiumLaunchLab",
|
||||
Event: "sell",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: true,
|
||||
Block: tx.Block,
|
||||
Token0AmountUint64: amountB,
|
||||
Token1AmountUint64: amountA,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
|
||||
var terminalProgramID = solana.MustPublicKeyFromBase58("term9YPb9mzAsABaqN71A4xdbxHmpBNZavpBiQKZzN3")
|
||||
var (
|
||||
terminalBuyTokensIX = []byte{0xa6, 0x54, 0x14, 0x96, 0x9f, 0x77, 0x59, 0xca}
|
||||
terminalSellTokensIX = []byte{0xbe, 0x84, 0xa2, 0x96, 0x93, 0x7c, 0xf8, 0x6b}
|
||||
terminalBuyTokensIX = []byte{0x14, 0xfe, 0x38, 0xc9, 0x3d, 0x37, 0x17, 0x27}
|
||||
terminalSellTokensIX = []byte{0xcd, 0xaa, 0x10, 0x49, 0x20, 0xd6, 0x62, 0xd6}
|
||||
terminalAmmSellTokensIX = []byte{0x40, 0x64, 0x97, 0xb9, 0x16, 0xfa, 0xec, 0xb1}
|
||||
)
|
||||
|
||||
@@ -144,7 +144,7 @@ func parseTermSell(tx VersionedTransaction, instruction Instructions) (*TxSignal
|
||||
Token0Amount: formatTokenAmount(tokenAmount),
|
||||
Token1Amount: formatSolAmount(solAmount),
|
||||
Program: "Pump",
|
||||
Event: "buy",
|
||||
Event: "sell",
|
||||
IsToken2022: false,
|
||||
IsMayhemMode: false,
|
||||
ExactSOL: false,
|
||||
|
||||
@@ -28,27 +28,28 @@ func SetLogLevel(level slog.Level) {
|
||||
}
|
||||
|
||||
type TxSignal struct {
|
||||
Source string `json:"source"`
|
||||
Label string `json:"label"`
|
||||
TxHash string `json:"tx_hash"`
|
||||
Maker string `json:"maker"`
|
||||
Token0Address string `json:"token0_address"`
|
||||
Token1Address string `json:"token1_address"`
|
||||
Token0Amount decimal.Decimal `json:"token0_amount"`
|
||||
Token1Amount decimal.Decimal `json:"token1_amount"`
|
||||
Block uint64 `json:"block"`
|
||||
BlockAt time.Time `json:"block_at"`
|
||||
BlockIndex uint64 `json:"block_index"`
|
||||
Event string `json:"event"`
|
||||
Program string `json:"program"`
|
||||
IsProcessed bool `json:"is_processed"`
|
||||
IsToken2022 bool `json:"is_token2022"`
|
||||
IsMayhemMode bool `json:"is_mayhem_mode"`
|
||||
CUPrice decimal.Decimal `json:"cu_price"`
|
||||
CULimit uint32 `json:"cu_limit"`
|
||||
SWQoSAgent string `json:"swqos_agent"`
|
||||
SWQoSTips decimal.Decimal `json:"swqos_tips"`
|
||||
TableCnt int `json:"table_cnt"`
|
||||
Source string `json:"source"`
|
||||
Label string `json:"label"`
|
||||
TxHash string `json:"tx_hash"`
|
||||
Maker string `json:"maker"`
|
||||
Token0Address string `json:"token0_address"`
|
||||
Token1Address string `json:"token1_address"`
|
||||
Token0Amount decimal.Decimal `json:"token0_amount"`
|
||||
Token1Amount decimal.Decimal `json:"token1_amount"`
|
||||
Block uint64 `json:"block"`
|
||||
BlockAt time.Time `json:"block_at"`
|
||||
BlockIndex uint64 `json:"block_index"`
|
||||
Event string `json:"event"`
|
||||
Program string `json:"program"`
|
||||
IsProcessed bool `json:"is_processed"`
|
||||
IsToken2022 bool `json:"is_token2022"`
|
||||
IsMayhemMode bool `json:"is_mayhem_mode"`
|
||||
IsCashbackEnabled bool `json:"is_cashback_enabled"`
|
||||
CUPrice decimal.Decimal `json:"cu_price"`
|
||||
CULimit uint32 `json:"cu_limit"`
|
||||
SWQoSAgent string `json:"swqos_agent"`
|
||||
SWQoSTips decimal.Decimal `json:"swqos_tips"`
|
||||
TableCnt int `json:"table_cnt"`
|
||||
|
||||
ExactSOL bool `json:"exact_in"`
|
||||
|
||||
|
||||
@@ -56,27 +56,28 @@ var (
|
||||
defaultFilterAccount []solana.PublicKey
|
||||
|
||||
registered = map[solana.PublicKey]Handler{
|
||||
pumpProgramID: {parsePumpInstruction, "pump"},
|
||||
azczProgramID: {parseAzczInstruction, "azcz"},
|
||||
f5tfProgramID: {parseF5tfInstruction, "f5tf"},
|
||||
flasProgramID: {parseFlasInstruction, "flas"},
|
||||
photonProgramID: {parsePhotonInstruction, "photon"},
|
||||
pumpAmmProgramID: {parsePumpAmmInstruction, "pumpamm"},
|
||||
binanceWalletProgramID: {parseBinanceWalletInstruction, "binancewallet"},
|
||||
boboProgramID: {parseBoboInstruction, "bobo"},
|
||||
qtkvProgramID: {parseQtkvInstruction, "qtkv"},
|
||||
fjszProgramID: {parseFjszInstruction, "fjsz"},
|
||||
terminalProgramID: {parseTermInstruction, "terminal"},
|
||||
jupiterV6ProgramID: {parseJupiterV6Instruction, "jupiterv6"},
|
||||
okxDexRouteV2ProgramID: {parseOkxDexRouteV2Instruction, "okxdexroutev2"},
|
||||
dflowProgramID: {parseDFlowInstruction, "dflow"},
|
||||
gmgnProgramID: {parseGMGNInstruction, "gmgn"},
|
||||
bonkProgramID: {parseBonkInstruction, "bonk"},
|
||||
bloomRouterProgramID: {parseBloomRouterInstruction, "bloomrouter"},
|
||||
dlmmProgramID: {parseDlmmInstruction, "dlmm"},
|
||||
dbotProgramID: {parseDbotInstruction, "dbot"},
|
||||
tradewizProgramID: {parseTradewizInstruction, "tradewiz"},
|
||||
maestroProgramId: {parseMaestroInstruction, "maestro"},
|
||||
pumpProgramID: {parsePumpInstruction, "pump"},
|
||||
azczProgramID: {parseAzczInstruction, "azcz"},
|
||||
f5tfProgramID: {parseF5tfInstruction, "f5tf"},
|
||||
flasProgramID: {parseFlasInstruction, "flas"},
|
||||
photonProgramID: {parsePhotonInstruction, "photon"},
|
||||
pumpAmmProgramID: {parsePumpAmmInstruction, "pumpamm"},
|
||||
binanceWalletProgramID: {parseBinanceWalletInstruction, "binancewallet"},
|
||||
boboProgramID: {parseBoboInstruction, "bobo"},
|
||||
qtkvProgramID: {parseQtkvInstruction, "qtkv"},
|
||||
fjszProgramID: {parseFjszInstruction, "fjsz"},
|
||||
terminalProgramID: {parseTermInstruction, "terminal"},
|
||||
jupiterV6ProgramID: {parseJupiterV6Instruction, "jupiterv6"},
|
||||
okxDexRouteV2ProgramID: {parseOkxDexRouteV2Instruction, "okxdexroutev2"},
|
||||
dflowProgramID: {parseDFlowInstruction, "dflow"},
|
||||
gmgnProgramID: {parseGMGNInstruction, "gmgn"},
|
||||
bonkProgramID: {parseBonkInstruction, "bonk"},
|
||||
bloomRouterProgramID: {parseBloomRouterInstruction, "bloomrouter"},
|
||||
dlmmProgramID: {parseDlmmInstruction, "dlmm"},
|
||||
dbotProgramID: {parseDbotInstruction, "dbot"},
|
||||
tradewizProgramID: {parseTradewizInstruction, "tradewiz"},
|
||||
maestroProgramId: {parseMaestroInstruction, "maestro"},
|
||||
raydiumLaunchLabProgramID: {parseRaydiumLaunchLabInstruction, "raydiumlaunchlab"},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/gagliardetto/solana-go"
|
||||
"github.com/gagliardetto/solana-go/rpc"
|
||||
"github.com/near/borsh-go"
|
||||
)
|
||||
|
||||
func TestDecodeAxiomArgs(t *testing.T) {
|
||||
@@ -49,8 +48,8 @@ func TestDecodeAxiomArgs(t *testing.T) {
|
||||
t.Fatalf("failed to decode hex string: %v", err)
|
||||
return
|
||||
}
|
||||
var args flasArgs
|
||||
if err := borsh.Deserialize(&args, data[1:]); err != nil {
|
||||
args, err := decodeFlasArgs(data)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode Axiom args: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -59,6 +58,41 @@ func TestDecodeAxiomArgs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeAxiomUpdatedRouteMarkers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
marker []byte
|
||||
match func([]byte) bool
|
||||
}{
|
||||
{name: "pump buy 0021", marker: []byte{0x00, 0x01, 0x21}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasBuyTokensIXs) }},
|
||||
{name: "pump buy 001b", marker: []byte{0x00, 0x01, 0x1b}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasBuyTokensIXs) }},
|
||||
{name: "pump sell", marker: []byte{0x01, 0x01, 0x1a}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasSellTokensIXs) }},
|
||||
{name: "pump amm buy", marker: []byte{0x00, 0x02, 0x1f}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasAmmBuyTokensIXs) }},
|
||||
{name: "pump amm sell", marker: []byte{0x01, 0x02, 0x1f}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasAmmSellTokensIXs) }},
|
||||
{name: "legacy bonk buy", marker: []byte{0x00, 0x02, 0x07}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasBonkBuyTokensIXs) }},
|
||||
{name: "legacy bonk sell", marker: []byte{0x01, 0x02, 0x07}, match: func(marker []byte) bool { return matchFlasMethod(marker, flasBonkSellTokensIXs) }},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
data := make([]byte, 20)
|
||||
data[0] = 0
|
||||
copy(data[17:20], tt.marker)
|
||||
args, err := decodeFlasArgs(data)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode args: %v", err)
|
||||
}
|
||||
expected := [3]uint8{tt.marker[0], tt.marker[1], tt.marker[2]}
|
||||
if args.Placeholder != expected {
|
||||
t.Fatalf("expected marker %x, got %x", tt.marker, args.Placeholder)
|
||||
}
|
||||
if !tt.match(tt.marker) {
|
||||
t.Fatalf("marker %x did not match route", tt.marker)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func toUpdata(slot uint64, tx *solana.Transaction) *SubscribeUpdateTransaction {
|
||||
signatures := make([][]byte, len(tx.Signatures))
|
||||
for i, sig := range tx.Signatures {
|
||||
@@ -154,12 +188,16 @@ func TestParseTermBuy(t *testing.T) {
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "5Gz1fa4Qhb35bkg9QCMXpxCX5uuNr7WcjcmrwajGZA7kXsvNS9pDnYe12ggWeSqf1nwZbVPob6DkX6fcwbE9ofBR"),
|
||||
getTransaction(t, client, "4rm1UFvWqTrBvcCfEzeXEPCeNsXRNhMHRx7AXrEiBwpFMJXNzXPt9zhrpQc1JrUnuBACeo7uRM8W8vKx56TQT7vs"),
|
||||
nil,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
@@ -175,17 +213,68 @@ func TestParseTermBuy(t *testing.T) {
|
||||
if signal.Event != "buy" {
|
||||
t.Fatalf("expected buy event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "BaLxyjXzATAnfm7cc5AFhWBpiwnsb71THcnofDLTWAPK" {
|
||||
t.Fatalf("expected maker BaLxyjXzATAnfm7cc5AFhWBpiwnsb71THcnofDLTWAPK, got %s", signal.Maker)
|
||||
if signal.Maker != "ATEruR96FhZWpZGkKvgzGG3gRL3Cdj6GttHtBsUFeAPE" {
|
||||
t.Fatalf("expected maker ATEruR96FhZWpZGkKvgzGG3gRL3Cdj6GttHtBsUFeAPE, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "5Wgv54peXRKDHYHapAELzgNKEPEh9E5Bf3hUR3sTpump" {
|
||||
t.Fatalf("expected token0 address 5Wgv54peXRKDHYHapAELzgNKEPEh9E5Bf3hUR3sTpump, got %s", signal.Token0Address)
|
||||
if signal.Token0Address != "7nHYtqhR4qq7LHVmC5Pnz7p6Se64i39TfnyM6ZC2pump" {
|
||||
t.Fatalf("expected token0 address 7nHYtqhR4qq7LHVmC5Pnz7p6Se64i39TfnyM6ZC2pump, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 6952026214256 {
|
||||
if signal.Token0AmountUint64 != 4473828190746 {
|
||||
t.Fatalf("expected token0 amount 6952026214256, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 653333333 {
|
||||
t.Fatalf("expected token1 amount 653333333, got %d", signal.Token1AmountUint64)
|
||||
if signal.Token1AmountUint64 != 250000000 {
|
||||
t.Fatalf("expected token1 amount 250000000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTermSell(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "4oSnHnDSscjmc6XX1rjXCEBavoLR9wkdZvGCAUn928iLWqrCwt2a6mgJpjP4NHqrCboUC82ugrjjEbNGNYAagkue"),
|
||||
nil,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "terminal" {
|
||||
t.Fatalf("expected terminal signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "sell" {
|
||||
t.Fatalf("expected sell event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "DjAj23BSiaRJKJziKHwtLexEHRDgL4tEjx4Ye4Gkug2E" {
|
||||
t.Fatalf("expected maker DjAj23BSiaRJKJziKHwtLexEHRDgL4tEjx4Ye4Gkug2E, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "5J6TKpfP5SHDaUNAdDg23c31wS3KnfZ3JTfFErt4pump" {
|
||||
t.Fatalf("expected token0 address 5J6TKpfP5SHDaUNAdDg23c31wS3KnfZ3JTfFErt4pump, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 6384819151429 {
|
||||
t.Fatalf("expected token0 amount 6384819151429, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 243633622 {
|
||||
t.Fatalf("expected token1 amount 243633622, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +297,10 @@ func TestParseBonkBuy(t *testing.T) {
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
@@ -256,6 +349,10 @@ func TestParseBonkSell(t *testing.T) {
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
@@ -305,6 +402,10 @@ func TestParsePhotonBuy(t *testing.T) {
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
@@ -353,6 +454,10 @@ func TestParseJupiterV6PumpFunBuy(t *testing.T) {
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
@@ -404,6 +509,10 @@ func TestParseJupiterV6PumpFunSell(t *testing.T) {
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
@@ -435,3 +544,780 @@ func TestParseJupiterV6PumpFunSell(t *testing.T) {
|
||||
t.Fatalf("expected ExactSOL false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRaydiumLaunchLabBuyExactIn(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "2erxUsE92LdrxhWy6HryUJpvBpVUociu2UY6AGoX7E6orrqm6AYxDzhmub3J9PDPa5CPNwWZBG8rCxKCdquVo2Lc"),
|
||||
nil,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "raydiumlaunchlab" {
|
||||
t.Fatalf("expected raydiumlaunchlab signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "buy" {
|
||||
t.Fatalf("expected buy event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "56JZV81H3XJedVWcV8RTXrn5YD6WW2k2LTGVDGCUwyYb" {
|
||||
t.Fatalf("expected maker 56JZV81H3XJedVWcV8RTXrn5YD6WW2k2LTGVDGCUwyYb, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "676zr3qFwy3XUXwVkQVdV9cidSaxcS6SrHga8cK4kKej" {
|
||||
t.Fatalf("expected token0 address 676zr3qFwy3XUXwVkQVdV9cidSaxcS6SrHga8cK4kKej, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 15336821188103 {
|
||||
t.Fatalf("expected token0 amount 15336821188103, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 1000000000 {
|
||||
t.Fatalf("expected token1 amount 1000000000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if !signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRaydiumLaunchLabBuyExactOut(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "4x2gArQAezap4RZKK5Tpfu1SNadCsTbjeUYG5KNggoHeGFiZdV1MmrrvXLXvyh2e6C1Gh22ohU4dFAzGw18y6VLT"),
|
||||
nil,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "raydiumlaunchlab" {
|
||||
t.Fatalf("expected raydiumlaunchlab signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "buy" {
|
||||
t.Fatalf("expected buy event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "nya666pQkP3PzWxi7JngU3rRMHuc7zbLK8c8wxQ4qpT" {
|
||||
t.Fatalf("expected maker nya666pQkP3PzWxi7JngU3rRMHuc7zbLK8c8wxQ4qpT, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "8ZTAvKJiPqExQYTYXoE2FuCoRFJAmyuEXXb4GbPvbonk" {
|
||||
t.Fatalf("expected token0 address 8ZTAvKJiPqExQYTYXoE2FuCoRFJAmyuEXXb4GbPvbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 2888057167672 {
|
||||
t.Fatalf("expected token0 amount 2888057167672, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 159300000 {
|
||||
t.Fatalf("expected token1 amount 159300000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRaydiumLaunchLabSellExactIn(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("4xBW5fcrR1eityayfv6mrxC7zfQNy89YpqoVmqiTbfRh"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "37LGuUiTnBpagELdwVWtffkbJaLsucbRimnFueKS23EaFdYsjHC42SS3XjyC5tyRaokj5tdwiKYfBqNFAmMbTUgv"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "raydiumlaunchlab" {
|
||||
t.Fatalf("expected raydiumlaunchlab signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "sell" {
|
||||
t.Fatalf("expected sell event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "7GSDLZHBNnEgYwe8TLLy8iDbnKWaR5NfNVoxvQJ26nux" {
|
||||
t.Fatalf("expected maker 7GSDLZHBNnEgYwe8TLLy8iDbnKWaR5NfNVoxvQJ26nux, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "6bs2ECcL1XG5Tw8t3xvpyCsQDunQpdYtDXNyD74bonk" {
|
||||
t.Fatalf("expected token0 address 6bs2ECcL1XG5Tw8t3xvpyCsQDunQpdYtDXNyD74bonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 3144872514758 {
|
||||
t.Fatalf("expected token0 amount 3144872514758, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 373068521 {
|
||||
t.Fatalf("expected token1 amount 373068521, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRaydiumLaunchLabSellExactOut(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("DSaHkhDp17UexbZsg2VUnWjEuTwKNCJrnG4LW122ANfd"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "3z8iC9zDLeQzjcLtUnwDT1j9Z5p5rFWXyCcRUhvUCVDTrPJSoZbbULkMTZk2mUUeAX1qaMBZUsPCd59B4KaGooSk"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "raydiumlaunchlab" {
|
||||
t.Fatalf("expected raydiumlaunchlab signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "sell" {
|
||||
t.Fatalf("expected sell event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "F4RMpjLMJZKmrYBxjpVadty7e326XUziLkYTDtNtpo2e" {
|
||||
t.Fatalf("expected maker F4RMpjLMJZKmrYBxjpVadty7e326XUziLkYTDtNtpo2e, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "ByubRkVymfNBoX9DUfUL39Zi6qzqWkTbLviQtx9ktxWv" {
|
||||
t.Fatalf("expected token0 address ByubRkVymfNBoX9DUfUL39Zi6qzqWkTbLviQtx9ktxWv, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 594669717336 {
|
||||
t.Fatalf("expected token0 amount 594669717336, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 141400000 {
|
||||
t.Fatalf("expected token1 amount 141400000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if !signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRaydiumLaunchLabCreate(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("J7aYNVkReYKUSEvS79sg2YubtMQMxUetByAFrNw7qZ3G"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "2qi4STgj33b1DydMKvtpqeSVwkcTJHww8ViX9ADMu2TaRz2uSGcVjgDyd9AELnn2N1ojSGy2qnM6uiXJc2vEcLmw"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) == 0 {
|
||||
t.Fatalf("expected at least 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "raydiumlaunchlab" {
|
||||
t.Fatalf("expected raydiumlaunchlab signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "create" {
|
||||
t.Fatalf("expected create event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "F9TNxWymThfvs4kVVmyaBqx8pjxpvP7kScNBqiczeY84" {
|
||||
t.Fatalf("expected maker F9TNxWymThfvs4kVVmyaBqx8pjxpvP7kScNBqiczeY84, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "5VvwdKsYUjPTNbEotwDcV48PQEfi42of7TzaLyaybonk" {
|
||||
t.Fatalf("expected token0 address 5VvwdKsYUjPTNbEotwDcV48PQEfi42of7TzaLyaybonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.IsToken2022 {
|
||||
t.Fatalf("expected IsToken2022 false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRaydiumLaunchLabCreate2(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("5Sq6FRoQkRbupnZM1iq9AFeLaSZPbVHgPheVqHD4GzVr"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "3ZofgvJ3vrNRnyGfAFwENSrDAHxVuXhqrL4svwctHuxZHnAengj5SHeEwPJiJFPXYaBcnfrUAk2V4368LVVSEP2W"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) == 0 {
|
||||
t.Fatalf("expected at least 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "raydiumlaunchlab" {
|
||||
t.Fatalf("expected raydiumlaunchlab signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "create" {
|
||||
t.Fatalf("expected create event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "7GhWwhaMgbKiRWxF93Bud6HnHMci6NCLTJyTxG8zFH51" {
|
||||
t.Fatalf("expected maker 7GhWwhaMgbKiRWxF93Bud6HnHMci6NCLTJyTxG8zFH51, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "GWeFeDVD75PVGHdLf7HrYJe2BYTiA8J3nVJXSqb4CpoU" {
|
||||
t.Fatalf("expected token0 address GWeFeDVD75PVGHdLf7HrYJe2BYTiA8J3nVJXSqb4CpoU, got %s", signal.Token0Address)
|
||||
}
|
||||
if !signal.IsToken2022 {
|
||||
t.Fatalf("expected IsToken2022 true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func manuallyLoadAddressTable(t *testing.T, client *rpc.Client, tablePubkey solana.PublicKey) *AddressTables {
|
||||
loader := NewAddressTables(client, false)
|
||||
table, err := loader.loadAddressTable(tablePubkey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load address table: %s", err)
|
||||
}
|
||||
|
||||
loader.tables.Add(tablePubkey, &TableInfo{
|
||||
addresses: table,
|
||||
})
|
||||
|
||||
return loader
|
||||
}
|
||||
|
||||
func TestParsePumpCreate(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("AXVvmhWaaPtV52jqYuTNqp1xRrkbxhfJfeHQKxq5cbvZ"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "3vUAHpBUoxeoZheo9m3XmufNUWmJWRAN4xZjSqDos71GL6tCKSTmJV6YeMS5XdVAbRAfqQi1rPusjbmEhoam4x9Y"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) == 0 {
|
||||
t.Fatalf("expected at least 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "pump" {
|
||||
t.Fatalf("expected pump signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "create" {
|
||||
t.Fatalf("expected create event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "8oVJLE69qH1i5jotFABcCoaPS38DAyZ7djRg6uuD3Cb7" {
|
||||
t.Fatalf("expected maker 8oVJLE69qH1i5jotFABcCoaPS38DAyZ7djRg6uuD3Cb7, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "AtaWsdyfcANLsHNdpYJyFdNvwSQnUzvhRN2MCCP9pump" {
|
||||
t.Fatalf("expected token0 address AtaWsdyfcANLsHNdpYJyFdNvwSQnUzvhRN2MCCP9pump, got %s", signal.Token0Address)
|
||||
}
|
||||
if !signal.IsToken2022 {
|
||||
t.Fatalf("expected IsToken2022 true, got false")
|
||||
}
|
||||
if signal.IsCashbackEnabled {
|
||||
t.Fatalf("expected IsCashbackEnabled true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePumpCreate2(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "3EKKjtNFzhtQaA9GyPt5UJHLr5mWT2XodaxWoenUrcaPpN8BKm84ATVapdUJcb9sJVFyS4iKD9spGBKfqkSFutea"),
|
||||
nil,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) == 0 {
|
||||
t.Fatalf("expected at least 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "pump" {
|
||||
t.Fatalf("expected pump signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "create" {
|
||||
t.Fatalf("expected create event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "5mGqXMqUbJzxQ9aqbBttB4JUfAMqceSTmrtpt6RuPXdC" {
|
||||
t.Fatalf("expected maker 5mGqXMqUbJzxQ9aqbBttB4JUfAMqceSTmrtpt6RuPXdC, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "J9Hqi5VddcTNJ2F5EyxSZxC2JpJeymfJXjLhsCVPZpna" {
|
||||
t.Fatalf("expected token0 address J9Hqi5VddcTNJ2F5EyxSZxC2JpJeymfJXjLhsCVPZpna, got %s", signal.Token0Address)
|
||||
}
|
||||
if !signal.IsToken2022 {
|
||||
t.Fatalf("expected IsToken2022 true, got false")
|
||||
}
|
||||
if signal.IsCashbackEnabled {
|
||||
t.Fatalf("expected IsCashbackEnabled true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFlasBonkBuy(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("7RKtfATWCe98ChuwecNq8XCzAzfoK3DtZTprFsPMGtio"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "5ED51fnabzxsPqjswp7R9qbfuTep7avtsQnsYg4R6w2jc9Ys2mMCXFNNnDNvUUhaREJS5Tz1dSfBL1dufXzDsiaX"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "flas" {
|
||||
t.Fatalf("expected flas signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "buy" {
|
||||
t.Fatalf("expected buy event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "75KjigN4rgweGMRu5oWY4DBPELpQ1TYsBQAXuzs7hKVA" {
|
||||
t.Fatalf("expected maker 75KjigN4rgweGMRu5oWY4DBPELpQ1TYsBQAXuzs7hKVA, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "2aMTnF7Kz9aRhTMmiVuSwbQ9Msrdkgm4RvFoJfZPbonk" {
|
||||
t.Fatalf("expected token0 address 2aMTnF7Kz9aRhTMmiVuSwbQ9Msrdkgm4RvFoJfZPbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 1052495896871 {
|
||||
t.Fatalf("expected token0 amount 1052495896871, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 99000000 {
|
||||
t.Fatalf("expected token1 amount 99000000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if !signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFlasBonkSell(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("7RKtfATWCe98ChuwecNq8XCzAzfoK3DtZTprFsPMGtio"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "2v3qLsnrJ5KqUDqtzXyc3S9vT6cLvXbaVR6vwfhp4ufC4Sg1vmR5xMdxzrtvErq8kiC8g7d5wLAbEMe8NwXJE5MS"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "flas" {
|
||||
t.Fatalf("expected flas signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "sell" {
|
||||
t.Fatalf("expected sell event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "7rtyqW2yr76Y9iCTvbAzkDdaJU8mbx3ZuzW9sTZ3pV2q" {
|
||||
t.Fatalf("expected maker 7rtyqW2yr76Y9iCTvbAzkDdaJU8mbx3ZuzW9sTZ3pV2q, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "2aMTnF7Kz9aRhTMmiVuSwbQ9Msrdkgm4RvFoJfZPbonk" {
|
||||
t.Fatalf("expected token0 address 2aMTnF7Kz9aRhTMmiVuSwbQ9Msrdkgm4RvFoJfZPbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 6413676607028 {
|
||||
t.Fatalf("expected token0 amount 6413676607028, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 249361301 {
|
||||
t.Fatalf("expected token1 amount 249361301, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMaestroBonkBuy(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("HPjn8EFrUcMxqQrdRzJkgXp85cGUHLFVkY4nZ7EUBbir"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "LuGGhtCU5enHY2J8qt5KAZybvQyokxKn4NxkhQfKz6RbkxW1anU9vHfAXeEVsjM49mtPmeyyVKKW1myaAXt6BhJ"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "maestro" {
|
||||
t.Fatalf("expected maestro signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "buy" {
|
||||
t.Fatalf("expected buy event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "Gyz2QP89RuoFG55V2xVpyGiG7rMya41j1ZbFNbd1WfF9" {
|
||||
t.Fatalf("expected maker Gyz2QP89RuoFG55V2xVpyGiG7rMya41j1ZbFNbd1WfF9, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "2aMTnF7Kz9aRhTMmiVuSwbQ9Msrdkgm4RvFoJfZPbonk" {
|
||||
t.Fatalf("expected token0 address 2aMTnF7Kz9aRhTMmiVuSwbQ9Msrdkgm4RvFoJfZPbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 11089351947578 {
|
||||
t.Fatalf("expected token0 amount 1052495896871, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 1000000000 {
|
||||
t.Fatalf("expected token1 amount 1000000000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if !signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMaestroBonkSell(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("HPjn8EFrUcMxqQrdRzJkgXp85cGUHLFVkY4nZ7EUBbir"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "3cXpA8C5uizp1iK8fV8eoxoonT5BcT7G52wN9aRsRwi9pUCUyuDt2FwXVVtbkvocxoAD82ZQWjCaLRgswgcszTHQ"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "maestro" {
|
||||
t.Fatalf("expected maestro signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "sell" {
|
||||
t.Fatalf("expected sell event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "fwBVjnPQHwrAVWP4VnwbA1Y9BiDWnixJ39G5PA59sZv" {
|
||||
t.Fatalf("expected maker fwBVjnPQHwrAVWP4VnwbA1Y9BiDWnixJ39G5PA59sZv, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "9553NoaZEQGYsttrym5w85RQVHhwVi3BfNU9GLfEbonk" {
|
||||
t.Fatalf("expected token0 address 9553NoaZEQGYsttrym5w85RQVHhwVi3BfNU9GLfEbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 11299453877090 {
|
||||
t.Fatalf("expected token0 amount 11299453877090, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 892516080 {
|
||||
t.Fatalf("expected token1 amount 892516080, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBloomRouterBonkBuy(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("3xRUDpys1Yy96y5QVBfhW94ukphuDXHsKh4mTAFofA6S"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "5XEGgHXokNKQpgUUf1zS8LXFRHR7XNBaPiRZxGumFkBH23b3TsTjs6wJ1NRHxf6xRvYBLwXWWJdw7AiNzAAgUzgg"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "bloomrouter" {
|
||||
t.Fatalf("expected bloomrouter signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "buy" {
|
||||
t.Fatalf("expected buy event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "9ozVSeSsgASFDUjveLNQV697kAavStvpXmKP65oMt2Ji" {
|
||||
t.Fatalf("expected maker 9ozVSeSsgASFDUjveLNQV697kAavStvpXmKP65oMt2Ji, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "6qzh89yisR498GsQysqzj69AW9BJc39LWhXtzxudbonk" {
|
||||
t.Fatalf("expected token0 address 6qzh89yisR498GsQysqzj69AW9BJc39LWhXtzxudbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 0 {
|
||||
t.Fatalf("expected token0 amount 0, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 1500000000 {
|
||||
t.Fatalf("expected token1 amount 1500000000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if !signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBloomRouterBonkSell(t *testing.T) {
|
||||
rpcUrl := os.Getenv("SOL_RPC_URL")
|
||||
if rpcUrl == "" {
|
||||
t.Fatalf("SOL_RPC_URL is not set")
|
||||
}
|
||||
|
||||
client := rpc.New(rpcUrl)
|
||||
loader := manuallyLoadAddressTable(t, client, solana.MustPublicKeyFromBase58("3xRUDpys1Yy96y5QVBfhW94ukphuDXHsKh4mTAFofA6S"))
|
||||
ch := make(chan TxSignal)
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
ParseTransactionForSubscribe(
|
||||
context.Background(),
|
||||
getTransaction(t, client, "MMdN29CYKDKDurbhUHn51AyyJ5ZEZq1F1TFJTrVYhRT1moaURqXAHJb2pFus4KrXAdAFo5Hr1Jw4bgE2EWeLXf6"),
|
||||
loader,
|
||||
ch,
|
||||
closed,
|
||||
)
|
||||
}()
|
||||
go func() {
|
||||
<-closed
|
||||
close(ch)
|
||||
}()
|
||||
signals := make([]TxSignal, 0)
|
||||
for signal := range ch {
|
||||
signals = append(signals, signal)
|
||||
}
|
||||
|
||||
if len(signals) != 1 {
|
||||
t.Fatalf("expected 1 signal, got %d", len(signals))
|
||||
}
|
||||
|
||||
signal := signals[0]
|
||||
if signal.Label != "bloomrouter" {
|
||||
t.Fatalf("expected bloomrouter signal, got %s", signal.Label)
|
||||
}
|
||||
if signal.Event != "sell" {
|
||||
t.Fatalf("expected sell event, got %s", signal.Event)
|
||||
}
|
||||
if signal.Maker != "HWiPtESw8pvhQWm1a7vbHg9rsnee13Pmb7dUGVC3f4mZ" {
|
||||
t.Fatalf("expected maker HWiPtESw8pvhQWm1a7vbHg9rsnee13Pmb7dUGVC3f4mZ, got %s", signal.Maker)
|
||||
}
|
||||
if signal.Token0Address != "6Yd5AGP4E4b1prHxJmuHexUToohiNKiQSG2nXvXWbonk" {
|
||||
t.Fatalf("expected token0 address 6Yd5AGP4E4b1prHxJmuHexUToohiNKiQSG2nXvXWbonk, got %s", signal.Token0Address)
|
||||
}
|
||||
if signal.Token0AmountUint64 != 6448480270053 {
|
||||
t.Fatalf("expected token0 amount 6448480270053, got %d", signal.Token0AmountUint64)
|
||||
}
|
||||
if signal.Token1AmountUint64 != 1000 {
|
||||
t.Fatalf("expected token1 amount 1000, got %d", signal.Token1AmountUint64)
|
||||
}
|
||||
if signal.ExactSOL {
|
||||
t.Fatalf("expected ExactSOL false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user