chore: support bloomrouter bonk buy/sell
This commit is contained in:
@@ -26,11 +26,80 @@ func parseBloomRouterInstruction(tx VersionedTransaction, instructionIndex int)
|
|||||||
return nil, nil
|
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 (
|
var (
|
||||||
amount uint64
|
amount uint64
|
||||||
sol uint64
|
sol uint64
|
||||||
exactIn bool
|
exactIn bool
|
||||||
event string
|
event string
|
||||||
|
program string
|
||||||
|
base solana.PublicKey
|
||||||
|
quote solana.PublicKey
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
args, err := decodeBloomRouterArgs(instruction.Data)
|
args, err := decodeBloomRouterArgs(instruction.Data)
|
||||||
@@ -41,8 +110,39 @@ func parseBloomRouterInstruction(tx VersionedTransaction, instructionIndex int)
|
|||||||
case 0:
|
case 0:
|
||||||
event = "buy"
|
event = "buy"
|
||||||
exactIn = true
|
exactIn = true
|
||||||
|
program = "Pump"
|
||||||
|
base, quote, err = findPumpFun()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
case 1:
|
case 1:
|
||||||
event = "sell"
|
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:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -61,43 +161,17 @@ func parseBloomRouterInstruction(tx VersionedTransaction, instructionIndex int)
|
|||||||
return nil, err
|
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{
|
return TxSignalBatch{&TxSignal{
|
||||||
TxHash: tx.Signatures[0].String(),
|
TxHash: tx.Signatures[0].String(),
|
||||||
Label: "bloomrouter",
|
Label: "bloomrouter",
|
||||||
Maker: maker.String(),
|
Maker: maker.String(),
|
||||||
Token0Address: mint.String(),
|
Token0Address: base.String(),
|
||||||
Token1Address: wsolMint,
|
Token1Address: quote.String(),
|
||||||
Token0Amount: formatTokenAmount(amount),
|
Token0Amount: formatTokenAmount(amount),
|
||||||
Token1Amount: formatSolAmount(sol),
|
Token1Amount: formatSolAmount(sol),
|
||||||
Program: "Pump",
|
Program: program,
|
||||||
Event: event,
|
Event: event,
|
||||||
ExactSOL: exactIn,
|
ExactSOL: exactIn,
|
||||||
IsToken2022: false,
|
|
||||||
IsMayhemMode: false,
|
|
||||||
Block: tx.Block,
|
Block: tx.Block,
|
||||||
Token0AmountUint64: amount,
|
Token0AmountUint64: amount,
|
||||||
Token1AmountUint64: sol,
|
Token1AmountUint64: sol,
|
||||||
|
|||||||
@@ -1124,3 +1124,115 @@ func TestParseMaestroBonkSell(t *testing.T) {
|
|||||||
t.Fatalf("expected ExactSOL false, got true")
|
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