can u help me please i need your assistance my eyes are going bad and i need you to write this script so that the spacing is correct dont remove any code though please and write all of my code in here just fix spacing issues thank much! use anyhow::{anyhow, Result};
use bincode;
use config::Config;
use env_logger::Env;
use futures_util::{SinkExt, StreamExt};
use log::{debug, error, info, warn};
use reqwest::Client as HttpClient;
use serde::{Deserialize, Serialize};
use serde_json::json;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    hash::Hash,
    signer::{keypair::Keypair, Signer},
    system_instruction::SystemError,
};
use std::{
    collections::HashMap,
    fs::{File, OpenOptions},
    io::{Read, Write},
    sync::{Arc, Mutex as StdMutex},
    time::{Duration, Instant},
};
use tokio::{sync::Mutex, time::interval};
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message as WsMessage};

const WSOL_MINT: &str = "So11111111111111111111111111111111111111112";
const RAYDIUM_AMM_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"; // Raydium Liquidity Pool V4 Program ID
const MAX_ACTIVE_TRADES: usize = 5;
const MAX_BUY_ATTEMPTS: u32 = 3;
const BUY_ATTEMPT_DELAY: Duration = Duration::from_millis(200);
const STATE_FILE: &str = "bot_state.json";
const PRICE_CHECK_INTERVAL: Duration = Duration::from_secs(1);

#[derive(Debug, Deserialize, Clone)]
pub struct BotConfig {
    rpc_url: String,
    helius_websocket_url: String,
    keypair_path: String,
    commitment: String,
    snipe_amount: f64,
    max_slippage: f64,
    priority_fee: u64,
    helius_api_key: String,
    profit_target_percent: f64,
    stop_loss_percent: f64,
    min_liquidity: f64,
    max_liquidity: f64,
    min_sol_balance: f64,
}

impl BotConfig {
    fn redacted(&self) -> Self {
        let mut redacted_self = self.clone();
        redacted_self.rpc_url = self.redact(&self.rpc_url);
        redacted_self.helius_websocket_url = self.redact(&self.helius_websocket_url);
        redacted_self.keypair_path = self.redact(&self.keypair_path);
        redacted_self.helius_api_key = self.redact(&self.helius_api_key);
        redacted_self
    }

    fn redact(&self, data: &str) -> String {
        let visible = 5;
        if data.len() > visible {
            format!("{}{}", &data[..visible], "*".repeat(data.len() - visible))
        } else {
            data.to_string()
        }
    }
}

#[derive(Clone, Serialize, Deserialize, Debug)]
struct Trade {
    token_address: String,
    amount: u64,
    buy_price: f64,
    take_profit_price: f64,
    stop_loss_price: f64,
    timestamp: u64,
}

struct PriceCache {
    prices: HashMap<String, (f64, Instant)>,
    ttl: Duration,
}

impl PriceCache {
    fn new(ttl: u64) -> Self {
        Self {
            prices: HashMap::new(),
            ttl: Duration::from_secs(ttl),
        }
    }

    fn get(&self, token_address: &str) -> Option<f64> {
        self.prices
            .get(token_address)
            .and_then(|(price, timestamp)| if timestamp.elapsed() < self.ttl { Some(*price) } else { None })
    }

    fn set(&mut self, token_address: String, price: f64) {
        self.prices.insert(token_address.clone(), (price, Instant::now()));
        debug!("Price cache updated: {} = {}", token_address, price);
    }
}

struct Bot {
    config: BotConfig,
    rpc_client: Arc<RpcClient>,
    keypair: Arc<Keypair>,
    price_cache: Arc<Mutex<PriceCache>>,
    active_trades: Arc<Mutex<HashMap<String, Trade>>>,
    last_blockhash: Arc<Mutex<(Hash, Instant)>>,
    metadata_limiter: Arc<Mutex<()>>,
    swap_limiter: Arc<Mutex<()>>,
    http_client: HttpClient,
}

impl Bot {
    async fn new() -> Result<Self> {
        info!("Loading configuration from environment variables");
        let config = load_config()?;
        info!("Config loaded: {:?}", config.redacted());

        let rpc_client = Arc::new(RpcClient::new_with_commitment(
            config.rpc_url.clone(),
            CommitmentConfig::processed(), // You can adjust the commitment level as needed
        ));
        let http_client = HttpClient::builder().timeout(Duration::from_secs(10)).build()?;
        let keypair = Arc::new(load_keypair(&config.keypair_path)?);
        debug!("Keypair successfully loaded.");

        let bot = Self {
            config,
            rpc_client,
            keypair,
            price_cache: Arc::new(Mutex::new(PriceCache::new(60))),
            active_trades: Arc::new(Mutex::new(HashMap::new())),
            last_blockhash: Arc::new(Mutex::new((Hash::default(), Instant::now()))),
            metadata_limiter: Arc::new(Mutex::new(())),
            swap_limiter: Arc::new(Mutex::new(())),
            http_client,
        };
        bot.load_state().await?;
        info!("Bot initialized successfully.");
        Ok(bot)
    }

    async fn run(&self) -> Result<()> {
        info!("Starting Raydium Sniper Bot");

        let initial_balance = self.get_sol_balance().await?;
        info!("Initial SOL balance: {} SOL", initial_balance as f64 / 1e9);

        loop {
            info!("Attempting to connect to Helius WebSocket...");
            match connect_async(&self.config.helius_websocket_url).await {
                Ok((ws_stream, _)) => {
                    let (mut write, mut read) = ws_stream.split();
                    info!("Connected to Helius WebSocket");

                    write
                        .send(WsMessage::Text(
                            json!({
                                "jsonrpc": "2.0",
                                "id": 1,
                                "method": "logsSubscribe",
                                "params": {
                                    "filter": {
                                        "programId": RAYDIUM_AMM_PROGRAM_ID
                                    },
                                    "commitment": "confirmed"
                                }
                            })
                            .to_string(),
                        ))
                        .await?;
                    info!("Subscribed to Raydium AMM logs");

                    let mut interval = interval(PRICE_CHECK_INTERVAL);
                    loop {
                        tokio::select! {
                            Some(msg) = read.next() => {
                                match msg {
                                    Ok(WsMessage::Text(text)) => {
                                        debug!("Received WebSocket message: {}", text);
                                        if let Err(e) = self.handle_event(&text).await {
                                            error!("Error handling event: {}", e);
                                        }
                                    }
                                    Ok(WsMessage::Close(_)) => {
                                        warn!("WebSocket connection closed");
                                        break;
                                    }
                                    Ok(_) => {},
                                    Err(e) => {
                                        error!("WebSocket error: {}", e);
                                        break;
                                    }
                                }
                            }
                            _ = interval.tick(), if !self.active_trades.lock().await.is_empty() => {
                                if let Err(e) = self.check_active_trades().await {
                                    error!("Error checking active trades: {}", e);
                                }
                            }
                        }
                    }
                }
                Err(e) => {
                    error!("WebSocket connect error: {}. Retrying in 5 seconds...", e);
                    tokio::time::sleep(Duration::from_secs(5)).await;
                }
            }
        }
    }

    async fn handle_event(&self, text: &str) -> Result<()> {
        debug!("Handling event for message: {}", text);
        let event: serde_json::Value = serde_json::from_str(text)?;

        if let Some(error) = event.get("error") {
            return Err(anyhow!("WebSocket error received: {}", error));
        }

        if let Some(params) = event.get("params") {
            if let Some(token_address) = params.get("tokenAddress").and_then(|v| v.as_str()) {
                if let Some(error) = event.get("error") {
                    return Err(anyhow!("WebSocket error received: {}", error));
                }
            
                if let Some(params) = event.get("params") {
                    if let Some(token_address) = params.get("tokenAddress").and_then(|v| v.as_str()) {
                        info!("New pool detected: Token {}", token_address);
                        self.process_token(token_address.to_string()).await?;
                    }
                }
            }
            
            async fn process_token(&self, token_address: String) -> Result<()> {
                let metadata = self.get_token_metadata(vec![token_address.clone()]).await?;
                if let Some(meta) = metadata.get(&token_address) {
                    if self.is_valid_pool(&token_address, meta).await? {
                        info!("Token {} is valid, attempting to buy...", token_address);
                        self.attempt_buy(token_address).await?;
                    } else {
                        warn!("Token {} failed validation checks. Skipping.", token_address);
                    }
                }
                Ok(())
            }
            
            async fn get_token_metadata(&self, token_addresses: Vec<String>) -> Result<HashMap<String, serde_json::Value>> {
                let url = format!(
                    "{}?api-key={}",
                    self.config.helius_rpc_url,
                    self.config.helius_api_key,
                );
            
                let body = json!({
                    "jsonrpc": "2.0",
                    "id": "get-token-metadata",
                    "method": "getTokenMetadata",
                    "params": [
                        token_addresses,
                        "decimals",
                        "name",
                        // Add other fields as needed
                    ]
                });
            
                info!("Fetching metadata from URL: {}", url);
            
                let response = self
                    .http_client
                    .post(&url)
                    .json(&body)
                    .send()
                    .await?
                    .json::<serde_json::Value>()
                    .await?;
            
                let metadata_map = response["result"].as_array()
                    .ok_or_else(|| anyhow!("Invalid metadata response format"))?
                    .iter()
                    .map(|meta| {
                        let address = meta["address"].as_str().unwrap_or("").to_string();
                        (address, meta.clone())
                    })
                    .collect();
            
                debug!("Metadata response for multiple tokens: {:?}", metadata_map);
            
                Ok(metadata_map)
            }
            
            async fn is_valid_pool(&self, token_address: &str, metadata: &serde_json::Value) -> Result<bool> {
                debug!("Validating pool for token: {}", token_address);
            
                let is_raydium_pool = metadata
                    .get("updateAuthority")
                    .and_then(serde_json::Value::as_str)
                    .map_or(false, |auth| auth == RAYDIUM_AMM_PROGRAM_ID);
            
                let is_spl_token = metadata
                    .get("tokenStandard")
                    .and_then(serde_json::Value::as_str)
                    == Some("SPL");
            
                let liquidity = metadata
                    .get("liquidity")
                    .and_then(serde_json::Value::as_f64)
                    .unwrap_or(0.0);
            
                let is_valid = is_raydium_pool
                    && is_spl_token
                    && liquidity >= self.config.min_liquidity
                    && liquidity <= self.config.max_liquidity;
            
                info!(
                    "Pool validation for {}: Raydium: {}, SPL: {}, Liquidity: {} SOL, Valid: {}",
                    token_address, is_raydium_pool, is_spl_token, liquidity, is_valid
                );
            
                Ok(is_valid)
            }
            
            async fn attempt_buy(&self, token_address: String) -> Result<()> {
                info!("Starting buy attempts for token: {}", token_address);
                let active_trades_count = self.active_trades.lock().await.len();
                if active_trades_count >= MAX_ACTIVE_TRADES {
                    warn!(
                        "Maximum active trades ({}) reached. Skipping buy for {}.",
                        MAX_ACTIVE_TRADES, token_address
                    );
                    return Ok(());
                }
            
                let snipe_amount = self.config.snipe_amount;
                let snipe_amount_lamports = (snipe_amount * 1e9) as u64;
            
                for attempt in 1..=MAX_BUY_ATTEMPTS {
                    info!("Buy attempt {} for token {}.", attempt, token_address);
                    match self.execute_buy(&token_address, snipe_amount_lamports).await {
                        Ok(_) => {
                            info!("Successfully bought token {}", token_address);
                            return Ok(());
                        }
                        Err(e) => {
                            warn!("Buy attempt {} failed for token {}: {}", attempt, token_address, e);
                            if attempt < MAX_BUY_ATTEMPTS {
                                tokio::time::sleep(BUY_ATTEMPT_DELAY).await;
                            }
                        }
                    }
                }
                error!("All buy attempts failed for token: {}", token_address);
                Ok(())
            }
            
            async fn execute_buy(&self, token_address: &str, amount: u64) -> Result<()> {
                debug!("Preparing to execute buy for token: {}, amount: {}", token_address, amount);
                let _guard = self.swap_limiter.lock().await;
            
                let jupiter_url = "https://quote-api.jup.ag/v6/quote";
                let params = json!({
                    "inputMint": WSOL_MINT,
                    "outputMint": token_address,
                    "amount": amount.to_string(),
                    "slippageBps": (self.config.max_slippage * 100.0) as u64,
                });
            
                info!("Fetching quote for buy: {:?}", params);
                let quote_response = self.http_client
                    .get(jupiter_url)
                    .query(&params)
                    .send()
                    .await?
                    .json::<serde_json::Value>()
                    .await?;
            
                debug!("Received quote response: {:?}", quote_response);
            
                let swap_url = "https://quote-api.jup.ag/v6/swap";
                let swap_params = json!({
                    "quoteResponse": quote_response,
                    "userPublicKey": self.keypair.pubkey().to_string(),
                    "wrapUnwrapSOL": true,
                    "priorityFee": self.config.priority_fee,
                });
            
                info!("Initiating swap with parameters: {:?}", swap_params);
                let swap_response = self.http_client
                    .post(swap_url)
                    .json(&swap_params)
                    .send()
                    .await?
                    .json::<serde_json::Value>()
                    .await?;
            
                debug!("Received swap response: {:?}", swap_response);
            
                let transaction_data = base64::decode(swap_response["swapTransaction"].as_str().unwrap())?;
                let mut transaction: Transaction = bincode::deserialize(&transaction_data)?;
                let recent_blockhash = self.get_recent_blockhash().await?;
                transaction.sign(&[&self.keypair], recent_blockhash);
            
                debug!("Sending transaction to Solana network");
                let signature = self.rpc_client.send_and_confirm_transaction(&transaction)?;
                info!("Buy transaction confirmed. Signature: {}", signature);
            
                // Record the trade
                {
                    let mut active_trades = self.active_trades.lock().await;
                    let trade = Trade {
                        token_address: token_address.to_string(),
                        amount,
                        buy_price: quote_response["outAmount"].as_f64().unwrap_or(0.0),
                        take_profit_price: quote_response["outAmount"].as_f64().unwrap_or(0.0)
                            * (1.0 + self.config.profit_target_percent / 100.0),
                        stop_loss_price: quote_response["outAmount"].as_f64().unwrap_or(0.0)
                            * (1.0 - self.config.stop_loss_percent / 100.0),
                        timestamp: Instant::now().elapsed().as_secs(),
                    };
                    active_trades.insert(token_address.to_string(), trade.clone());
                    debug!("Added trade to active trades: {:?}", trade);
                }
            
                Ok(())
            }
            
            async fn check_active_trades(&self) -> Result<()> {
                info!("Checking active trades for sell opportunities...");
                let mut trades_to_remove = Vec::new();
                let active_trades = self.active_trades.lock().await;
            
                for (token_address, trade) in active_trades.iter() {
                    let current_price = self.get_token_price(token_address).await?;
                    debug!("Current price for {}: {}", token_address, current_price);
            
                    if current_price >= trade.take_profit_price || current_price <= trade.stop_loss_price {
                        info!(
                            "Executing sell for token {}. Current price: {}.",
                            token_address, current_price
                        );
                        if let Err(e) = self.execute_sell(token_address).await {
                            error!("Failed to sell token {}: {}", token_address, e);
                        } else {
                            trades_to_remove.push(token_address.clone());
                        }
                    }
                }
                drop(active_trades);
            
                if !trades_to_remove.is_empty() {
                    let mut active_trades = self.active_trades.lock().await;
                    for token_address in trades_to_remove {
                        active_trades.remove(&token_address);
                        info!(
                            "Removed trade for token {} after successful sell.",
                            token_address
                        );
                    }
                }
                Ok(())
            }
            
            async fn execute_sell(&self, token_address: &str) -> Result<()> {
                debug!("Preparing to execute sell for token: {}", token_address);
                let _guard = self.swap_limiter.lock().await;
            
                let amount = {
                    let active_trades = self.active_trades.lock().await;
                    active_trades
                        .get(token_address)
                        .map(|trade| trade.amount)
                        .ok_or_else(|| anyhow!("Trade not found for token {}", token_address))?
                };
            
                let jupiter_url = "https://quote-api.jup.ag/v6/quote";
                let params = json!({
                    "inputMint": token_address,
                    "outputMint": WSOL_MINT,
                    "amount": amount.to_string(),
                    "slippageBps": (self.config.max_slippage * 100.0) as u64,
                });
            
                info!("Fetching quote for sell: {:?}", params);
                let quote_response = self.http_client
                    .get(jupiter_url)
                    .query(&params)
                    .send()
                    .await?
                    .json::<serde_json::Value>()
                    .await?;
            
                debug!("Received quote response: {:?}", quote_response);
            
                let swap_url = "https://quote-api.jup.ag/v6/swap";
                let swap_params = json!({
                    "quoteResponse": quote_response,
                    "userPublicKey": self.keypair.pubkey().to_string(),
                    "wrapUnwrapSOL": true,
                    "priorityFee": self.config.priority_fee,
                });
            
                info!("Initiating swap with parameters: {:?}", swap_params);
                let swap_response = self.http_client
                    .post(swap_url)
                    .json(&swap_params)
                    .send()
                    .await?
                    .json::<serde_json::Value>()
                    .await?;
            
                debug!("Received swap response: {:?}", swap_response);
            
                let transaction_data = base64::decode(swap_response["swapTransaction"].as_str().unwrap())?;
                let mut transaction: Transaction = bincode::deserialize(&transaction_data)?;
                let recent_blockhash = self.get_recent_blockhash().await?;
                transaction.sign(&[&self.keypair], recent_blockhash);
            
                debug!("Sending transaction to Solana network");
                let signature = self.rpc_client.send_and_confirm_transaction(&transaction)?;
                info!("Sell transaction confirmed. Signature: {}", signature);
            
                Ok(())
            }
            
            async fn get_token_price(&self, token_address: &str) -> Result<f64> {
                debug!("Fetching token price for: {}", token_address);
                if let Some(price) = self.price_cache.lock().await.get(token_address) {
                    debug!("Price cache hit for {}: {}", token_address, price);
                    return Ok(price);
                }
            
                let url = format!(
                    "https://rpc.helius.xyz?api-key={}",
                    self.config.helius_api_key
                );
            
                let body = json!({
                    "jsonrpc": "2.0",
                    "id": "get-token-price",
                    "method": "getTokenMetadata",
                    "params": [
                        [token_address]
                    ]
                });
            
                info!("Fetching token price from URL: {}", url);
                let response = self
                    .http_client
                    .post(&url)
                    .json(&body)
                    .send()
                    .await?
                    .json::<serde_json::Value>()
                    .await?;
                debug!("Received token metadata response: {:?}", response);
            
                if let Some(price) = response["result"][0]["price"].as_f64() {
                    self.price_cache
                        .lock()
                        .await
                        .set(token_address.to_string(), price);
                    Ok(price)
                } else {
                    Err(anyhow!("Price not found for token {}", token_address))
                }
            }
            
            async fn get_sol_balance(&self) -> Result<u64> {
                debug!(
                    "Fetching SOL balance for account: {}",
                    self.keypair.pubkey()
                );
                let balance = self.rpc_client.get_balance(&self.keypair.pubkey())?;
                if balance < self.config.min_sol_balance as u64 * 1_000_000_000 {
                    warn!(
                        "SOL balance is below minimum threshold: {} SOL",
                        balance as f64 / 1e9
                    );
                }
                Ok(balance)
            }
            
            async fn get_recent_blockhash(&self) -> Result<Hash> {
                debug!("Fetching recent blockhash...");
                let mut last_blockhash = self.last_blockhash.lock().await;
                if last_blockhash.1.elapsed() > Duration::from_secs(60) {
                    let new_blockhash = self.rpc_client.get_latest_blockhash()?;
                    *last_blockhash = (new_blockhash, Instant::now());
                    debug!("Updated blockhash: {}", new_blockhash);
                    Ok(new_blockhash)
                } else {
                    debug!("Using cached blockhash");
                    Ok(last_blockhash.0)
                }
            }
            
            async fn save_state(&self) -> Result<()> {
                info!("Saving bot state to file...");
                let active_trades = self.active_trades.lock().await;
                let serialized = serde_json::to_string(&*active_trades)?;
                let mut file = OpenOptions::new()
                    .write(true)
                    .create(true)
                    .truncate(true)
                    .open(STATE_FILE)?;
                file.write_all(serialized.as_bytes())?;
                info!("State successfully saved to {}.", STATE_FILE);
                Ok(())
            }
            
            async fn load_state(&self) -> Result<()> {
                let state_file_path = STATE_FILE;
                info!("Loading bot state from file: {}", state_file_path);
                match File::open(state_file_path) {
                    Ok(mut file) => {
                        let mut contents = String::new();
                        file.read_to_string(&mut contents)?;
                        let loaded_trades: HashMap<String, Trade> = serde_json::from_str(&contents)?;
                        let mut active_trades = self.active_trades.lock().await;
                        *active_trades = loaded_trades;
                        info!("Successfully loaded state from {}.", state_file_path);
                    }
                    Err(e) => {
                        warn!(
                            "State file not found: {}. Starting with an empty state. Error: {}",
                            state_file_path, e
                        );
                    }
                }
                Ok(())
            }
            
            #[tokio::main]
            async fn main() -> Result<()> {
                env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
                info!("🚀 Solana Sniper Bot v1.0 - Launching! 🚀");
            
                let bot = Arc::new(Bot::new("config.toml").await?);
            
                // Save state every 60 seconds
                let bot_clone = bot.clone();
                tokio::spawn(async move {
                    loop {
                        tokio::time::sleep(Duration::from_secs(60)).await;
                        if let Err(e) = bot_clone.save_state().await {
                            error!("Failed to save state: {}", e);
                        }
                    }
                });
            
                // Setup signal handling for graceful shutdown
    let (tx, rx) = tokio::sync::oneshot::channel();
    let tx = Arc::new(StdMutex::new(Some(tx)));
    let tx_clone = tx.clone();
    
    ctrlc::set_handler(move || {
        if let Some(tx) = tx_clone.lock().unwrap().take() {
            let _ = tx.send(());
        }
    })
    .expect("Error setting Ctrl+C handler");

    tokio::select! {
        result = bot.run() => {
            if let Err(e) = result {
                error!("Bot encountered an error: {}", e);
            }
        }
        _ = rx => {
            info!("Received shutdown signal. Saving state and exiting...");
            if let Err(e) = bot.save_state().await {
                error!("Error saving state on shutdown: {}", e);
            }
        }
    }

    info!("🛑 Bot shutting down. Goodbye!");
    Ok(())
}