chore: support maestro bonk buy/sell
This commit is contained in:
@@ -106,7 +106,6 @@ func maestroMultiSwap2DexName(dexID uint8) string {
|
|||||||
return "OrcaWhirPool"
|
return "OrcaWhirPool"
|
||||||
case 9:
|
case 9:
|
||||||
return "MeteoraPools"
|
return "MeteoraPools"
|
||||||
|
|
||||||
case 10:
|
case 10:
|
||||||
return "MeteoraDynamicBondingCurve"
|
return "MeteoraDynamicBondingCurve"
|
||||||
default:
|
default:
|
||||||
@@ -125,20 +124,7 @@ func parseMaestroInstruction(tx VersionedTransaction, idx int) (TxSignalBatch, e
|
|||||||
return parseMaestroInstructionDataAndAccounts(tx, ix.Data, ix.Accounts)
|
return parseMaestroInstructionDataAndAccounts(tx, ix.Data, ix.Accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixData []byte, ixAccounts []uint8) (TxSignalBatch, error) {
|
func parsePumpAMMMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixAccounts []uint8, args *MaestroMultiSwap2Args) (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
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
event string
|
event string
|
||||||
token0Amount uint64
|
token0Amount uint64
|
||||||
@@ -255,3 +241,91 @@ func parseMaestroInstructionDataAndAccounts(tx VersionedTransaction, ixData []by
|
|||||||
},
|
},
|
||||||
}, nil
|
}, 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")
|
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