async def handle_shutdown(self, signal_received=None):
        """Handle shutdown signal gracefully"""
        # Set shutdown flag
        if self.shutdown_requested:
            return
            
        signal_name = signal_received.name if signal_received else "unknown"
        logger.info(f"Received shutdown signal ({signal_name}), cleaning up...")
        self.shutdown_requested = True
        
        # Print final statistics
        self.print_stats()
        
        # Wait for active trades to complete (with timeout)
        if self.active_trades > 0:
            logger.info(f"Waiting for {self.active_trades} active trades to complete...")
            for _ in range(10):  # Wait up to 5 seconds
                if self.active_trades == 0:
                    break
                await asyncio.sleep(0.5)
        
        logger.info("Shutdown complete")
        
    async def start(self):
        """Start the scanner and trader"""
        # Register signal handlers
        loop = asyncio.get_running_loop()
        for sig in (signal.SIGINT, signal.SIGTERM):
            try:
                loop.add_signal_handler(
                    sig, lambda sig=sig: asyncio.create_task(self.handle_shutdown(sig))
                )
            except NotImplementedError:
                # Windows does not support add_signal_handler
                pass
                
        # Start the main monitoring task
        await self.monitor_raydium_accounts()
        
async def main():
    """Main entry point"""
    # Create and start the trader
    trader = SolanaTrader()
    
    try:
        await trader.start()
    except KeyboardInterrupt:
        logger.info("Interrupted by user")
        await trader.handle_shutdown()
    except Exception as e:
        logger.error(f"Unhandled exception: {e}")
        logger.error(traceback.format_exc())
    finally:
        # Final cleanup
        logger.info("Exiting")
        
if __name__ == "__main__":
    # Run the main function
    asyncio.run(main())    async def process_account(self, pubkey: str, account_data: str) -> None:
        """Process a pool account from Raydium program"""
        try:
            # Skip if already seen
            if pubkey in self.pools_seen:
                return
                
            # Mark as seen
            self.pools_seen.add(pubkey)
            
            # Update counter
            self.stats["accounts_scanned"] += 1
            
            # Decode base64 data
            raw_data = base64.b64decode(account_data)
            
            # First check for the specific pattern that works reliably
            pool = self.check_pool_specific_pattern(raw_data, pubkey)
            if pool:
                # Already evaluated this pool with the specific pattern
                if pool.is_tradable:
                    # Check trade rate limiting
                    current_time = time.time()
                    last_trade_time = self.stats.get("last_trade_time", 0)
                    min_interval = config.MIN_TRADE_INTERVAL
                    
                    if current_time - last_trade_time >= min_interval:
                        self.stats["last_trade_time"] = current_time
                        await self.execute_trade(pool)
                return
                
            # Fall back to the more generic check if specific pattern didn't match
            pool = self.check_pool(raw_data, pubkey)
            
            # Skip non-secure pools
            if not pool or not pool.is_secure:
                return
                
            # Evaluate if we should trade
            should_trade, confidence = self.evaluate_trade(pool)
            
            if should_trade:
                # Check trade rate limiting
                current_time = time.time()
                last_trade_time = self.stats.get("last_trade_time", 0)
                min_interval = config.MIN_TRADE_INTERVAL
                
                if current_time - last_trade_time >= min_interval:
                    self.stats["last_trade_time"] = current_time
                    await self.execute_trade(pool)
        
        except Exception as e:
            logger.error(f"Error processing account {pubkey}: {e}")
            
    async def monitor_raydium_accounts(self):
        """Connect to Helius WebSocket and monitor Raydium accounts"""
        # Print startup banner
        self.print_startup_banner()
        
        # Check initial balances
        await self.check_initial_balances()
        
        # Initialize reconnect parameters
        retry_delay = 1
        max_retry_delay = 30
        
        while not self.shutdown_requested:
            try:
                # Connect to WebSocket with API key in headers
                headers = {
                    "api-key": config.API_KEY
                }
                
                logger.info(f"Connecting to WebSocket: {config.HELIUS_WS_URL}")
                async with websockets.connect(
                    config.HELIUS_WS_URL,
                    extra_headers=headers,
                    ping_interval=20,
                    ping_timeout=30
                ) as ws:
                    # Reset retry delay on successful connection
                    retry_delay = 1
                    logger.info("Connected to Helius WebSocket")
                    
                    # Subscribe to Raydium program logs
                    subscription_msg = json.dumps({
                        "jsonrpc": "2.0",
                        "id": 1,
                        "method": "logsSubscribe",
                        "params": [
                            {"mentions": [config.RAYDIUM_PROGRAM_ID]},
                            {"commitment": "processed"}
                        ]
                    })
                    
                    logger.info(f"Subscribing to Raydium program logs: {config.RAYDIUM_PROGRAM_ID}")
                    await ws.send(subscription_msg)
                    
                    # Wait for subscription confirmation
                    confirmation = await ws.recv()
                    confirmation_data = json.loads(confirmation)
                    
                    if "error" in confirmation_data:
                        logger.error(f"Subscription failed: {confirmation_data['error']}")
                        await asyncio.sleep(retry_delay)
                        retry_delay = min(retry_delay * 2, max_retry_delay)
                        continue
                        
                    logger.info("Subscription confirmed, scanning for secure token pools...")
                    
                    # Process messages
                    while not self.shutdown_requested:
                        # Print stats periodically
                        current_time = time.time()
                        if current_time - self.stats["last_update"] >= 60:  # Every minute
                            self.stats["last_update"] = current_time
                            self.print_stats()
                        
                        try:
                            # Get next message with timeout
                            try:
                                msg = await asyncio.wait_for(ws.recv(), timeout=30)
                            except asyncio.TimeoutError:
                                logger.debug("WebSocket timeout, sending ping")
                                pong_waiter = await ws.ping()
                                await asyncio.wait_for(pong_waiter, timeout=10)
                                logger.debug("Ping acknowledged")
                                continue
                            
                            # Parse the message
                            data = json.loads(msg)
                            
                            # Check if it's a valid log message
                            if "params" in data and "result" in data["params"]:
                                # Get the account data
                                result = data["params"]["result"]
                                if "value" in result:
                                    value = result["value"]
                                    
                                    # Get account data if available
                                    if "account" in value and "data" in value["account"]:
                                        account_data = value["account"]["data"][0]
                                        pubkey = value.get("pubkey", "unknown")
                                        
                                        # Process the account asynchronously
                                        await self.process_account(pubkey, account_data)
                                        
                        except json.JSONDecodeError:
                            logger.warning("Failed to decode JSON message")
                            continue
                        except websockets.exceptions.ConnectionClosed:
                            logger.warning("WebSocket connection closed")
                            break
                        except Exception as e:
                            logger.error(f"Error processing message: {e}")
                            logger.debug(traceback.format_exc())
                            continue
                            
            except websockets.exceptions.ConnectionClosed:
                logger.warning(f"WebSocket connection closed, reconnecting in {retry_delay}s...")
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)
            except Exception as e:
                logger.error(f"WebSocket error: {e}")
                logger.debug(traceback.format_exc())
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)    def print_stats(self):
        """Print current statistics"""
        elapsed = time.time() - self.stats["start_time"]
        scan_rate = self.stats["accounts_scanned"] / max(1, elapsed)
        
        logger.info("\n" + "=" * 50)
        logger.info("📊 SCANNER STATISTICS")
        logger.info("=" * 50)
        logger.info(f"Runtime: {int(elapsed//3600):02d}:{int((elapsed%3600)//60):02d}:{int(elapsed%60):02d}")
        logger.info(f"Accounts Scanned: {self.stats['accounts_scanned']} ({scan_rate:.1f}/sec)")
        logger.info(f"Pools Found: {self.stats['pools_found']}")
        logger.info(f"Mint Revoked: {self.stats['mint_revoked_found']}")
        logger.info(f"LP Locked: {self.stats['lp_locked_found']}")
        logger.info(f"Secure Pools: {self.stats['secure_pools_found']}")
        
        if not config.SCAN_ONLY:
            logger.info("=" * 50)
            logger.info("💰 TRADING STATISTICS")
            logger.info("=" * 50)
            logger.info(f"Trades Attempted: {self.stats['trades_attempted']}")
            logger.info(f"Trades Successful: {self.stats['trades_successful']}")
            
            if self.stats["trades_successful"] > 0:
                success_rate = (self.stats["trades_successful"] / self.stats["trades_attempted"]) * 100
                logger.info(f"Success Rate: {success_rate:.1f}%")
                
                avg_execution = sum(self.stats["execution_times"]) / len(self.stats["execution_times"]) \
                    if self.stats["execution_times"] else 0
                logger.info(f"Avg Execution Time: {avg_execution*1000:.1f}ms")
                
            logger.info(f"Total Profit: {self.stats['total_profit']:.6f} SOL (${self.stats['total_profit'] * 145:.2f})")
            logger.info(f"SOL Balance: {self.stats['sol_balance']:.4f}")
            logger.info(f"WSOL Balance: {self.stats['wsol_balance']:.4f}")
        
        logger.info("=" * 50)
        
        # If any secure pools found, print their summary
        if self.secure_pools:
            logger.info("\n" + "=" * 50)
            logger.info("🔒 SECURE POOLS FOUND")
            logger.info("=" * 50)
            
            for i, pool in enumerate(self.secure_pools[-10:]):  # Show last 10
                logger.info(f"{i+1}. {pool.pubkey} ({pool.mint})")
                logger.info(f"   LP Lock: {pool.lp_percentage:.1f}%")
                
                if pool.liquidity_usd is not None:
                    logger.info(f"   Liquidity: ${pool.liquidity_usd:.2f}")
                    
                should_trade, confidence = self.evaluate_trade(pool)
                logger.info(f"   Trade Confidence: {confidence:.2f}")
                logger.info(f"   Tradable: {'✅' if should_trade else '❌'}")
                logger.info("")
                
            logger.info("=" * 50)    async def execute_trade(self, pool: SecurePool) -> bool:
        """Execute a trade on the given pool"""
        # Prevent trading if scan-only mode
        if config.SCAN_ONLY:
            logger.info(f"Skipping trade for {pool.mint} (scan-only mode)")
            return False
            
        # Make sure we're not exceeding active trades limit
        if self.active_trades >= self.max_active_trades:
            logger.warning(f"Too many active trades ({self.active_trades}), skipping opportunity")
            return False
            
        async with self.trade_lock:
            # Increment active trades counter
            self.active_trades += 1
            self.stats["trades_attempted"] += 1
            
            # Create trade record for history
            trade_record = {
                "pool": pool.pubkey,
                "mint": pool.mint,
                "time": datetime.now().strftime("%H:%M:%S"),
                "front_run_amount": config.FRONT_RUN_AMOUNT,
                "front_tx": None,
                "back_tx": None,
                "profit": 0.0,
                "status": "started",
                "execution_time": 0.0
            }
            start_time = time.time()
            
            try:
                # Check balances first
                sol_balance, token_balances = await self.trading_core.get_balances()
                wsol_balance = token_balances.get(config.SOL_MINT, 0.0)
                
                # Update our stats
                self.stats["sol_balance"] = sol_balance
                self.stats["wsol_balance"] = wsol_balance
                
                # Make sure we have enough balance
                if wsol_balance < config.FRONT_RUN_AMOUNT:
                    logger.warning(f"Insufficient WSOL balance for trade: {wsol_balance} < {config.FRONT_RUN_AMOUNT}")
                    trade_record["status"] = "failed_insufficient_balance"
                    self.active_trades -= 1
                    self.trade_history.append(trade_record)
                    return False
                    
                if sol_balance < config.MIN_SOL_BALANCE:
                    logger.warning(f"Insufficient SOL balance for gas: {sol_balance} < {config.MIN_SOL_BALANCE}")
                    trade_record["status"] = "failed_insufficient_gas"
                    self.active_trades -= 1
                    self.trade_history.append(trade_record)
                    return False
                
                # Ensure token associated token account exists
                token_ata = await self.trading_core.ensure_ata(pool.mint)
                logger.info(f"Token ATA ready: {token_ata}")
                
                # ==== FRONT-RUN TRANSACTION ====
                # Create buy transaction with amount in SOL
                amount_in_sol = config.FRONT_RUN_AMOUNT
                
                # Create swap parameters
                swap_params = SwapParams(
                    token_mint=pool.mint, 
                    amount_in=amount_in_sol,
                    is_buy=True,
                    slippage_bps=config.SLIPPAGE_BPS,
                    compute_limit=config.COMPUTE_LIMIT,
                    priority_fee=config.PRIORITY_FEE
                )
                
                # Execute front-run transaction
                logger.info(f"Executing front-run for {pool.mint} with {amount_in_sol} SOL")
                front_run_start = time.time()
                front_result = await self.trading_core.execute_swap(swap_params)
                front_run_time = time.time() - front_run_start
                
                # Check if front-run succeeded
                if not front_result.success:
                    logger.error(f"Front-run failed: {front_result.error}")
                    trade_record["status"] = "failed_front_run"
                    trade_record["front_tx"] = front_result.signature
                    self.active_trades -= 1
                    self.trade_history.append(trade_record)
                    return False
                    
                # Record front-run success
                front_tx_id = front_result.signature
                trade_record["front_tx"] = front_tx_id
                logger.info(f"Front-run transaction sent: {front_tx_id} in {front_run_time*1000:.1f}ms")
                
                # ==== WAIT FOR PRICE IMPACT ====
                # Wait a bit for price to stabilize after our buy
                await asyncio.sleep(0.1)
                
                # ==== BACK-RUN TRANSACTION ====
                # Create sell all transaction
                swap_params = SwapParams(
                    token_mint=pool.mint, 
                    amount_in=0,  # Sell all
                    is_buy=False,
                    slippage_bps=config.SLIPPAGE_BPS,
                    compute_limit=config.COMPUTE_LIMIT,
                    priority_fee=config.PRIORITY_FEE + 250000  # Higher priority for exit
                )
                
                # Execute back-run transaction
                logger.info(f"Executing back-run for {pool.mint}")
                back_run_start = time.time()
                back_result = await self.trading_core.execute_swap(swap_params)
                back_run_time = time.time() - back_run_start
                
                # Check if back-run succeeded
                if not back_result.success:
                    logger.error(f"Back-run failed: {back_result.error}")
                    trade_record["status"] = "failed_back_run"
                    trade_record["back_tx"] = back_result.signature
                    self.active_trades -= 1
                    self.trade_history.append(trade_record)
                    return False
                    
                # Record back-run success
                back_tx_id = back_result.signature
                trade_record["back_tx"] = back_tx_id
                logger.info(f"Back-run transaction sent: {back_tx_id} in {back_run_time*1000:.1f}ms")
                
                # ==== CALCULATE PROFIT ====
                # Get updated balances
                new_sol_balance, new_token_balances = await self.trading_core.get_balances()
                new_wsol_balance = new_token_balances.get(config.SOL_MINT, 0.0)
                
                # Calculate profit
                profit = (new_wsol_balance - wsol_balance) - 0.002  # Subtract fees
                
                # Update stats
                self.stats["sol_balance"] = new_sol_balance
                self.stats["wsol_balance"] = new_wsol_balance
                self.stats["trades_successful"] += 1
                self.stats["total_profit"] += profit
                
                # Record execution time
                execution_time = time.time() - start_time
                self.stats["execution_times"].append(execution_time)
                trade_record["execution_time"] = execution_time
                trade_record["profit"] = profit
                trade_record["status"] = "completed"
                
                # Log success
                logger.info(f"Trade complete in {execution_time*1000:.1f}ms - Profit: {profit:.6f} SOL (${profit * 145:.2f})")
                
                # Store last trade time
                self.stats["last_trade_time"] = time.time()
                
                return True
                
            except Exception as e:
                logger.error(f"Error executing trade: {e}")
                logger.error(traceback.format_exc())
                trade_record["status"] = f"error: {str(e)}"
                return False
                
            finally:
                # Always decrement active trades counter
                self.active_trades -= 1
                self.trade_history.append(trade_record)
                
                # Keep only the last 100 trade records
                if len(self.trade_history) > 100:
                    self.trade_history = self.trade_history[-100:]    def evaluate_trade(self, pool: SecurePool) -> Tuple[bool, float]:
        """Evaluate whether to trade on this pool and calculate confidence score"""
        if not pool.is_tradable:
            return False, 0.0
            
        # Start with base confidence
        confidence = 0.5
        
        # Increase confidence based on liquidity
        if pool.liquidity_usd is not None:
            if pool.liquidity_usd >= 150000:
                confidence += 0.2
            elif pool.liquidity_usd >= 80000:
                confidence += 0.1
                
        # Check mint format (increases confidence if it's a proper mint)
        if pool.mint != "unknown" and len(pool.mint) >= 32:
            confidence += 0.1
            
        # Higher LP lock percentage increases confidence
        if pool.lp_percentage >= 99.0:  # 99% or higher
            confidence += 0.1
            
        # Match specific pattern we know works well
        if pool.size == 752 and pool.first_byte == 0x06:
            confidence += 0.2
            
        # Consider market cap if available
        if pool.market_cap is not None:
            if pool.market_cap < 10000000:  # Less than $10M
                confidence += 0.1
                
        # Check liquidity threshold
        min_liquidity = config.MIN_LIQUIDITY_USD
        if pool.liquidity_usd is None or pool.liquidity_usd < min_liquidity:
            confidence -= 0.5  # Significant penalty
        
        # Calculate final decision - Require at least 0.65 confidence to trade
        should_trade = (
            confidence >= 0.65 and 
            self.trading_active and 
            not config.SCAN_ONLY
        )
        
        return should_trade, confidence

    async def check_initial_balances(self):
        """Check initial SOL and WSOL balances"""
        try:
            # Get balances
            sol_balance, token_balances = await self.trading_core.get_balances()
            wsol_balance = token_balances.get(config.SOL_MINT, 0.0)
            
            # Update stats
            self.stats["sol_balance"] = sol_balance
            self.stats["wsol_balance"] = wsol_balance
            
            # Log initial balances
            logger.info(f"Initial balances - SOL: {sol_balance:.4f}, WSOL: {wsol_balance:.4f}")
            
            # Check if balances are sufficient
            if sol_balance < config.MIN_SOL_BALANCE:
                logger.warning(f"Low SOL balance: {sol_balance:.4f} < {config.MIN_SOL_BALANCE}")
                
            if wsol_balance < config.FRONT_RUN_AMOUNT:
                logger.warning(f"WSOL balance below position size: {wsol_balance:.4f} < {config.FRONT_RUN_AMOUNT}")
                
            return sol_balance, token_balances
            
        except Exception as e:
            logger.error(f"Balance check error: {e}")
            logger.debug(traceback.format_exc())
            return 0.0, {}#!/usr/bin/env python3
"""
Raydium Secure Token Scanner and Trader
---------------------------------------
Scans for Raydium pools with mint authority revoked and liquidity locked,
then automatically executes trades on promising tokens.

Features:
- Multiple security pattern detection methods
- GPU accelerated calculations with PyTorch CUDA
- Direct integration with Rust trading core
- Real-time performance metrics
"""

import asyncio
import websockets
import json
import base64
import base58
import time
import os
import traceback
import signal
import logging
import numpy as np
import threading
from datetime import datetime
from typing import Dict, List, Optional, Set, Any, Tuple, Union
from dataclasses import dataclass
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("scanner.log"),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger("RaydiumScanner")

# Try to import GPU acceleration
try:
    import torch
    TORCH_AVAILABLE = True
    logger.info(f"PyTorch version: {torch.__version__}")
    logger.info(f"CUDA available: {torch.cuda.is_available()}")
except ImportError:
    TORCH_AVAILABLE = False
    logger.warning("PyTorch not available, using CPU only")

# Try to import Rust trading core
try:
    from solana_rust_bot import RaydiumTradingCore, SwapParams
    RUST_CORE_AVAILABLE = True
    logger.info("✅ Rust trading core loaded successfully")
except ImportError:
    logger.warning("⚠️ Rust trading core not available, using Python fallback")
    RUST_CORE_AVAILABLE = False

    # Define fallback classes if Rust core isn't available
    class SwapParams:
        def __init__(self, token_mint, amount_in, is_buy, **kwargs):
            self.token_mint = token_mint
            self.amount_in = amount_in
            self.is_buy = is_buy

    class RaydiumTradingCore:
        def __init__(self, rpc_url=None, **kwargs):
            self.rpc_url = rpc_url
            logger.info(f"Initialized Python fallback trading core with RPC: {rpc_url}")
            
        async def get_balances(self):
            return 0.5, {"So11111111111111111111111111111111111111112": 0.4}
            
        def get_public_key(self):
            return os.getenv("SOLANA_PUBLIC_KEY")
            
        async def execute_custom_swap(self, token_mint, instruction_data):
            logger.info(f"Would execute custom swap for {token_mint}")
            return {"signature": "simulated_tx_signature", "success": True}
            
        async def execute_swap(self, params):
            logger.info(f"Would execute swap: buy={params.is_buy}, amount={params.amount_in}")
            return {"signature": "simulated_tx_signature", "success": True}
            
        async def ensure_ata(self, token_mint):
            return os.getenv("WSOL_TOKEN_ACCOUNT")

# Configuration class that loads from environment variables
class Config:
    def __init__(self):
        # Network configuration
        self.RPC_URL = os.getenv("RPC_URL", "https://api.mainnet-beta.solana.com")
        self.WS_URL = os.getenv("WS_URL", "wss://api.mainnet-beta.solana.com")
        self.HELIUS_WS_URL = os.getenv("HELIUS_WS_URL", "")
        self.API_KEY = os.getenv("API_KEY", "")
        
        # In case HELIUS_WS_URL is not specified but we have WS_URL
        if not self.HELIUS_WS_URL and self.WS_URL:
            self.HELIUS_WS_URL = self.WS_URL
        
        # Program IDs
        self.RAYDIUM_PROGRAM_ID = os.getenv("RAYDIUM_PROGRAM_ID", "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")
        self.SERUM_PROGRAM_ID = os.getenv("SERUM_PROGRAM_ID", "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
        self.TOKEN_PROGRAM_ID = os.getenv("TOKEN_PROGRAM_ID", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
        self.SYSTEM_PROGRAM_ID = os.getenv("SYSTEM_PROGRAM_ID", "11111111111111111111111111111111")
        self.ASSOCIATED_TOKEN_PROGRAM_ID = os.getenv("ASSOCIATED_TOKEN_PROGRAM_ID", "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
        
        # Token configurations
        self.SOL_MINT = os.getenv("SOL_MINT", "So11111111111111111111111111111111111111112")
        self.WSOL_ADDRESS = os.getenv("WSOL_ADDRESS", self.SOL_MINT)
        
        # Wallet settings
        self.SOLANA_PUBLIC_KEY = os.getenv("SOLANA_PUBLIC_KEY", "")
        self.WSOL_TOKEN_ACCOUNT = os.getenv("WSOL_TOKEN_ACCOUNT", "")
        self.KEYPAIR_PATH = os.getenv("KEYPAIR_PATH", "keypair.bin")
        
        # Trading parameters
        self.COMPUTE_LIMIT = int(os.getenv("COMPUTE_LIMIT", "50000"))
        self.PRIORITY_FEE = int(os.getenv("PRIORITY_FEE", "750000"))
        self.SKIP_PREFLIGHT = os.getenv("SKIP_PREFLIGHT", "true").lower() == "true"
        self.MIN_TRADE_INTERVAL = float(os.getenv("MIN_TRADE_INTERVAL", "0.2"))
        self.MAX_RETRIES = int(os.getenv("MAX_RETRIES", "3"))
        self.MAX_TRADES_PER_MINUTE = int(os.getenv("MAX_TRADES_PER_MINUTE", "60"))
        self.MAX_ACTIVE_MEVS = int(os.getenv("MAX_ACTIVE_MEVS", "1"))
        
        # Trading strategy parameters
        self.FRONT_RUN_AMOUNT = float(os.getenv("FRONT_RUN_AMOUNT", "0.35"))
        self.MIN_VICTIM_SIZE = float(os.getenv("SANDWICH_MIN_VICTIM_SIZE", "5.0"))
        self.MAX_VICTIM_SIZE = float(os.getenv("SANDWICH_MAX_VICTIM_SIZE", "320.0"))
        self.MIN_PROFIT = float(os.getenv("MIN_PROFIT", "0.002"))
        self.SLIPPAGE_BPS = int(os.getenv("SLIPPAGE_BPS", "1000"))
        
        # Balance thresholds
        self.MIN_SOL_BALANCE = float(os.getenv("MIN_SOL_BALANCE", "0.03"))
        self.MIN_WSOL_BALANCE = float(os.getenv("MIN_WSOL_BALANCE", "0.02"))
        
        # Scanner-specific settings
        self.SCAN_ONLY = os.getenv("SCAN_ONLY", "false").lower() == "true"
        self.AUTO_TRADE = os.getenv("TRADING", "true").lower() == "true"
        self.MIN_LIQUIDITY_USD = float(os.getenv("MIN_LIQUIDITY_USD", "60000"))
        self.MAX_MARKET_CAP = float(os.getenv("MAX_MARKET_CAP", "50000000"))
        self.MIN_TOKEN_AGE_SECONDS = int(os.getenv("MIN_TOKEN_AGE_SECONDS", "86400"))  # 1 day
        self.LP_LOCK_THRESHOLD = float(os.getenv("LP_LOCK_THRESHOLD", "95.0"))  # 95% locked
        
        # Amm configuration required for trading
        self.AMM_ID = os.getenv("AMM_ID", "")
        self.MARKET_AMM_AUTHORITY = os.getenv("MARKET_AMM_AUTHORITY", "")
        self.MARKET_AMM_OPEN_ORDERS = os.getenv("MARKET_AMM_OPEN_ORDERS", "")
        self.MARKET_AMM_TARGET = os.getenv("MARKET_AMM_TARGET", "")
        self.MARKET_POOL_TOKEN_ACCOUNT = os.getenv("MARKET_POOL_TOKEN_ACCOUNT", "")
        self.MARKET_POOL_PC_TOKEN_ACCOUNT = os.getenv("MARKET_POOL_PC_TOKEN_ACCOUNT", "")
        
        # GPU configuration
        self.USE_GPU = TORCH_AVAILABLE and torch.cuda.is_available()
        if self.USE_GPU:
            self.CUDA_STREAM_PRIORITY = int(os.getenv("STREAM_PRIORITY", "-3"))
            self.ALLOW_TF32 = os.getenv("ALLOW_TF32", "true").lower() == "true"
            self.CUDA_BENCHMARK = os.getenv("CUDA_BENCHMARK", "true").lower() == "true"
            
            # Initialize CUDA preferences if available
            if self.ALLOW_TF32:
                try:
                    torch.set_float32_matmul_precision("high")
                    torch.backends.cuda.matmul.allow_tf32 = True
                    torch.backends.cudnn.allow_tf32 = True
                except Exception as e:
                    logger.warning(f"Failed to set TF32 options: {e}")
                    
            if self.CUDA_BENCHMARK:
                torch.backends.cudnn.benchmark = True
                
            self.cuda_stream = torch.cuda.Stream(priority=self.CUDA_STREAM_PRIORITY)
            logger.info(f"CUDA GPU enabled: {torch.cuda.get_device_name(0)}")
        else:
            logger.info("GPU not available, using CPU only")

    def get_int(self, key, default=0):
        """Get integer value from environment variables"""
        value = os.getenv(key, str(default))
        try:
            return int(value)
        except ValueError:
            return default
            
    def get_float(self, key, default=0.0):
        """Get float value from environment variables"""
        value = os.getenv(key, str(default))
        try:
            return float(value)
        except ValueError:
            return default
            
    def get_bool(self, key, default=False):
        """Get boolean value from environment variables"""
        value = os.getenv(key, "true" if default else "false").lower()
        return value == "true"

# Centralized configuration instance
config = Config()
logger.setLevel(logging.DEBUG if os.getenv("LOG_LEVEL", "INFO").upper() == "DEBUG" else logging.INFO)

@dataclass
class SecurePool:
    """Class containing pool data with security features"""
    pubkey: str
    mint: str
    size: int
    first_byte: int
    mint_revoked: bool
    mint_offset: Optional[int]
    lp_locked: bool
    lp_percentage: float
    lp_range: Optional[Tuple[int, int]]
    price: Optional[float] = None
    liquidity_sol: Optional[float] = None
    liquidity_usd: Optional[float] = None
    total_supply: Optional[float] = None
    decimals: Optional[int] = None
    market_cap: Optional[float] = None
    volume_24h: Optional[float] = None
    discovery_time: str = ""
    raw_data: Optional[bytes] = None
    
    @property
    def is_secure(self) -> bool:
        """Check if pool has all required security features"""
        return self.mint_revoked and self.lp_locked and self.lp_percentage >= config.LP_LOCK_THRESHOLD
    
    @property
    def is_tradable(self) -> bool:
        """Check if pool meets criteria for trading"""
        if not self.is_secure:
            return False
            
        # Check if we have enough metrics to evaluate
        has_metrics = self.liquidity_usd is not None
        
        # Apply trading filters only if metrics are available
        if has_metrics:
            return (
                self.liquidity_usd is not None and 
                self.liquidity_usd >= config.MIN_LIQUIDITY_USD and
                (self.market_cap is None or self.market_cap <= config.MAX_MARKET_CAP)
            )
        
        # If we don't have metrics but token is secure, it's tradable
        return True

class SolanaTrader:
    """
    Main class for Solana secure token scanner and trader
    Monitors Raydium pools, analyzes security features, and executes trades
    """
    def __init__(self):
        """Initialize the scanner and trader"""
        self.pools_seen = set()
        self.secure_pools = []
        self.trading_active = config.AUTO_TRADE
        self.shutdown_requested = False
        self.active_trades = 0
        self.max_active_trades = config.MAX_ACTIVE_MEVS
        self.trade_lock = asyncio.Lock()
        self.trade_history = []
        
        # Create Rust trading core if available
        if RUST_CORE_AVAILABLE:
            try:
                self.trading_core = RaydiumTradingCore(
                    rpc_url=config.RPC_URL,
                    keypair_path=config.KEYPAIR_PATH
                )
                # Ensure our public key matches
                pubkey = self.trading_core.get_public_key()
                if pubkey == config.SOLANA_PUBLIC_KEY:
                    logger.info(f"Trading core initialized with wallet: {pubkey}")
                else:
                    logger.warning(f"Public key mismatch! Expected: {config.SOLANA_PUBLIC_KEY}, Got: {pubkey}")
            except Exception as e:
                logger.error(f"Error initializing Rust trading core: {e}")
                # Fallback to Python implementation
                RUST_CORE_AVAILABLE = False
                self.trading_core = RaydiumTradingCore(rpc_url=config.RPC_URL)
        else:
            self.trading_core = RaydiumTradingCore(rpc_url=config.RPC_URL)
            
        # Initialize scanner statistics
        self.stats = {
            "accounts_scanned": 0,
            "pools_found": 0,
            "mint_revoked_found": 0,
            "lp_locked_found": 0,
            "secure_pools_found": 0,
            "trades_attempted": 0,
            "trades_successful": 0,
            "total_profit": 0.0,
            "start_time": time.time(),
            "sol_balance": 0.0,
            "wsol_balance": 0.0,
            "last_update": time.time(),
            "last_trade_time": 0,
            "execution_times": []
        }
        
        # First byte markers for AMM pools
        self.VALID_POOL_MARKERS = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]
        
        # Common pool sizes
        self.COMMON_POOL_SIZES = [752, 800, 920, 940, 1024, 1200, 1600, 2048, 2208]
        
        # Initialize thread pool for CPU-bound operations
        self.thread_pool = ThreadPoolExecutor(max_workers=min(48, os.cpu_count() or 4))
        
        # Initialize CUDA stream if available
        if config.USE_GPU:
            self.cuda_stream = config.cuda_stream
            logger.debug("CUDA stream initialized")
        else:
            self.cuda_stream = None
            
        # Setup security pattern detection
        self.SECURITY_CHECK_OPTIONS = [
            # Different possible offsets where mint authority status might be found
            # (offset, bit_mask) for mint authority
            (72, 0x01),   # Traditional offset
            (80, 0x01),   # Alternative offset 1
            (81, 0x01),   # Alternative offset 2
            (61, 0x01),   # Alternative offset 3
            (40, 0x01),   # Alternative offset 4
        ]

        self.LP_LOCK_CHECK_OPTIONS = [
            # Different possible ranges where LP lock percentage might be found
            # (start, end, normalizer)
            (80, 88, 100),      # Traditional range
            (82, 90, 100),      # Alternative range 1
            (90, 98, 100),      # Alternative range 2
            (64, 72, 100),      # Alternative range 3
            (72, 80, 100),      # Alternative range 4
            (88, 96, 100),      # Alternative range 5
        ]
        
        logger.info("SolanaTrader initialized")
        
    def print_startup_banner(self):
        """Print startup information"""
        logger.info("\n" + "=" * 50)
        logger.info("🚀 RAYDIUM SECURE TOKEN SCANNER AND TRADER")
        logger.info("=" * 50)
        logger.info(f"Start Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        logger.info(f"RPC URL: {config.RPC_URL}")
        logger.info(f"WebSocket URL: {config.HELIUS_WS_URL}")
        logger.info(f"Wallet: {config.SOLANA_PUBLIC_KEY}")
        
        if RUST_CORE_AVAILABLE:
            logger.info("Rust Trading Core: ✅ Available")
        else:
            logger.info("Rust Trading Core: ❌ Not available (using Python fallback)")
            
        if config.USE_GPU:
            gpu_name = torch.cuda.get_device_name(0)
            memory = torch.cuda.get_device_properties(0).total_memory / (1024**3)
            logger.info(f"GPU Acceleration: ✅ {gpu_name} ({memory:.1f} GB)")
        else:
            logger.info("GPU Acceleration: ❌ Not available (using CPU only)")
            
        if config.SCAN_ONLY:
            logger.info("Mode: 📊 Scan Only (no trading)")
        else:
            logger.info("Mode: 🔄 Scan and Trade")
            logger.info(f"Front Run Amount: {config.FRONT_RUN_AMOUNT} SOL")
            logger.info(f"Min Profit Target: {config.MIN_PROFIT} SOL")
            
        logger.info(f"Trading Active: {'✅' if self.trading_active else '❌'}")
        logger.info("Security Detection: Multiple offset patterns")
        logger.info("=" * 50 + "\n")
    
    def check_mint_authority(self, data: bytes) -> Tuple[bool, Optional[int]]:
        """Check multiple possible byte locations for mint authority status"""
        for offset, mask in self.SECURITY_CHECK_OPTIONS:
            if len(data) > offset:
                try:
                    # Check if the bit is set (1 = active, 0 = revoked)
                    bit_set = bool(data[offset] & mask)
                    # We're looking for revoked mint authority (bit not set)
                    if not bit_set:
                        return True, offset
                except:
                    pass
        return False, None
    
    def check_lp_lock(self, data: bytes) -> Tuple[bool, float, Optional[Tuple[int, int]]]:
        """Check multiple possible byte ranges for LP lock percentage"""
        for start, end, normalizer in self.LP_LOCK_CHECK_OPTIONS:
            if len(data) >= end:
                try:
                    # Extract value and normalize to percentage
                    raw_value = int.from_bytes(data[start:end], byteorder="little")
                    percentage = raw_value / normalizer
                    
                    # Check if percentage is realistic (0-100) and high enough
                    if 0 <= percentage <= 100 and percentage >= config.LP_LOCK_THRESHOLD:
                        return True, percentage, (start, end)
                except:
                    pass
        return False, 0, None

    def check_pool_specific_pattern(self, raw_data: bytes, pubkey: str) -> Optional[SecurePool]:
        """Check for 752-byte 0x06 pools with security features at specific offsets"""
        # Skip if not the specific pattern we're looking for
        if len(raw_data) != 752 or raw_data[0] != 0x06:
            return None
        
        # Check for mint authority revoked at offset 72
        mint_revoked = False
        if len(raw_data) > 72:
            try:
                # Check bit - NOT set means revoked
                bit_set = bool(raw_data[72] & 0x01)
                mint_revoked = not bit_set
            except:
                pass
        
        # Check for LP lock at offsets 88-96
        lp_locked = False
        lp_percentage = 0.0
        try:
            if len(raw_data) >= 96:
                # Extract value at exact offset
                raw_value = int.from_bytes(raw_data[88:96], byteorder="little")
                percentage = raw_value / 100
                
                # Check if >= 95%
                if percentage >= config.LP_LOCK_THRESHOLD:
                    lp_locked = True
                    lp_percentage = percentage
        except:
            pass
        
        # Only proceed if both security features found
        if not (mint_revoked and lp_locked):
            return None
        
        # Extract token metrics - focusing on the specific pattern
        metrics = {}
        
        try:
            # Extract decimals at offset 40
            decimals = raw_data[40]
            if 0 <= decimals <= 18:
                metrics["decimals"] = decimals
                
            # Extract total supply (bytes 41-49)
            supply_raw = int.from_bytes(raw_data[41:49], byteorder="little")
            if 0 < supply_raw < 1e18:
                metrics["total_supply"] = supply_raw / (10 ** decimals)
            
            # Extract liquidity from same bytes used for LP lock (88-96)
            liquidity_raw = int.from_bytes(raw_data[88:96], byteorder="little")
            liquidity_sol = liquidity_raw / 1e9
            if 0 <= liquidity_sol < 10000:
                metrics["liquidity_sol"] = liquidity_sol
                metrics["liquidity_usd"] = liquidity_sol * 145  # Current SOL price estimate
            
            # Extract price at specific offset (112-120)
            if len(raw_data) >= 120:
                price_raw = int.from_bytes(raw_data[112:120], byteorder="little")
                price_usd = price_raw / 1e9
                if 0 <= price_usd < 1000:
                    metrics["price"] = price_usd
                    
            # Extract mint at offset that's worked for 752 byte pools
            if len(raw_data) >= 192:
                try:
                    mint_data = raw_data[160:192]
                    mint_address = base58.b58encode(bytes(mint_data[:32])).decode('utf-8')
                    if len(mint_address) >= 32 and not mint_address.startswith('1111'):
                        metrics["mint"] = mint_address
                except:
                    pass
        except Exception as e:
            logger.error(f"Error extracting metrics from specific pattern: {e}")
        
        # Create pool object
        pool = SecurePool(
            pubkey=pubkey,
            mint=metrics.get("mint", "unknown"),
            size=len(raw_data),
            first_byte=raw_data[0],
            mint_revoked=mint_revoked,
            mint_offset=72,  # Known offset
            lp_locked=lp_locked,
            lp_percentage=lp_percentage,
            lp_range=(88, 96),  # Known range
            price=metrics.get("price"),
            liquidity_sol=metrics.get("liquidity_sol"),
            liquidity_usd=metrics.get("liquidity_usd"),
            total_supply=metrics.get("total_supply"),
            decimals=metrics.get("decimals"),
            market_cap=metrics.get("market_cap", None),
            volume_24h=metrics.get("volume_24h", None),
            discovery_time=datetime.now().strftime("%H:%M:%S"),
            raw_data=raw_data[:128]
        )
        
        # Track secure pool found
        self.stats["secure_pools_found"] += 1
        self.secure_pools.append(pool)
        self.print_pool_info(pool)
        
        return pool
        
    def check_pool(self, raw_data: bytes, pubkey: str) -> Optional[SecurePool]:
        """Analyze pool for security features with multiple check approaches"""
        # Quick check for pool-like properties
        if len(raw_data) == 0:
            return None
            
        # Check if size matches common pool sizes
        size_match = len(raw_data) in self.COMMON_POOL_SIZES
        
        # Check if first byte is a common pool marker
        marker_match = len(raw_data) > 0 and raw_data[0] in self.VALID_POOL_MARKERS
        
        # Only proceed if basic checks pass
        if not (size_match or marker_match):
            return None
            
        # Found a potential pool
        self.stats["pools_found"] += 1
        
        # Check for mint authority revocation
        mint_revoked, mint_offset = self.check_mint_authority(raw_data)
        
        # Check for LP lock
        lp_locked, lp_percentage, lp_range = self.check_lp_lock(raw_data)
        
        # Update statistics
        if mint_revoked:
            self.stats["mint_revoked_found"] += 1
            
        if lp_locked:
            self.stats["lp_locked_found"] += 1
        
        # Extract token metrics - generic approach
        metrics = {}
        
        try:
            # Try to extract mint from a few common offsets
            mint_address = None
            mint_offsets = [160, 32, 96, 128]
            
            for offset in mint_offsets:
                if len(raw_data) >= offset + 32:
                    try:
                        mint_data = raw_data[offset:offset+32]
                        candidate = base58.b58encode(bytes(mint_data)).decode('utf-8')
                        if len(candidate) >= 32 and not candidate.startswith('1111'):
                            mint_address = candidate
                            break
                    except:
                        continue
            
            if mint_address:
                metrics["mint"] = mint_address
                
            # Try to extract other metrics (simplified)
            if lp_locked and lp_range:
                start, end = lp_range
                liquidity_raw = int.from_bytes(raw_data[start:end], byteorder="little")
                liquidity_sol = liquidity_raw / 1e9
                if 0 <= liquidity_sol < 10000:
                    metrics["liquidity_sol"] = liquidity_sol
                    metrics["liquidity_usd"] = liquidity_sol * 145  # Current SOL price estimate
        
        except Exception as e:
            logger.debug(f"Error extracting metrics from generic pattern: {e}")
        
        # Create pool object
        pool = SecurePool(
            pubkey=pubkey,
            mint=metrics.get("mint", "unknown"),
            size=len(raw_data),
            first_byte=raw_data[0] if len(raw_data) > 0 else 0,
            mint_revoked=mint_revoked,
            mint_offset=mint_offset,
            lp_locked=lp_locked,
            lp_percentage=lp_percentage,
            lp_range=lp_range,
            price=metrics.get("price"),
            liquidity_sol=metrics.get("liquidity_sol"),
            liquidity_usd=metrics.get("liquidity_usd"),
            total_supply=metrics.get("total_supply"),
            decimals=metrics.get("decimals"),
            market_cap=metrics.get("market_cap", None),
            volume_24h=metrics.get("volume_24h", None),
            discovery_time=datetime.now().strftime("%H:%M:%S"),
            raw_data=raw_data[:128] if len(raw_data) >= 128 else raw_data
        )
        
        # Check if it's secure (both mint revoked and LP locked)
        if pool.is_secure:
            self.stats["secure_pools_found"] += 1
            self.secure_pools.append(pool)
            self.print_pool_info(pool)
        
        return pool

    def print_pool_info(self, pool: SecurePool):
        """Print formatted pool information"""
        if not pool:
            return
            
        logger.info("\n" + "=" * 60)
        logger.info(f"🏦 POOL: {pool.pubkey}")
        logger.info("=" * 60)
        
        # Basic info
        logger.info(f"📏 Size: {pool.size} bytes")
        logger.info(f"🔤 First Byte: 0x{pool.first_byte:02x}")
        
        # Security features
        mint_status = "REVOKED ✅" if pool.mint_revoked else "ACTIVE ❌"
        logger.info(f"🔐 Mint Authority: {mint_status}")
        
        if pool.mint_offset is not None:
            logger.info(f"   Found at offset: {pool.mint_offset}")
            
        lock_status = "LOCKED ✅" if pool.lp_locked else "UNLOCKED ❌"
        logger.info(f"💧 Liquidity Lock: {pool.lp_percentage:.1f}% {lock_status}")
            
        if pool.lp_range is not None:
            logger.info(f"   Found at offsets: {pool.lp_range}")
        
        # Token info
        if pool.mint != "unknown":
            logger.info(f"🪙 Token Mint: {pool.mint}")
        
        # Metrics
        if pool.liquidity_sol is not None:
            logger.info(f"💦 Liquidity: {pool.liquidity_sol:.2f} SOL (${pool.liquidity_usd:.2f})")
            
        if pool.price is not None:
            logger.info(f"💲 Price: ${pool.price:.12f}")
            
        if pool.total_supply is not None:
            logger.info(f"📊 Supply: {pool.total_supply:,.0f}")
            
        if pool.market_cap is not None:
            logger.info(f"💰 Market Cap: ${pool.market_cap:,.2f}")
            
        # Overall security status
        if pool.is_secure:
            logger.info(f"🛡️ SECURITY STATUS: SECURE ✅")
        else:
            logger.info(f"🛡️ SECURITY STATUS: INSECURE ❌")
        
        # Trading status
        if pool.is_tradable:
            logger.info(f"💱 TRADE STATUS: TRADABLE ✅")
        else:
            logger.info(f"💱 TRADE STATUS: NOT TRADABLE ❌")
        
        # Solscan link for reference
        logger.info(f"🔍 Solscan: https://solscan.io/account/{pool.pubkey}")
        logger.info(f"⏰ Discovery Time: {pool.discovery_time}")
        logger.info("=" * 60) please fix the order of the script in the code here to be correct