package shreder import ( "encoding/binary" "fmt" "github.com/gagliardetto/solana-go" ) var bloomRouterProgramID = solana.MustPublicKeyFromBase58("b1oomGGqPKGD6errbyfbVMBuzSC8WtAAYo8MwNafWW1") var pumpFunAccount = solana.MustPublicKeyFromBase58("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf") type bloomRouterArgs struct { Side uint16 SolAmount uint64 TokenAmount uint64 } func parseBloomRouterInstruction(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) < 26 { 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) if err != nil { return nil, err } switch args.Side { 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 } if args.SolAmount > ^uint64(0)/100 { return nil, fmt.Errorf("bloomrouter sol amount overflow") } // bloomrouter SOL amount has 2 fewer decimals than lamports. sol = args.SolAmount * 100 amount = args.TokenAmount if len(instruction.Accounts) == 0 { return nil, fmt.Errorf("accounts too short") } maker, err := tx.GetAccount(int(instruction.Accounts[0])) if err != nil { return nil, err } return TxSignalBatch{&TxSignal{ TxHash: tx.Signatures[0].String(), Label: "bloomrouter", Maker: maker.String(), Token0Address: base.String(), Token1Address: quote.String(), Token0Amount: formatTokenAmount(amount), Token1Amount: formatSolAmount(sol), Program: program, Event: event, ExactSOL: exactIn, Block: tx.Block, Token0AmountUint64: amount, Token1AmountUint64: sol, }}, nil } func decodeBloomRouterArgs(data []byte) (bloomRouterArgs, error) { if len(data) < 26 { return bloomRouterArgs{}, fmt.Errorf("data too short for bloomrouter args, len=%d", len(data)) } return bloomRouterArgs{ Side: binary.BigEndian.Uint16(data[8:10]), SolAmount: binary.LittleEndian.Uint64(data[10:18]), TokenAmount: binary.LittleEndian.Uint64(data[18:26]), }, nil }