chore: support maestro bonk buy/sell
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -1012,3 +1012,115 @@ func TestParseFlasBonkSell(t *testing.T) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user