#!/usr/bin/env python3
"""
Raydium MEV Bot - Optimized Trading System
Enhanced for high-performance sandwich trading on Raydium AMM
"""

import os
import json
import asyncio
import base64
import base58
import time
import signal
import re
import sys
import platform
import traceback
from datetime import datetime
from typing import Dict, List, Optional, Tuple, Any, Set, Union
from concurrent.futures import ThreadPoolExecutor
import websockets
import decimal
from functools import lru_cache

# Import custom modules
from config_loader import config
from solana_logger import logger

# Attempt to import the Rust trading core
try:
    from solana_rust_bot import TradingCore, TradeParams
    RUST_CORE_AVAILABLE = True
    logger.system("✅ Rust trading core loaded successfully")
except ImportError as e:
    # Try to import the specific trading_core_fallback module first
    try:
        from trading_core_fallback import TradingCore, TradeParams
        logger.system("✅ Using dedicated Python fallback for trading core")
    except ImportError:
        # If that fails, use the minimal implementation from config_loader
        from config_loader import TradingCore, TradeParams
        logger.warning("Using minimal config_loader fallback for trading core")
    
    RUST_CORE_AVAILABLE = False
    logger.warning(f"Rust trading core not available, using Python fallback: {e}")


class BlockhashManager:
    """
    Manages blockhash acquisition and caching for optimized transaction submission
    """
    def __init__(self, rpc_client, reuse_interval: float = 0.5):
        """
        Initialize the blockhash manager
        
        Args:
            rpc_client: The RPC client to use for blockhash queries
            reuse_interval: How long (in seconds) to reuse a blockhash before fetching a new one
        """
        self.rpc_client = rpc_client
        self.reuse_interval = reuse_interval
        self.latest_blockhash = None
        self.latest_blockhash_slot = None
        self.last_update_time = 0
        self.lock = asyncio.Lock()
        
    async def get_fresh_blockhash(self) -> str:
        """
        Always fetch a fresh blockhash from the network
        
        Returns:
            str: The latest blockhash
        """
        try:
            response = await self.rpc_client.get_latest_blockhash()
            blockhash = response
            slot = 0
            
            # Update cached values
            self.latest_blockhash = blockhash
            self.latest_blockhash_slot = slot
            self.last_update_time = time.time()
            
            return blockhash
        except Exception as e:
            # If error, try cached value if available
            if self.latest_blockhash:
                logger.warning(f"Failed to get fresh blockhash, using cached one: {e}")
                return self.latest_blockhash
            else:
                raise ValueError(f"Failed to get blockhash: {e}")
    
    async def get_cached_blockhash(self) -> str:
        """
        Get a cached blockhash if it's recent enough, otherwise fetch a new one
        
        Returns:
            str: The blockhash to use
        """
        current_time = time.time()
        
        # If we already have a recent blockhash, use it
        if (self.latest_blockhash and 
            current_time - self.last_update_time < self.reuse_interval):
            return self.latest_blockhash
            
        # Otherwise, get a fresh blockhash
        return await self.get_fresh_blockhash()
    
    async def get_blockhash(self, force_fresh: bool = False) -> str:
        """
        Get a blockhash with proper locking to prevent concurrent RPC requests
        
        Args:
            force_fresh: Whether to force fetching a fresh blockhash
            
        Returns:
            str: The blockhash to use
        """
        async with self.lock:
            if force_fresh:
                return await self.get_fresh_blockhash()
            else:
                return await self.get_cached_blockhash()


class RaydiumMEVBot:
    """Raydium MEV Bot for sandwich trading opportunities with optimized execution"""
    
    def __init__(self):
        """Initialize the Raydium MEV bot"""
        try:
            # Load configuration
            self.setup_environment()
            # Initialize trading components
            self.init_components()
            # Setup performance tracking
            self.setup_metrics()
            # Setup safety features
            self.setup_safety_features()
            # Initialize advanced systems
            self.setup_advanced_systems()
            # Log startup information
            self.log_startup_info()
        except Exception as e:
            logger.error(f"Initialization error: {e}")
            logger.debug(traceback.format_exc())
            raise
        
    def setup_environment(self):
        """Setup environment and load configuration"""
        # Core addresses and constants
        self.WSOL_MINT = "So11111111111111111111111111111111111111112"
        self.LAMPORTS_PER_SOL = 1_000_000_000
        
        # Network settings
        network_config = config.get_network_config()
        self.RPC_URL = network_config.get("rpc_url")
        self.WS_URL = network_config.get("ws_url")
        self.API_KEY = network_config.get("api_key")
        self.WALLET = config.get("SOLANA_PUBLIC_KEY")
        self.WSOL_ACCOUNT = config.get("WSOL_TOKEN_ACCOUNT")
        
        # Raydium program ID
        self.RAYDIUM_PROGRAM_ID = config.get("RAYDIUM_PROGRAM_ID")
        
        # Trading parameters
        trade_config = config.get_trade_config()
        self.POSITION_SIZE = trade_config.get("front_run_amount", 0.4)
        self.MIN_VICTIM_SIZE = trade_config.get("min_victim_size", 5.0)
        self.MAX_VICTIM_SIZE = trade_config.get("max_victim_size", 320.0)
        self.MIN_PROFIT = trade_config.get("min_profit", 0.002)
        self.COMPUTE_LIMIT = trade_config.get("compute_limit", 50000)
        self.PRIORITY_FEE = trade_config.get("priority_fee", 750000)
        
        # Solana price tracking
        self.PRICE_UPDATE_INTERVAL = 60  # seconds
        self.solana_price_usd = 0.0
        self.last_price_update = 0
        
        # Debugging options
        self.debug_mode = os.environ.get("DEBUG_MODE", "0") == "1"

        # Validate required accounts are present
        self._validate_required_accounts()
        
    def _validate_required_accounts(self):
        """Validate all required Raydium accounts are present"""
        required_accounts = [
            "RAYDIUM_PROGRAM_ID",
            "AMM_ID",
            "MARKET_AMM_AUTHORITY",
            "MARKET_AMM_OPEN_ORDERS",
            "MARKET_AMM_TARGET",
            "MARKET_POOL_TOKEN_ACCOUNT",
            "MARKET_POOL_PC_TOKEN_ACCOUNT",
            "SERUM_PROGRAM_ID",
            "MARKET_SERUM_MARKET",
            "MARKET_SERUM_BIDS",
            "MARKET_SERUM_ASKS",
            "MARKET_EVENT_QUEUE",
            "MARKET_BASE_VAULT",
            "MARKET_QUOTE_VAULT",
            "BASE_VAULT_SIGNER",
        ]
        
        missing_accounts = []
        for acc in required_accounts:
            if not config.get(acc):
                missing_accounts.append(acc)
                
        if missing_accounts:
            logger.warning(f"Missing required Raydium accounts: {', '.join(missing_accounts)}")
            logger.warning("These accounts must be added to your .env configuration")
            
    def init_components(self):
        """Initialize core trading components"""
        try:
            # Initialize trading core with RPC URL
            self.trading_core = TradingCore(self.RPC_URL)
            
            # Initialize basic state
            self.active = True
            self.shutdown_requested = False
            
            # Initialize token validation cache
            self.validation_cache = {}
            
            # Initialize blockhash manager with robust error handling
            try:
                self.blockhash_manager = BlockhashManager(
                    self.trading_core,
                    reuse_interval=config.get_float("BLOCKHASH_REUSE_INTERVAL", 0.5)
                )
                logger.debug("BlockhashManager initialized successfully")
            except Exception as e:
                logger.warning(f"Failed to initialize BlockhashManager, using default: {e}")
                # Create a minimal fallback blockhash manager
                from typing import Optional
                
                class FallbackBlockhashManager:
                    """Minimal fallback implementation of BlockhashManager"""
                    def __init__(self):
                        self.latest_blockhash = None
                        self.last_update_time = 0
                    
                    async def get_blockhash(self, force_fresh: bool = False) -> str:
                        """Get a blockhash (simulated)"""
                        import random
                        return f"fallback_blockhash_{int(time.time())}_{random.randint(1000, 9999)}"
                
                self.blockhash_manager = FallbackBlockhashManager()
            
            # Initialize ATA cache
            self.ata_cache = {}
            
            # Initialize caches and state
            self.processed_txs: Set[str] = set()
            self.recent_trades: List[Dict[str, Any]] = []
            
            logger.system("Core components initialized")
        except Exception as e:
            logger.error(f"Component initialization error: {e}")
            logger.debug(traceback.format_exc())
            raise RuntimeError(f"Failed to initialize components: {e}")
            
    def setup_metrics(self):
        """Initialize performance metrics"""
        self.trades_found = 0
        self.trades_executed = 0
        self.successful_trades = 0
        self.total_profit = 0.0
        self.start_time = datetime.now()
        self.balances = {"SOL": 0.0, "WSOL": 0.0}
        self.execution_times = []
        self.profit_hist = []
        
        # Enhanced trade metrics
        self.trade_metrics = {
            "avg_front_run_time": 0,
            "avg_back_run_time": 0,
            "avg_total_time": 0,
            "front_run_times": [],
            "back_run_times": [],
            "total_times": [],
            "success_rate": 0,
        }
        
    def setup_safety_features(self):
        """Setup safety features to prevent trading errors"""
        # Concurrency controls
        self.trade_lock = asyncio.Lock()
        self.active_trades = 0
        self.MAX_ACTIVE_TRADES = config.get_int("MAX_ACTIVE_MEVS", 1)
        
        # Minimum balance thresholds
        balance_config = config.get_balance_config()
        self.min_sol_balance = balance_config.get("min_sol_balance", 0.03)
        self.min_wsol_balance = balance_config.get("min_wsol_balance", 0.02)
        
        # Trading state
        self.is_trading_paused = False
        self.last_trade_time = 0
        
        # Circuit breaker
        self.consecutive_failures = 0
        self.MAX_CONSECUTIVE_FAILURES = 5
        
    def setup_advanced_systems(self):
        """Setup advanced monitoring and optimization systems"""
        # Market sentiment tracking
        self.market_volatility = 0.5  # Medium volatility (0-1 scale)
        self.network_congestion = 0.3  # Medium congestion (0-1 scale)
        
        # Rate limiting for API calls
        self.rate_limit_tokens = 100
        self.rate_limit_last_refill = time.time()
        self.rate_limit_lock = asyncio.Lock()
        
        # Dynamic performance tuning
        self.adaptive_params = {
            "position_size_multiplier": 1.0,
            "min_profit_multiplier": 1.0,
            "priority_fee_multiplier": 1.0,
        }
        
        # Track blockchain conditions
        self.block_times = []
        self.avg_block_time = 0.43  # Start with Solana's average of ~430ms
        
        # Initialize token validation and liquidity caches
        self.token_metadata_cache = {}
        self.liquidity_cache = {}
        
        # Health monitoring
        self.health_status = "good"
        self.last_error_time = 0
        self.error_count = 0
        
        # Safe tokens set
        self.safe_tokens = set([self.WSOL_MINT])
        
        # Configure multithreading
        cpu_count = os.cpu_count()
        thread_count = min(48, max(4, cpu_count - 2))  # Leave a couple cores for system
        self.thread_pool = ThreadPoolExecutor(max_workers=thread_count)
        
        # Configure CUDA if available
        self.cuda_enabled = False
        self.cuda_stream = None
        
        try:
            import torch
            if torch.cuda.is_available():
                # Empty cache
                torch.cuda.empty_cache()
                
                # Set precision (with error handling)
                try:
                    torch.set_float32_matmul_precision("high")
                except AttributeError:
                    logger.warning("PyTorch version does not support set_float32_matmul_precision")
                
                # Enable TF32 if supported
                if torch.cuda.is_available() and config.get_bool("ALLOW_TF32", True):
                    try:
                        torch.backends.cuda.matmul.allow_tf32 = True
                        torch.backends.cudnn.allow_tf32 = True
                    except AttributeError:
                        logger.warning("PyTorch version does not support TF32")
                
                # Set CUDA benchmark mode for optimal performance
                if config.get_bool("CUDA_BENCHMARK", True):
                    torch.backends.cudnn.benchmark = True
                    
                # Create CUDA stream for prioritized computation
                try:
                    priority = config.get_int("STREAM_PRIORITY", -3)
                    self.cuda_stream = torch.cuda.Stream(priority=priority)
                except (AttributeError, TypeError):
                    self.cuda_stream = torch.cuda.Stream()
                    logger.warning("Using default CUDA stream (priority not supported)")
                
                # Log GPU info
                gpu_name = torch.cuda.get_device_name(0)
                gpu_mem = torch.cuda.get_device_properties(0).total_memory / (1024**3)
                
                logger.system(f"CUDA initialized: {gpu_name} ({gpu_mem:.1f}GB)")
                self.cuda_enabled = True
            else:
                logger.warning("CUDA not available - using CPU only")
        except ImportError:
            logger.warning("PyTorch not installed - GPU acceleration disabled")
        except Exception as e:
            logger.warning(f"Failed to initialize CUDA: {e}")
            logger.warning("Falling back to CPU calculations")
            
        logger.system(f"Compute resources initialized with {thread_count} threads")
        
    def log_startup_info(self):
        """Log startup information and configuration"""
        logger.system(f"""
🚀 Raydium MEV Bot Starting
============================
Position Size: {self.POSITION_SIZE} SOL
Trade Range: {self.MIN_VICTIM_SIZE}-{self.MAX_VICTIM_SIZE} SOL
Min Profit: {self.MIN_PROFIT} SOL
CUDA Enabled: {getattr(self, 'cuda_enabled', False)}
Rust Core: {RUST_CORE_AVAILABLE}
============================
Wallet: {self.WALLET}
WSOL Account: {self.WSOL_ACCOUNT}
============================
""")
        
    def extract_ray_log_data(self, log_line: str) -> Optional[bytes]:
        """Extract raw data from ray_log message with improved parsing"""
        try:
            if "ray_log:" not in log_line:
                return None
                
            # Extract the base64 encoded part after ray_log:
            match = re.search(r'ray_log:\s*([A-Za-z0-9+/=]+)', log_line)
            if not match:
                logger.debug(f"No base64 data found in ray_log line: {log_line[:50]}...")
                return None
                
            raw_base64 = match.group(1).strip()
            
            # Decode the base64 data
            raw_data = base64.b64decode(raw_base64)
            
            if len(raw_data) < 9:
                logger.debug(f"Decoded ray_log data too short: {len(raw_data)} bytes")
                return None
                
            return raw_data
                
        except Exception as e:
            logger.error(f"Error extracting ray_log data: {e}")
            if self.debug_mode:
                logger.debug(f"Problematic log line: {log_line[:100]}...")
            return None
    
    def decode_ray_log(self, log_line: str) -> Optional[bytes]:
        """
        Decode Raydium ray_log data from log line with robust error handling
        Returns raw bytes for further processing
        """
        try:
            raw_data = self.extract_ray_log_data(log_line)
            if not raw_data:
                return None
                
            return raw_data
                
        except Exception as e:
            logger.error(f"Ray log decode error: {e}")
            if self.debug_mode:
                logger.debug(traceback.format_exc())
            return None
    
    async def validate_token(self, token_mint: str) -> bool:
        """
        Validate if a token is safe to trade with enhanced risk scoring
        Returns True if token is valid, False otherwise
        """
        # Fast cache check
        if token_mint in self.validation_cache:
            return self.validation_cache[token_mint]
            
        # Always allow WSOL
        if token_mint == self.WSOL_MINT:
            self.validation_cache[token_mint] = True
            return True
            
        try:
            # Check if token is in our safe tokens set
            if token_mint in self.safe_tokens:
                self.validation_cache[token_mint] = True
                return True
                
            # Risky token patterns - avoiding tax tokens, honeypots, etc.
            risk_patterns = [
                # Common patterns in malicious token names
                "tax", "honeypot", "scam", "ponzi", "squid", "elon", "moon", 
                "safe", "gem", "inu", "shib", "doge"
            ]
            
            # For this simplified implementation, we'll always validate tokens
            # In a production environment, you would add more checks here
            
            # If all checks pass, cache the result and return True
            self.validation_cache[token_mint] = True
            return True
            
        except Exception as e:
            logger.error(f"Token validation error for {token_mint}: {e}")
            self.validation_cache[token_mint] = False
            return False
            
    async def parse_trade_data(self, raw_data: bytes, tx_id: str = None) -> Optional[dict]:
        """
        Parse Raydium trade data from raw bytes with enhanced validation and support for different formats
        
        Args:
            raw_data: Raw bytes from ray_log
            tx_id: Optional transaction ID for tracking
            
        Returns:
            Dictionary with trade details or None if invalid
        """
        try:
            # Skip if already processed
            if tx_id and tx_id in self.processed_txs:
                return None
            
            # Validate minimum data length for instruction type and amount
            if len(raw_data) < 9:
                logger.debug(f"Raw data too short for basic parsing: {len(raw_data)} bytes")
                return None
            
            # Extract instruction type and amount
            instruction_type = raw_data[0]
            amount = int.from_bytes(raw_data[1:9], "little") / self.LAMPORTS_PER_SOL
            
            # Validate instruction type (Raydium swap instructions)
            valid_instructions = [0x03, 0x04, 0x09, 0x0a]  # Added 0x04 as valid
            if instruction_type not in valid_instructions:
                logger.debug(f"Invalid instruction type: 0x{instruction_type:02x}")
                return None
            
            # Validate amount is within our trading range
            if not (self.MIN_VICTIM_SIZE <= amount <= self.MAX_VICTIM_SIZE):
                logger.debug(f"Amount outside trading range: {amount:.4f} SOL")
                return None
            
            # Extract token mints with proper validation and fallbacks
            token_a_mint = None
            token_b_mint = None
            
            # Try to use Rust decoder first (usually faster and more reliable)
            try:
                if RUST_CORE_AVAILABLE:
                    try:
                        token_a_mint, token_b_mint, amount_lamports = self.trading_core.decode_ray_log_py(raw_data)
                        # Double-check amount with our Python parsing
                        if abs(amount_lamports/self.LAMPORTS_PER_SOL - amount) > 0.000001:
                            logger.debug(f"Amount mismatch: Python={amount}, Rust={amount_lamports/self.LAMPORTS_PER_SOL}")
                    except Exception as e:
                        logger.debug(f"Rust decoder failed, using Python fallback: {e}")
                        token_a_mint = None
                        token_b_mint = None
            except Exception as e:
                # Fall back to our Python implementation
                logger.debug(f"Rust decoder access error: {e}")
            
            # If token mints are still not extracted, try Python fallback
            if not token_a_mint:
                # Try shorter log format first (newer format seen in testraylog.py)
                if len(raw_data) >= 57:
                    try:
                        # Try multiple potential offsets for token_a
                        for offset in [32, 24, 16, 9]:
                            if offset + 32 <= len(raw_data):
                                token_a_bytes = raw_data[offset:offset + 32]
                                candidate = base58.b58encode(token_a_bytes).decode()
                                
                                # Check if it looks like a valid Solana address
                                if len(candidate) >= 32 and all(c in '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' for c in candidate):
                                    token_a_mint = candidate
                                    logger.debug(f"Found Token A at offset {offset}: {token_a_mint}")
                                    break
                    except Exception as e:
                        logger.debug(f"Short format token extraction error: {e}")
            
                # Handle standard format (bytes 40-72 for Token A)
                if not token_a_mint and len(raw_data) >= 72:
                    try:
                        token_a_bytes = raw_data[40:72]
                        token_a_mint = base58.b58encode(token_a_bytes).decode()
                        
                        # Basic validation of base58 encoded string
                        if len(token_a_mint) < 32 or not all(c in '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' for c in token_a_mint):
                            logger.debug(f"Invalid Token A format: {token_a_mint}")
                            
                            # Try alternative position (sometimes tokens are at different offsets)
                            if len(raw_data) >= 64:
                                alt_token_bytes = raw_data[32:64]
                                token_a_mint = base58.b58encode(alt_token_bytes).decode()
                                logger.debug(f"Using alternative position for Token A: {token_a_mint}")
                    except Exception as e:
                        logger.debug(f"Token A decode error: {e}")
            
                # If we still don't have a valid token_a_mint, try one last approach
                if not token_a_mint and len(raw_data) >= 41:
                    try:
                        # For short formats, try extracting from any 32-byte segment
                        for i in range(9, len(raw_data) - 32, 1):  # More granular search
                            candidate_bytes = raw_data[i:i + 32]
                            candidate = base58.b58encode(candidate_bytes).decode()
                            
                            # Check if it looks like a valid Solana address
                            if len(candidate) >= 32 and all(c in '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' for c in candidate):
                                token_a_mint = candidate
                                logger.debug(f"Found potential Token A at offset {i}: {token_a_mint}")
                                break
                    except Exception as e:
                        logger.debug(f"Alternative Token A extraction error: {e}")
            
                # If still no token_a_mint, we can't process this trade
                if not token_a_mint:
                    logger.debug(f"Failed to extract Token A from data of length {len(raw_data)}")
                    return None
            
            # For Token B, default to WSOL if not set
            if not token_b_mint:
                if len(raw_data) >= 104:
                    try:
                        token_b_bytes = raw_data[72:104]
                        token_b_mint = base58.b58encode(token_b_bytes).decode()
                        
                        # Validate token_b_mint format
                        if len(token_b_mint) < 32 or not all(c in '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' for c in token_b_mint):
                            logger.debug(f"Invalid Token B format, using WSOL")
                            token_b_mint = self.WSOL_MINT
                    except Exception:
                        token_b_mint = self.WSOL_MINT
                else:
                    # Default to WSOL if token B data not found
                    token_b_mint = self.WSOL_MINT
            
            # Validate tokens
            is_valid = await self.validate_token(token_a_mint)
            if not is_valid:
                logger.debug(f"Invalid token A: {token_a_mint}")
                return None
                
            # Calculate profit potential
            net_profit = self.calculate_profit(amount, token_a_mint, token_b_mint)
            
            # Skip if not profitable
            if net_profit <= self.MIN_PROFIT:
                logger.debug(f"Not profitable: {net_profit:.6f} SOL (min={self.MIN_PROFIT})")
                return None
            
            # Mark as processed if tx_id provided
            if tx_id:
                self.processed_txs.add(tx_id)
                
                # Prevent the processed_txs set from growing too large
                if len(self.processed_txs) > 10000:
                    # Keep only the most recent 5000 transactions
                    self.processed_txs = set(list(self.processed_txs)[-5000:])
            
            # Return trade opportunity
            trade_data = {
                "victim_data": raw_data,
                "amount": amount,
                "token_a_mint": token_a_mint,
                "token_b_mint": token_b_mint,
                "net_profit": net_profit,
                "instruction_type": instruction_type,
                "transaction_id": tx_id or "unknown",
                "timestamp": time.time(),
                "profit_usd": net_profit * self.solana_price_usd
            }
            
            # Add risk score (could be expanded in future)
            risk_score = 1.0  # Default low risk
            if amount > 200:
                risk_score = 2.0  # Medium risk for large trades
            trade_data["risk_score"] = risk_score
            
            return trade_data
            
        except Exception as e:
            logger.error(f"Trade data parse error: {e}")
            if self.debug_mode:
                logger.debug(traceback.format_exc())
            return None

    async def check_initial_balances(self):
        """Check initial SOL and WSOL balances"""
        try:
            # Get SOL balance
            sol_balance = await self.trading_core.get_sol_balance()
            if sol_balance is not None:
                self.balances["SOL"] = sol_balance
            else:
                logger.error("Failed to get SOL balance")
            
            # Get WSOL balance
            wsol_balance = await self.trading_core.get_wsol_balance()
            if wsol_balance is not None:
                self.balances["WSOL"] = wsol_balance
            else:
                logger.error("Failed to get WSOL balance")
            
            # Log initial balances
            logger.info(f"Initial balances - SOL: {self.balances['SOL']:.4f}, WSOL: {self.balances['WSOL']:.4f}")
            
            # Check if balances are sufficient
            if self.balances["SOL"] < self.min_sol_balance:
                logger.warning(f"Low SOL balance: {self.balances['SOL']:.4f}")
                
            if self.balances["WSOL"] < self.POSITION_SIZE:
                logger.warning(f"WSOL balance below position size: {self.balances['WSOL']:.4f}")
            
            return self.balances
        except Exception as e:
            logger.error(f"Balance check error: {e}")
            logger.debug(traceback.format_exc())
            return self.balances
    
    def calculate_profit(self, amount: float, token_a_mint: str, token_b_mint: str) -> float:
        """Calculate estimated profit for a trade opportunity"""
        # CPU calculation function (used as fallback if GPU fails)
        def cpu_calculation() -> float:
            if amount >= 40:
                profit_rate = 0.025
            elif amount >= 20:
                profit_rate = 0.020
            elif amount >= 5:
                profit_rate = 0.015
            else:
                return 0.0
                
            # Calculate costs
            base_cost = 0.002  # Base transaction cost (gas + priority)
            impact = 0.001 * amount  # Price impact (0.1%)
            
            # Calculate net profit
            gross_profit = amount * profit_rate
            net_profit = gross_profit - (impact * 2) - base_cost
            
            return max(0, net_profit)
        
        # If CUDA is not enabled or not available, use CPU calculation
        if not getattr(self, 'cuda_enabled', False) or not getattr(self, 'cuda_stream', None):
            return cpu_calculation()
            
        # Try GPU calculation with fallback to CPU
        try:
            # GPU accelerated calculation
            import torch
            with torch.cuda.stream(self.cuda_stream):
                # Convert to tensor
                amount_tensor = torch.tensor(amount, device="cuda")
                
                # Calculate profit rate based on amount
                if amount >= 40:
                    profit_rate = 0.025
                elif amount >= 20:
                    profit_rate = 0.020
                elif amount >= 5:
                    profit_rate = 0.015
                else:
                    return 0.0
                    
                profit_rate_tensor = torch.tensor(profit_rate, device="cuda")
                
                # Calculate price impact and costs
                impact = 0.001 * amount_tensor  # 0.1% impact
                gas_cost = torch.tensor(0.000125, device="cuda")
                priority_fee = torch.tensor(0.00185, device="cuda")
                
                # Calculate gross profit and costs
                gross_profit = amount_tensor * profit_rate_tensor
                total_cost = (impact * 2) + gas_cost + priority_fee
                
                # Calculate net profit
                net_profit = gross_profit - total_cost
                
                # Return result, ensuring it's at least the minimum profit
                if float(net_profit) <= self.MIN_PROFIT:
                    return 0.0
                    
                return float(net_profit)
                
        except Exception as e:
            # If GPU calculation fails, log the error and fall back to CPU
            logger.warning(f"GPU profit calculation failed, using CPU fallback: {e}")
            
            # Disable CUDA for future calculations to avoid repeated errors
            self.cuda_enabled = False
            
            # Use CPU calculation
            return cpu_calculation()
    
    async def ensure_atas_for_trade(self, token_a_mint: str, token_b_mint: str) -> Tuple[bool, Optional[str]]:
        """
        Efficiently ensure that associated token accounts (ATAs) exist for a trade.
        This will check and create ATAs for both tokens if they don't exist.
        
        Args:
            token_a_mint: The mint address of token A
            token_b_mint: The mint address of token B (usually WSOL)
            
        Returns:
            Tuple[bool, Optional[str]]: (Success status, Error message if any)
        """
        try:
            logger.debug(f"Ensuring ATAs exist for token A: {token_a_mint} and token B: {token_b_mint}")
            
            # Use Rust core to ensure ATAs exist
            # The Rust implementation has efficient caching built in
            try:
                # Check if ATAs already exist in cache
                if token_a_mint in self.ata_cache and token_b_mint in self.ata_cache:
                    return True, None
                
                # Ensure token A ATA exists
                if hasattr(self.trading_core, "ensure_ata_exists"):
                    token_a_ata = await self.trading_core.ensure_ata_exists(token_a_mint)
                    # Cache the result
                    self.ata_cache[token_a_mint] = token_a_ata
                    logger.debug(f"Token A ATA: {token_a_ata}")
                    
                    # Ensure token B ATA exists (usually WSOL)
                    token_b_ata = await self.trading_core.ensure_ata_exists(token_b_mint)
                    # Cache the result
                    self.ata_cache[token_b_mint] = token_b_ata
                    logger.debug(f"Token B ATA: {token_b_ata}")
                else:
                    # Handle case where ATA creation is handled by the trading core automatically
                    return True, None
                
                return True, None
                
            except Exception as e:
                error_msg = f"Failed to create ATA: {str(e)}"
                logger.error(error_msg)
                return False, error_msg
                
        except Exception as e:
            error_msg = f"ATA preparation error: {str(e)}"
            logger.error(error_msg)
            return False, error_msg
    
    async def execute_trade_rust_core(self, trade_data: Dict[str, Any], is_buy: bool, 
                                       amount: int, blockhash: str = None) -> Optional[str]:
        """
        Execute a trade using the Rust core with explicit blockhash control
    
        Args:
            trade_data: Trade data dictionary
            is_buy: Whether this is a buy (True) or sell (False) trade
            amount: Amount in lamports (0 for sell_all)
            blockhash: Optional blockhash to use (if None, Rust core will fetch one)
        
        Returns:
            Optional[str]: Transaction signature if successful, None otherwise
        """
        try:
            # Prepare account list
            account_list = [
                config.get("RAYDIUM_PROGRAM_ID"),
                config.get("AMM_ID"),
                config.get("MARKET_AMM_AUTHORITY"),
                config.get("MARKET_AMM_OPEN_ORDERS"),
                config.get("MARKET_AMM_TARGET"),
                config.get("MARKET_POOL_TOKEN_ACCOUNT"),
                config.get("MARKET_POOL_PC_TOKEN_ACCOUNT"),
                config.get("SERUM_PROGRAM_ID"),
                config.get("MARKET_SERUM_MARKET"),
                config.get("MARKET_SERUM_BIDS"),
                config.get("MARKET_SERUM_ASKS"),
                config.get("MARKET_EVENT_QUEUE"),
                config.get("MARKET_BASE_VAULT"),
                config.get("MARKET_QUOTE_VAULT"),
                config.get("BASE_VAULT_SIGNER")
            ]
            
            # Create trade parameters - FIX: Always provide amount_in with a minimum value of 1 if selling all
            if not is_buy and amount == 0:
                # For sell_all, provide a minimal amount_in to satisfy the parameter check
                # but set sell_all to True to indicate we want to sell the entire balance
                amount_in = 1  # Minimum value that satisfies the check
            else:
                amount_in = amount if is_buy or amount > 0 else None
            
            # Create trade parameters
            params = TradeParams(
                accounts=account_list,
                amount_in=amount_in,  # Modified to always provide a value for amount_in
                is_buy=is_buy,
                token_a_mint=trade_data["token_a_mint"],
                token_b_mint=trade_data["token_b_mint"],
                victim_data=None,
                sell_all=not is_buy or amount == 0
            )
            
            # If blockhash is provided and the class supports it, set it
            try:
                if blockhash:
                    params.blockhash = blockhash
            except (AttributeError, TypeError):
                logger.debug("TradeParams does not support blockhash attribute, ignoring")
            
            # Execute the trade
            max_retries = min(3, config.get_int("MAX_RETRIES", 3))
            
            for attempt in range(max_retries):
                try:
                    tx_id = await self.trading_core.execute_trade_py(params)
                    if tx_id:
                        return tx_id
                except Exception as e:
                    retry_msg = f", retrying ({attempt+1}/{max_retries})" if attempt < max_retries - 1 else ""
                    logger.warning(f"Trade execution attempt failed: {e}{retry_msg}")
                    
                    if attempt < max_retries - 1:
                        # Short delay before retry - using exponential backoff
                        await asyncio.sleep(0.05 * (2 ** attempt))
        
            return None
            
        except Exception as e:
            logger.error(f"Trade execution error: {str(e)}")
            if self.debug_mode:
                logger.debug(traceback.format_exc())
            return None
    
    async def execute_sandwich_trade(self, trade_data: Dict[str, Any]) -> bool:
        """
        Execute a sandwich trade with optimized blockhash handling for atomic entry and exit
        
        Args:
            trade_data: Dictionary containing details about the trade opportunity
            
        Returns:
            bool: True if trade was successful, False otherwise
        """
        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:
            if self.is_trading_paused:
                logger.warning("Trading is paused, skipping opportunity")
                return False
                
            # Mark that we're starting a trade
            self.active_trades += 1
            tx_id = trade_data["transaction_id"]
            start_time = time.time()
            
            # Initialize tracking variables
            front_tx_id = None
            back_tx_id = None
            trade_success = False
            
            try:
                # Quick validation of balances
                if self.balances["WSOL"] < self.POSITION_SIZE:
                    logger.warning(f"Insufficient WSOL balance for trade: {self.balances['WSOL']:.4f} SOL")
                    return False
                    
                if self.balances["SOL"] < self.min_sol_balance:
                    logger.warning(f"Insufficient SOL balance for gas: {self.balances['SOL']:.4f} SOL")
                    return False
                
                # Ensure ATAs exist for both tokens
                atas_ready, error = await self.ensure_atas_for_trade(
                    trade_data["token_a_mint"],
                    trade_data["token_b_mint"]
                )
                
                if not atas_ready:
                    logger.error(f"Cannot execute trade - ATA preparation failed: {error}")
                    return False
                
                # ====== BLOCKHASH OPTIMIZATION ======
                # Get a cached blockhash for front-run (faster entry)
                # We can reuse a recent blockhash to speed up the front-run
                cached_blockhash = await self.blockhash_manager.get_blockhash(force_fresh=False)
                
                # ====== FRONT-RUN TRANSACTION ======
                # Execute front-run with cached blockhash
                front_run_start = time.time()
                
                try:
                    # Use the cached blockhash for quicker transaction submission
                    front_tx_id = await self.execute_trade_rust_core(
                        trade_data,
                        is_buy=True,
                        amount=int(self.POSITION_SIZE * self.LAMPORTS_PER_SOL),
                        blockhash=cached_blockhash
                    )
                    
                    if not front_tx_id:
                        logger.error("Front-run transaction failed")
                        return False
                        
                    front_run_time = time.time() - front_run_start
                    self.trade_metrics["front_run_times"].append(front_run_time)
                    logger.info(f"Front-run transaction sent: {front_tx_id} in {front_run_time*1000:.1f}ms")
                    self.trades_executed += 1
                    
                except Exception as e:
                    logger.error(f"Front-run execution failed: {e}")
                    return False
                
                # ====== WAIT FOR VICTIM TRANSACTION ======
                victim_wait_time = config.get_float("VICTIM_WAIT_TIME", 0.05)
                await asyncio.sleep(victim_wait_time)
                
                # ====== BACK-RUN WITH FRESH BLOCKHASH ======
                # Always use a fresh blockhash for back-run to ensure it's picked up quickly
                fresh_blockhash = await self.blockhash_manager.get_blockhash(force_fresh=True)
                
                back_run_start = time.time()
                
                try:
                    # Use the fresh blockhash for reliable transaction submission
                    back_tx_id = await self.execute_trade_rust_core(
                        trade_data,
                        is_buy=False,  # Sell
                        amount=0,  # Sell all
                        blockhash=fresh_blockhash
                    )
                    
                    if not back_tx_id:
                        logger.error("Back-run transaction failed")
                        return False
                        
                    back_run_time = time.time() - back_run_start
                    self.trade_metrics["back_run_times"].append(back_run_time)
                    logger.info(f"Back-run transaction sent: {back_tx_id} in {back_run_time*1000:.1f}ms")
                    
                except Exception as e:
                    logger.error(f"Back-run execution failed: {e}")
                    return False
                
                # ====== RECORD SUCCESS ======
                # Record success
                self.successful_trades += 1
                self.total_profit += trade_data["net_profit"]
                trade_success = True
                
                # Reset failure counter on success
                self.consecutive_failures = 0
                
                # Record execution time
                execution_time = time.time() - start_time
                self.execution_times.append(execution_time)
                self.trade_metrics["total_times"].append(execution_time)
                
                # Update average metrics
                self.trade_metrics["avg_front_run_time"] = sum(self.trade_metrics["front_run_times"][-20:]) / max(1, len(self.trade_metrics["front_run_times"][-20:]))
                self.trade_metrics["avg_back_run_time"] = sum(self.trade_metrics["back_run_times"][-20:]) / max(1, len(self.trade_metrics["back_run_times"][-20:]))
                self.trade_metrics["avg_total_time"] = sum(self.trade_metrics["total_times"][-20:]) / max(1, len(self.trade_metrics["total_times"][-20:]))
                self.trade_metrics["success_rate"] = (self.successful_trades / max(1, self.trades_executed)) * 100
                
                # Add more detailed metrics to the log
                trade_metrics = {
                    "front_tx": front_tx_id,
                    "back_tx": back_tx_id,
                    "victim_tx": tx_id,
                    "token": trade_data["token_a_mint"],
                    "amount": trade_data["amount"],
                    "profit": trade_data["net_profit"],
                    "front_run_time": front_run_time,
                    "back_run_time": back_run_time,
                    "total_time": execution_time,
                    "profit_usd": trade_data["net_profit"] * self.solana_price_usd
                }
                
                logger.trade(f"Sandwich complete in {execution_time*1000:.1f}ms - Profit: {trade_data['net_profit']:.6f} SOL (${trade_data['net_profit'] * self.solana_price_usd:.2f})", trade_metrics)
                
                # Add to recent trades list
                trade_summary = {
                    "time": datetime.now().strftime("%H:%M:%S"),
                    "token": trade_data["token_a_mint"][-5:],  # Last 5 chars of token address
                    "amount": trade_data["amount"],
                    "profit": trade_data["net_profit"],
                    "profit_usd": trade_data["net_profit"] * self.solana_price_usd,
                    "execution_time": execution_time,
                    "front_tx": front_tx_id[:8] + "...",  # First 8 chars of tx ID
                    "back_tx": back_tx_id[:8] + "..."     # First 8 chars of tx ID
                }
                
                self.recent_trades.append(trade_summary)
                if len(self.recent_trades) > 20:
                    self.recent_trades = self.recent_trades[-20:]
                
                # Add to profit history for analysis
                self.profit_hist.append(trade_data["net_profit"])
                
                return True
                
            except Exception as e:
                # Record error details
                error_details = {
                    "error_type": type(e).__name__, 
                    "tx_id": tx_id,
                    "token": trade_data.get("token_a_mint", "unknown")
                }
                
                # Increment failure counter
                self.consecutive_failures += 1
                
                # Check if we need to trigger circuit breaker
                if self.consecutive_failures >= self.MAX_CONSECUTIVE_FAILURES:
                    logger.warning(f"Circuit breaker triggered after {self.consecutive_failures} consecutive failures")
                    self.is_trading_paused = True
                    # Auto-resume after delay
                    asyncio.create_task(self.auto_resume_trading(300))  # 5 minutes
                
                # Handle partial trade errors (front-run succeeded but back-run failed)
                if front_tx_id and not back_tx_id:
                    error_details["front_tx"] = front_tx_id
                    logger.error(f"Partial trade error (front-run sent but back-run failed): {e}", error_details)
                else:
                    logger.error(f"Trade execution error: {e}", error_details)
                    
                if self.debug_mode:
                    logger.debug(traceback.format_exc())
                    
                return trade_success
                
            finally:
                # Always decrement active trades counter
                self.active_trades -= 1
                self.last_trade_time = time.time()
    
    async def monitor_balances(self):
        """Monitor SOL and WSOL balances"""
        retry_delay = 1
        max_retry_delay = 30
        
        while self.active:
            try:
                async with websockets.connect(
                    self.WS_URL,
                    extra_headers={
                        "api-key": self.API_KEY,
                        "User-Agent": "solana-client/1.18.23",
                    },
                    ping_interval=20,
                    ping_timeout=30,
                    close_timeout=60
                ) as ws:
                    # Reset retry delay on successful connection
                    retry_delay = 1
                    
                    # Define accounts to monitor
                    accounts = {
                        "SOL": self.WALLET,
                        "WSOL": self.WSOL_ACCOUNT
                    }
                    
                    # Subscribe to account changes
                    for acc_type, address in accounts.items():
                        await ws.send(json.dumps({
                            "jsonrpc": "2.0",
                            "id": 1 if acc_type == "SOL" else 2,
                            "method": "accountSubscribe",
                            "params": [
                                address,
                                {"encoding": "base64", "commitment": "confirmed"}
                            ]
                        }))
                        
                    # Track subscriptions
                    subs = {}
                    
                    # Process messages
                    while self.active:
                        try:
                            msg = await ws.recv()
                            data = json.loads(msg)
                            
                            # Handle subscription confirmation
                            if "result" in data:
                                sub_id = data["result"]
                                acc_type = "SOL" if data["id"] == 1 else "WSOL"
                                subs[sub_id] = acc_type
                                logger.debug(f"{acc_type} subscription active (ID: {sub_id})")
                                continue
                            
                            # Handle account notification
                            if "method" in data and data["method"] == "accountNotification":
                                params = data["params"]
                                sub_id = params["subscription"]
                                
                                if sub_id in subs:
                                    acc_type = subs[sub_id]
                                    value = params["result"]["value"]
                                    
                                    # Update SOL balance
                                    if acc_type == "SOL":
                                        lamports = int(value["lamports"])
                                        balance = lamports / self.LAMPORTS_PER_SOL
                                        
                                        # Skip if no significant change
                                        if abs(balance - self.balances[acc_type]) < 0.00001:
                                            continue
                                            
                                        # Record change
                                        old_balance = self.balances[acc_type]
                                        self.balances[acc_type] = balance
                                        change = balance - old_balance
                                        
                                        logger.info(f"SOL Balance: {balance:.4f} ({'+' if change >= 0 else ''}{change:.4f})")
                                    
                                    # Update WSOL balance  
                                    elif acc_type == "WSOL":
                                        try:
                                            if "data" in value:
                                                token_data = base64.b64decode(value["data"][0])
                                                if len(token_data) >= 72:
                                                    amount = int.from_bytes(token_data[64:72], "little")
                                                    balance = amount / self.LAMPORTS_PER_SOL
                                                    
                                                    # Skip if no significant change
                                                    if abs(balance - self.balances[acc_type]) < 0.00001:
                                                        continue
                                                        
                                                    # Record change
                                                    old_balance = self.balances[acc_type]
                                                    self.balances[acc_type] = balance
                                                    change = balance - old_balance
                                                    
                                                    logger.info(f"WSOL Balance: {balance:.4f} ({'+' if change >= 0 else ''}{change:.4f})")
                                        except Exception as e:
                                            logger.warning(f"WSOL data parse error: {e}")
                                            continue
                                    
                                    # Check for low balances
                                    if self.balances["SOL"] < self.min_sol_balance:
                                        logger.warning(f"Low SOL balance: {self.balances['SOL']:.4f}")
                                        
                                    if self.balances["WSOL"] < self.POSITION_SIZE:
                                        if not self.is_trading_paused:
                                            logger.warning(f"WSOL balance below position size, pausing trading: {self.balances['WSOL']:.4f}")
                                            self.is_trading_paused = True
                                    elif self.is_trading_paused and self.balances["WSOL"] >= self.POSITION_SIZE:
                                        logger.info(f"WSOL balance restored, resuming trading: {self.balances['WSOL']:.4f}")
                                        self.is_trading_paused = False
                        
                        except json.JSONDecodeError:
                            logger.warning("Invalid JSON received in balance WebSocket")
                            continue
                        except Exception as e:
                            if not self.shutdown_requested:
                                logger.error(f"Balance monitoring error: {e}")
                            break
                    
            except websockets.ConnectionClosed:
                logger.warning(f"Balance WebSocket disconnected, reconnecting in {retry_delay}s...")
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)
            except Exception as e:
                if not self.shutdown_requested:
                    logger.error(f"Balance monitor error: {e}")
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)
    
    async def update_solana_price(self):
        """Update Solana price periodically"""
        retry_delay = 5
        max_retry_delay = 60
        
        while self.active:
            try:
                # If we already have a price, only update after the interval passes
                current_time = time.time()
                if self.solana_price_usd > 0 and current_time - self.last_price_update < self.PRICE_UPDATE_INTERVAL:
                    await asyncio.sleep(5)  # Short sleep if we don't need to update yet
                    continue
                    
                # Fetch price (simplified for this implementation)
                # In a production environment, you'd want to implement a more robust price feed
                self.solana_price_usd = 140.04  # Placeholder price
                self.last_price_update = time.time()
                logger.info(f"Updated SOL price: ${self.solana_price_usd:.2f}")
                
                # Wait before next update
                await asyncio.sleep(self.PRICE_UPDATE_INTERVAL)
                
            except Exception as e:
                logger.warning(f"Solana price update failed: {e}")
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)
    
    async def monitor_market_conditions(self):
        """
        Monitor market conditions and adjust trading parameters dynamically
        """
        update_interval = config.get_int("MARKET_UPDATE_INTERVAL", 300)  # 5 minutes default
        
        while self.active:
            try:
                # Get network status (simplified implementation)
                rpc_latency_start = time.time()
                try:
                    # Simple ping to measure RPC latency
                    await self.trading_core.get_sol_balance()
                except Exception:
                    # If all else fails, just sleep a bit
                    await asyncio.sleep(0.1)
                
                rpc_latency = time.time() - rpc_latency_start
                
                # Normalize latency to 0-1 scale (>1s is severe congestion)
                normalized_latency = min(rpc_latency, 1.0)
                
                # Exponential moving average for stability
                self.network_congestion = 0.8 * self.network_congestion + 0.2 * normalized_latency
                
                # Log significant changes
                if abs(normalized_latency - self.network_congestion) > 0.2:
                    logger.info(f"Network congestion update: {self.network_congestion:.2f} (latency: {rpc_latency*1000:.1f}ms)")
                
                # Adjust priority fee based on network congestion
                if self.network_congestion > 0.8:  # High congestion
                    new_priority_fee = 3_000_000
                elif self.network_congestion > 0.5:  # Medium congestion
                    new_priority_fee = 1_500_000
                else:  # Low congestion
                    new_priority_fee = 750_000
                    
                if new_priority_fee != self.PRIORITY_FEE:
                    logger.info(f"Adjusting priority fee: {self.PRIORITY_FEE} → {new_priority_fee}")
                    self.PRIORITY_FEE = new_priority_fee
                
                # Wait for next update
                await asyncio.sleep(update_interval)
                
            except Exception as e:
                logger.error(f"Market monitoring error: {e}")
                await asyncio.sleep(60)  # Retry after a minute
    
    async def monitor_raydium_swaps(self):
        """Monitor Raydium program for swap opportunities with enhanced parsing and rate limiting"""
        retry_delay = 1
        max_retry_delay = 30
        
        while self.active:
            try:
                async with websockets.connect(
                    self.WS_URL,
                    extra_headers={
                        "api-key": self.API_KEY,
                        "User-Agent": "solana-client/1.18.23",
                    },
                    ping_interval=20,
                    ping_timeout=30,
                    close_timeout=60
                ) as ws:
                    # Reset retry delay on successful connection
                    retry_delay = 1
                    logger.info("Connected to mempool WebSocket")
                    
                    # Subscribe to Raydium program logs
                    await ws.send(json.dumps({
                        "jsonrpc": "2.0",
                        "id": 1,
                        "method": "logsSubscribe",
                        "params": [
                            {"mentions": [self.RAYDIUM_PROGRAM_ID]},
                            {"commitment": "processed"}
                        ]
                    }))
                    
                    # Wait for subscription confirmation
                    confirmation = await ws.recv()
                    logger.info("Raydium logs subscription active")
                    
                    # Process messages
                    while self.active:
                        try:
                            msg = await ws.recv()
                            data = json.loads(msg)
                            
                            if "params" in data:
                                result = data["params"].get("result", {})
                                logs = result.get("value", {}).get("logs", [])
                                tx_id = result.get("value", {}).get("signature", "unknown")
                                
                                # Skip if no logs
                                if not logs:
                                    continue
                                    
                                # Skip if not from Raydium
                                if not logs[0].startswith(f"Program {self.RAYDIUM_PROGRAM_ID}"):
                                    continue
                                
                                # Skip if Jupiter is involved (avoid competing with aggregators)
                                if any("Program JUP" in log for log in logs):
                                    continue
                                
                                # Skip if already processed transaction
                                if tx_id in self.processed_txs:
                                    continue
                                    
                                # Check for ray_logs in transaction logs
                                for log in logs:
                                    if "ray_log:" in log:
                                        # Decode raw data
                                        raw_data = self.decode_ray_log(log)
                                        if not raw_data:
                                            logger.debug(f"Failed to decode ray_log: {log[:50]}...")
                                            continue
                                            
                                        # Print full raw data for debugging during early runs
                                        if self.debug_mode:
                                            logger.debug(f"Raw data length: {len(raw_data)} bytes")
                                            logger.debug(f"Raw data hex: {raw_data[:min(50, len(raw_data))].hex()}")
                                        
                                        # Try to parse trade data
                                        trade = await self.parse_trade_data(raw_data, tx_id)
                                        
                                        # Process valid trade opportunity
                                        if trade:
                                            self.trades_found += 1
                                            logger.info(f"Found trade: {trade['amount']:.2f} SOL, estimated profit: {trade['net_profit']:.4f} SOL")
                                            
                                            # Check if we should execute this trade (not paused, have funds, etc.)
                                            if (not self.is_trading_paused and 
                                                self.balances["WSOL"] >= self.POSITION_SIZE and
                                                self.balances["SOL"] >= self.min_sol_balance and
                                                self.active_trades < self.MAX_ACTIVE_TRADES):
                                                
                                                # Execute the trade
                                                asyncio.create_task(self.execute_sandwich_trade(trade))
                                                
                        except json.JSONDecodeError:
                            logger.warning("Invalid JSON received in WebSocket")
                            continue
                        except Exception as e:
                            if not self.shutdown_requested:
                                logger.error(f"WebSocket message processing error: {e}")
                                if self.debug_mode:
                                    logger.debug(traceback.format_exc())
                            continue
                    
            except websockets.ConnectionClosed:
                logger.warning(f"WebSocket disconnected, reconnecting in {retry_delay}s...")
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)
            except Exception as e:
                if not self.shutdown_requested:
                    logger.error(f"Raydium monitor error: {e}")
                await asyncio.sleep(retry_delay)
                retry_delay = min(retry_delay * 2, max_retry_delay)
    
    async def precompute_token_allowlist(self):
        """
        Precompute a list of safe tokens to trade
        """
        try:
            # Initialize safe token list
            self.safe_tokens = set()
            self.safe_tokens.add(self.WSOL_MINT)  # WSOL is always safe
            
            # Add other known safe tokens (could be loaded from config)
            known_safe_tokens = [
                # USDC
                "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
                # USDT
                "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
                # SOL (wrapped)
                "So11111111111111111111111111111111111111112",
                # Add other safe tokens here
            ]
            
            for token in known_safe_tokens:
                self.safe_tokens.add(token)
                
            logger.info(f"Precomputed {len(self.safe_tokens)} safe tokens for trading")
            
        except Exception as e:
            logger.error(f"Failed to precompute safe tokens: {e}")
            # Initialize with just WSOL as fallback
            self.safe_tokens = {self.WSOL_MINT}

    async def setup_rust_core_extensions(self):
        """
        Setup extensions and optimizations for the Rust trading core
        """
        try:
            # Verify Rust core is working
            sol_balance = await self.trading_core.get_sol_balance()
            wsol_balance = await self.trading_core.get_wsol_balance()
            pubkey = self.trading_core.get_pubkey()
            
            logger.system(f"Rust core verified with pubkey: {pubkey}")
            logger.system(f"Initial balances - SOL: {sol_balance:.4f}, WSOL: {wsol_balance:.4f}")
            
            # Update our balances
            self.balances["SOL"] = sol_balance
            self.balances["WSOL"] = wsol_balance
            
        except Exception as e:
            logger.error(f"Failed to setup Rust core extensions: {e}")
            logger.warning("Some optimizations may not be available")

    async def initialize_trading_system(self):
        """
        Complete initialization of the trading system with all optimizations
        """
        # Precompute token allowlist
        await self.precompute_token_allowlist()
        
        # Setup Rust core extensions
        await self.setup_rust_core_extensions()
        
        # Check our balances
        await self.check_initial_balances()
        
        # Log startup complete
        logger.system("""
✅ Trading system initialization complete
--------------------------------------------
Optimization Status:
- Blockhash Management: Enabled
- ATA Optimization: Enabled
- Transaction Retry: Enabled
- Circuit Breaker: Enabled
- Performance Monitoring: Enabled
--------------------------------------------
System is ready for trading
""")
                
    async def auto_resume_trading(self, delay_seconds: int):
        """Auto-resume trading after circuit breaker or pause"""
        await asyncio.sleep(delay_seconds)
        if self.is_trading_paused:
            logger.info(f"Auto-resuming trading after {delay_seconds}s pause")
            self.is_trading_paused = False
            self.consecutive_failures = 0
    
    async def show_performance(self):
        """Display trading performance metrics periodically"""
        while self.active:
            try:
                runtime = (datetime.now() - self.start_time).total_seconds()
                hours = int(runtime // 3600)
                minutes = int((runtime % 3600) // 60)
                seconds = int(runtime % 60)
                
                # Calculate advanced metrics
                avg_profit = self.total_profit / max(1, self.successful_trades)
                profit_per_hour = self.total_profit / (runtime / 3600) if runtime > 0 else 0
                success_rate = self.successful_trades / max(1, self.trades_executed) * 100
                opportunity_rate = self.trades_found / (runtime / 3600) if runtime > 0 else 0
                
                # Calculate average execution time
                avg_execution_time = sum(self.execution_times[-20:]) / max(1, len(self.execution_times[-20:])) if self.execution_times else 0
                
                logger.system(f"""
🏁 Trading Performance Report
============================
Runtime: {hours:02d}:{minutes:02d}:{seconds:02d}
Trade Statistics:
----------------
Opportunities Found: {self.trades_found} ({opportunity_rate:.1f}/hr)
Trades Executed: {self.trades_executed}
Successful Trades: {self.successful_trades}
Success Rate: {success_rate:.1f}%
Avg Execution Time: {avg_execution_time*1000:.0f}ms
Profit Metrics:
--------------
Total Profit: {self.total_profit:.6f} SOL (${self.total_profit * self.solana_price_usd:.2f})
Profit per Hour: {profit_per_hour:.6f} SOL/hr
Average Profit per Trade: {avg_profit:.6f} SOL
Current Balances:
----------------
SOL: {self.balances['SOL']:.4f}
WSOL: {self.balances['WSOL']:.4f}
Market Conditions:
----------------
SOL Price: ${self.solana_price_usd:.2f}
Network Congestion: {self.network_congestion:.2f}
Market Volatility: {self.market_volatility:.2f}
============================
""")
                
                # Show recent trades if any
                if self.recent_trades:
                    trades_table = "\nRecent Trades:\n" + "=" * 70 + "\n"
                    trades_table += f"{'Time':^10} | {'Token':^10} | {'Amount':^10} | {'Profit SOL':^10} | {'Profit USD':^10} | {'Speed':^10}\n"
                    trades_table += "-" * 70 + "\n"
                    
                    for t in reversed(self.recent_trades[-10:]):
                        trades_table += f"{t['time']:^10} | {t['token']:^10} | {t['amount']:^10.2f} | {t['profit']:^10.6f} | ${t['profit_usd']:^9.2f} | {t['execution_time']*1000:^6.0f}ms\n"
                    
                    logger.system(trades_table)
                
                await asyncio.sleep(300)  # Update every 5 minutes
                
            except Exception as e:
                logger.error(f"Performance display error: {e}")
                await asyncio.sleep(60)
    
    async def start(self):
        """Start all bot tasks with improved initialization"""
        try:
            # Initialize trading system with all optimizations
            await self.initialize_trading_system()
            
            # Define all tasks
            tasks = [
                asyncio.create_task(self.monitor_balances()),
                asyncio.create_task(self.update_solana_price()),
                asyncio.create_task(self.monitor_raydium_swaps()),
                asyncio.create_task(self.show_performance()),
                asyncio.create_task(self.monitor_market_conditions())
            ]
            
            logger.system("All bot tasks started")
            
            # Handle shutdown signal
            try:
                # For Unix systems
                for sig in (signal.SIGINT, signal.SIGTERM):
                    asyncio.get_event_loop().add_signal_handler(sig, lambda: self.handle_shutdown(tasks))
            except NotImplementedError:
                # For Windows systems
                logger.info("Running on Windows, using alternate signal handling")
                # Windows doesn't support signal handlers with asyncio, we'll rely on KeyboardInterrupt
            
            # Wait for all tasks to complete
            await asyncio.gather(*tasks, return_exceptions=True)
            
        except Exception as e:
            logger.error(f"Bot startup error: {e}")
            logger.debug(traceback.format_exc())
    
    def handle_shutdown(self, tasks):
        """Handle shutdown signals gracefully"""
        logger.system("Shutdown signal received")
        self.shutdown_requested = True
        self.active = False
        for task in tasks:
            task.cancel()
    
    def shutdown(self):
        """Shut down the bot gracefully"""
        logger.system("Shutting down the bot")
        self.active = False
        self.shutdown_requested = True
        
        # Close CUDA resources if needed
        if getattr(self, 'cuda_enabled', False):
            try:
                import torch
                torch.cuda.empty_cache()
            except:
                pass
        
        # Close thread executor
        try:
            self.thread_pool.shutdown(wait=False)
        except:
            pass


async def main():
    """Main entry point with proper error handling"""
    bot = None
    try:
        bot = RaydiumMEVBot()
        await bot.start()
    except KeyboardInterrupt:
        logger.system("Bot terminated by user")
        if bot:
            bot.shutdown()
    except Exception as e:
        logger.error(f"Unhandled exception in main: {e}")
        logger.debug(traceback.format_exc())
    finally:
        # Cleanup resources if needed
        if bot:
            bot.shutdown()
        # Close any open connections
        await asyncio.sleep(0.5)  # Brief delay to allow clean shutdown
        logger.system("Bot shutdown completed")

if __name__ == "__main__":
    try:
        if sys.platform == 'win32':
            # Windows-specific asyncio setup
            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
        
        # Run the main async function
        asyncio.run(main())
    except KeyboardInterrupt:
        logger.system("Bot terminated by user")
    except Exception as e:
        logger.error(f"Fatal error: {e}")
        logger.debug(traceback.format_exc())  make it dynamically copy the traders addresses please for the trade to copy them exactly how the trader is that we are targeting is using them please thank you .

Okay, I need to figure out how to modify the provided Raydium MEV bot code so that it dynamically copies the trader's addresses for each transaction it's targeting. The user wants the bot to use the exact same addresses that the targeted trader is using, rather than relying on predefined addresses from the config.

First, I should look at where the current code is using the predefined addresses. From the code, I see that in the `execute_trade_rust_core` method, there's a list of accounts that includes various Raydium and Serum addresses loaded from the config. These include AMM_ID, MARKET_AMM_AUTHORITY, etc. The goal is to replace these static addresses with the ones extracted from the victim's transaction.

The key part is in parsing the trade data. The `parse_trade_data` function currently extracts token_a_mint and token_b_mint, but we need to extract more account addresses from the victim's transaction logs. These accounts would be part of the instruction data when the victim executes a swap on Raydium.

Looking at the `parse_trade_data` method, when processing the raw_data from the ray_log, the code attempts to extract token mints. To get the other necessary addresses (like AMM_ID, market accounts, etc.), I need to parse those from the victim's transaction data.

In Solana transactions, when a program is invoked, the accounts involved in the transaction are listed in the `accounts` field of the instruction. The Raydium swap instruction would include all the necessary accounts like the AMM, market, open orders, etc. These accounts are part of the raw transaction data.

So, the plan is to extract these accounts from the victim's transaction data when processing the ray_log. The raw_data in the `parse_trade_data` function likely contains these accounts. The current code tries to extract token mints, but we need to parse the entire list of accounts used in the victim's swap instruction.

In Solana, the instruction data for a swap includes a list of accounts required for the swap. The structure of a Raydium swap transaction's instruction data includes the program ID, followed by the accounts involved. Each account in the list is a public key, so each is 32 bytes.

The raw_data obtained from the ray_log should contain this list of accounts. So, in the `parse_trade_data` function, after extracting the instruction type and amount, we need to parse the subsequent bytes to get the account addresses.

For example, after the first byte (instruction type) and the next 8 bytes (amount), the remaining bytes would include the accounts involved in the transaction. Each account is a 32-byte public key.

So, modifying the `parse_trade_data` function to extract these accounts from the raw_data is essential. Once these accounts are extracted, they can be stored in the trade_data dictionary and then used when constructing the front-run and back-run transactions.

In the `execute_trade_rust_core` method, instead of using the predefined account_list from the config, the bot should use the account addresses extracted from the victim's transaction. This way, each sandwich trade uses the exact same accounts as the victim's trade.

Potential steps:

1. In `parse_trade_data`, after extracting the instruction type and amount, parse the subsequent bytes to extract the account addresses used in the victim's transaction.

2. Store these accounts in the trade_data dictionary.

3. Modify `execute_trade_rust_core` to use the accounts from trade_data instead of the config.

But how exactly are these accounts structured in the raw_data? The current code's attempts to extract token mints might not be sufficient. The correct approach would be to parse all the accounts from the instruction data.

Looking at Raydium's swap instruction structure, the accounts are passed as a list of public keys. For example, the swap instruction (instruction 9 or 10) requires a specific list of accounts. The exact number and order of these accounts can be found in Raydium's documentation or code.

Assuming that the raw_data includes the full list of accounts required for the swap, we can parse each 32-byte segment as a public key. For example, after the initial 9 bytes (1 byte instruction + 8 bytes amount), the next bytes would be the accounts.

So, in the `parse_trade_data` function, after processing the instruction and amount, the code should iterate over the remaining bytes in 32-byte chunks to extract each account address.

Once these accounts are extracted, they can be stored in the trade_data, and then passed to the `execute_trade_rust_core` method to construct the TradeParams with the correct accounts.

This approach ensures that each sandwich trade uses the exact same accounts as the victim's transaction, allowing the bot to dynamically adapt to different pools and markets that the victim is trading on.

Potential code changes:

- In `parse_trade_data`, after extracting instruction_type and amount, extract the accounts from raw_data.

- Modify the TradeParams in `execute_trade_rust_core` to use these dynamically extracted accounts instead of the config ones.

Testing this would require verifying that the extracted accounts match those used in the victim's transaction and that the Rust core can correctly use them to construct the swap transactions.

Another consideration is ensuring that the order of the accounts in the list matches what the Raydium program expects. The order of the accounts in the instruction is crucial, so the parsing must maintain their original order.

In summary, the main modifications involve parsing the account addresses from the victim's transaction data and using them dynamically in the bot's trades instead of static config values.

(Due to technical issues, the search service is temporarily unavailable.)

To dynamically copy the trader's addresses from each targeted transaction, we need to modify the trade parsing and execution logic. Here are the key changes:

    Enhanced Trade Data Parsing:

python
Copy

async def parse_trade_data(self, raw_data: bytes, tx_id: str = None) -> Optional[dict]:
    # ... [existing code up to amount extraction]
    
    # Extract accounts list from raw_data
    accounts = []
    account_bytes = raw_data[9:]  # After instruction type and amount
    for i in range(0, len(account_bytes), 32):
        if i+32 > len(account_bytes):
            break
        account = base58.b58encode(account_bytes[i:i+32]).decode()
        accounts.append(account)

    # Validate minimum required accounts for Raydium swap
    if len(accounts) < 15:
        logger.debug("Insufficient accounts in transaction data")
        return None

    # Map accounts to their roles using Raydium's expected structure
    account_map = {
        "raydium_program": accounts[0],
        "amm_id": accounts[1],
        "amm_authority": accounts[2],
        "amm_open_orders": accounts[3],
        "amm_target": accounts[4],
        "pool_token_account": accounts[5],
        "pool_pc_token_account": accounts[6],
        "serum_program": accounts[7],
        "serum_market": accounts[8],
        "serum_bids": accounts[9],
        "serum_asks": accounts[10],
        "serum_event_queue": accounts[11],
        "serum_base_vault": accounts[12],
        "serum_quote_vault": accounts[13],
        "base_vault_signer": accounts[14]
    }

    # ... [existing validation and profit calculation]

    # Add account map to trade data
    trade_data["accounts"] = account_map
    
    return trade_data

    Modified Trade Execution:

python
Copy

async def execute_trade_rust_core(self, trade_data: Dict[str, Any], is_buy: bool, 
                                   amount: int, blockhash: str = None) -> Optional[str]:
    try:
        # Use dynamically extracted accounts from victim transaction
        account_map = trade_data["accounts"]
        account_list = [
            account_map["raydium_program"],
            account_map["amm_id"],
            account_map["amm_authority"],
            account_map["amm_open_orders"],
            account_map["amm_target"],
            account_map["pool_token_account"],
            account_map["pool_pc_token_account"],
            account_map["serum_program"],
            account_map["serum_market"],
            account_map["serum_bids"],
            account_map["serum_asks"],
            account_map["serum_event_queue"],
            account_map["serum_base_vault"],
            account_map["serum_quote_vault"],
            account_map["base_vault_signer"]
        ]

        # ... [rest of existing execute_trade_rust_core code]

    Updated Trade Data Structure:

python
Copy

# In the returned trade_data dictionary add:
trade_data = {
    # ... existing fields
    "accounts": account_map,
    # ... rest of fields
}

These changes will:

    Extract and decode all account addresses from the victim's transaction data

    Map them to their respective roles in the Raydium swap transaction

    Use these dynamically extracted addresses when constructing our own transactions

Key Advantages:

    Eliminates dependency on pre-configured pool addresses

    Allows following victims into any Raydium pool

    Matches the victim's transaction structure exactly

    Increases opportunities by working across all pools

Note: The account positions in the list are based on Raydium's program structure. This might need adjustment if Raydium updates their program interface.

To use this enhanced version, you would need to:

    Remove static pool addresses from your config

    Ensure your RPC node provides full transaction data

    Verify the account parsing matches current Raydium program expectations

This implementation dynamically clones the victim's exact transaction structure while maintaining the same MEV sandwich strategy.