Finished `release` profile [optimized] target(s) in 0.27s
     Running `target\release\solana_rust_bot.exe`
=====================================
🔫 SOLANA SNIPER ELITE v2.0 🎯
=====================================
🔫 Initializing Solana Sniper Bot v2.0
💰 Loaded wallet: EHrpEhiBU13CygunXsmeGnzR6ircuYFQcFYPZT5BWUMA
[2024-10-24T15:30:06Z INFO  solana_rust_bot] 🎯 Manual Buy Initiated!
[2024-10-24T15:30:06Z DEBUG solana_rust_bot] 💰 Buying Token: DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263
[2024-10-24T15:30:06Z INFO  solana_rust_bot] 🎯 Trade Opportunity Found!
[2024-10-24T15:30:06Z DEBUG solana_rust_bot] 💰 Snipe Amount: 10000000
[2024-10-24T15:30:06Z DEBUG solana_rust_bot] ⚡ Priority Fee: 5000
[2024-10-24T15:30:06Z WARN  solana_rust_bot] 🚀 Executing Trade on: DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263

🎯 EXECUTING SNIPE
[2024-10-24T15:30:06Z DEBUG solana_rust_bot] 🌐 Requesting Quote URL: https://quote-api.jup.ag/v4/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263&amount=10000000&slippageBps=1500
[2024-10-24T15:30:06Z DEBUG reqwest::connect] starting new connection: https://quote-api.jup.ag/
[2024-10-24T15:30:06Z DEBUG hyper::client::connect::dns] resolving host="quote-api.jup.ag"
[2024-10-24T15:30:06Z DEBUG hyper::client::connect::http] connecting to [2606:4700:4400::ac40:90c5]:443
[2024-10-24T15:30:06Z DEBUG hyper::client::connect::http] connected to [2606:4700:4400::ac40:90c5]:443
[2024-10-24T15:30:06Z DEBUG hyper::proto::h1::io] flushed 242 bytes
[2024-10-24T15:30:06Z DEBUG hyper::proto::h1::io] parsed 10 headers
[2024-10-24T15:30:06Z DEBUG hyper::proto::h1::conn] incoming body is empty
[2024-10-24T15:30:06Z DEBUG hyper::client::pool] pooling idle connection for ("https", quote-api.jup.ag)
[2024-10-24T15:30:06Z DEBUG solana_rust_bot] 📡 Response Status: 404 Not Found
[2024-10-24T15:30:06Z DEBUG solana_rust_bot] 📝 Raw Response:
Error: Empty response from Jupiter API
error: process didn't exit successfully: `target\release\solana_rust_bot.exe` (exit code: 1)
PS C:\solana_rust_bot>  use log::{debug, info, warn, error};
use env_logger;
use anyhow::{anyhow, Result};
use base64::{self, Engine, engine::general_purpose::STANDARD as base64_engine};
use bincode;
use config::{Config, File};
use ctrlc;
use futures_util::{SinkExt, StreamExt};
use reqwest::Client as HttpClient;
use serde::{Deserialize, Serialize};
use serde_json::json;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    compute_budget::ComputeBudgetInstruction,
    commitment_config::CommitmentConfig,
    instruction::{AccountMeta, Instruction},
    message::Message,
    signer::{keypair::Keypair, Signer},
    transaction::Transaction,
};

use std::{
    sync::Arc,
    time::Duration,
    time::Instant,
};
use tokio::{sync::Mutex, time::interval};
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message as WsMessage};
use colored::*;

const WSOL_MINT: &str = "So11111111111111111111111111111111111111112";
const RAYDIUM_AMM_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
const STATE_FILE: &str = "sniper_state.json";

#[derive(Debug, Deserialize, Clone)]
struct BotConfig {
    rpc: RpcConfig,
    urls: UrlConfig,
    wallet: WalletConfig,
    api_keys: ApiKeys,
    trading: TradingConfig,
    websocket: WebsocketConfig,
    token_requirements: TokenRequirements,
}

#[derive(Debug, Deserialize, Clone)]
struct RpcConfig {
    solana_rpc_url: String,
    fallback_url: String,
}

#[derive(Debug, Deserialize, Clone)]
struct UrlConfig {
    ws_url: String,
    rpc_url: String,
}

#[derive(Debug, Deserialize, Clone)]
struct WalletConfig {
    keypair_path: String,
    public_key: String,
}

#[derive(Debug, Deserialize, Clone)]
struct ApiKeys {
    helius_api_key: String,
}

#[derive(Debug, Deserialize, Clone)]
struct TradingConfig {
    snipe_amount: u64,
    profit_target_percent: f64,
    max_retry_attempts: u32,
    stop_loss_percent: f64,
    max_slippage: f64,
    priority_fee: u64,
    min_sol_balance: f64,
}

#[derive(Debug, Deserialize, Clone)]
struct WebsocketConfig {
    reconnect_interval: u64,
}

#[derive(Debug, Deserialize, Clone)]
struct TokenRequirements {
    mutable: bool,
    burnt: bool,
    frozen: bool,
    min_supply: u64,
}

#[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 Bot {
    config: BotConfig,
    rpc_client: Arc<RpcClient>,
    keypair: Arc<Keypair>,
    active_trades: Arc<Mutex<std::collections::HashMap<String, Trade>>>,
    http_client: HttpClient,
}

impl Bot {
    async fn new() -> Result<Self> {
        println!("{}", "🔫 Initializing Solana Sniper Bot v2.0".bright_green().bold());
        
        let config = Config::builder()
            .add_source(File::with_name("config.toml"))
            .build()?
            .try_deserialize::<BotConfig>()?;

        let rpc_client = Arc::new(RpcClient::new_with_commitment(
            config.rpc.solana_rpc_url.clone(),
            CommitmentConfig::confirmed(),
        ));

        let keypair_bytes: Vec<u8> = serde_json::from_str(
            &std::fs::read_to_string(&config.wallet.keypair_path)?
        )?;
        
        let keypair = Arc::new(Keypair::from_bytes(&keypair_bytes)?);
        
        println!("{}", format!("💰 Loaded wallet: {}", keypair.pubkey()).yellow());
        
        let http_client = HttpClient::builder()
            .timeout(Duration::from_secs(10))
            .build()?;

        let active_trades = Arc::new(Mutex::new(std::collections::HashMap::new()));

        Ok(Self {
            config,
            rpc_client,
            keypair,
            active_trades,
            http_client,
        })
    }

    async fn get_token_price(&self, token_address: &str) -> Result<f64> {
        let jupiter_price_url = format!(
            "https://price.jup.ag/v4/price?ids={}&vsToken=SOL",
            token_address
        );
        
        let price_response = self.http_client
            .get(&jupiter_price_url)
            .send()
            .await?
            .json::<serde_json::Value>()
            .await?;

        let price = price_response["data"][token_address]["price"]
            .as_f64()
            .ok_or_else(|| anyhow!("Price not found"))?;

        Ok(price)
    }

    async fn execute_trade(&self, token_address: &str) -> Result<()> {
        info!("🎯 Trade Opportunity Found!");
        debug!("💰 Snipe Amount: {}", self.config.trading.snipe_amount);
        debug!("⚡ Priority Fee: {}", self.config.trading.priority_fee);
    
        warn!("🚀 Executing Trade on: {}", token_address);
        println!("{}", "\n🎯 EXECUTING SNIPE".bright_green().bold());
        
        let jupiter_quote_url = format!(
            "https://quote-api.jup.ag/v4/quote?inputMint={}&outputMint={}&amount={}&slippageBps={}",
            WSOL_MINT,
            token_address,
            self.config.trading.snipe_amount,
            (self.config.trading.max_slippage * 100.0) as u64
        );
        
        debug!("🌐 Requesting Quote URL: {}", jupiter_quote_url);
    
        let response = self.http_client
            .get(&jupiter_quote_url)
            .send()
            .await?;
    
        debug!("📡 Response Status: {}", response.status());
    
        let text = response.text().await?;
        debug!("📝 Raw Response: {}", text);
    
        let quote: serde_json::Value = if !text.is_empty() {
            serde_json::from_str(&text)?
        } else {
            return Err(anyhow!("Empty response from Jupiter API"));
        };
    
        let route = quote["data"].as_array()
            .and_then(|arr| arr.first())
            .ok_or_else(|| anyhow!("No route found"))?;
    
        let swap_request = json!({
            "route": route,
            "userPublicKey": self.keypair.pubkey().to_string(),
            "wrapUnwrapSOL": true
        });
    
        let swap_response: serde_json::Value = self.http_client
            .post("https://quote-api.jup.ag/v4/swap")
            .json(&swap_request)
            .send()
            .await?
            .json()
            .await?;
    
        let swap_transaction = swap_response["swapTransaction"]
            .as_str()
            .ok_or_else(|| anyhow!("Swap transaction not found"))?;
    
        let decoded_transaction = base64_engine.decode(swap_transaction)?;
        let mut transaction: Transaction = bincode::deserialize(&decoded_transaction)?;
    
        let compute_limit_ix = ComputeBudgetInstruction::set_compute_unit_limit(200_000);
        let priority_fee_ix = ComputeBudgetInstruction::set_compute_unit_price(
            self.config.trading.priority_fee
        );
    
        let mut final_instructions = vec![compute_limit_ix, priority_fee_ix];
        let instructions_vec: Vec<Instruction> = transaction.message.instructions.iter()
            .map(|ix| {
                Instruction {
                    program_id: transaction.message.account_keys[ix.program_id_index as usize],
                    accounts: ix.accounts.iter().map(|&idx| {
                        let is_signer = transaction.message.header.num_required_signatures as usize > idx as usize;
                        let is_writable = transaction.message.is_writable(idx as usize);
                        
                        AccountMeta {
                            pubkey: transaction.message.account_keys[idx as usize],
                            is_signer,
                            is_writable,
                        }
                    }).collect(),
                    data: ix.data.clone(),
                }
            })
            .collect();
        
        final_instructions.extend(instructions_vec);
        
        let message = Message::new(&final_instructions, Some(&self.keypair.pubkey()));
        let blockhash = self.rpc_client.get_latest_blockhash()?;
        transaction = Transaction::new(&[&self.keypair], message, blockhash);
    
        println!("{}", "🚀 Sending transaction...".bright_yellow());
        let signature = self.rpc_client.send_and_confirm_transaction_with_spinner(&transaction)?;
        println!("{}", format!("✅ Transaction confirmed: {}", signature).bright_green());
    
        let buy_price = self.get_token_price(token_address).await?;
        let trade = Trade {
            token_address: token_address.to_string(),
            amount: self.config.trading.snipe_amount,
            buy_price,
            take_profit_price: buy_price * (1.0 + self.config.trading.profit_target_percent / 100.0),
            stop_loss_price: buy_price * (1.0 - self.config.trading.stop_loss_percent / 100.0),
            timestamp: chrono::Utc::now().timestamp() as u64,
        };
    
        self.active_trades.lock().await.insert(token_address.to_string(), trade);
        Ok(())
    }
    
    
    async fn monitor_trades(&self) -> Result<()> {
        debug!("🔍 Monitoring active trades");
        let trades = self.active_trades.lock().await;
        
        for (token_address, trade) in trades.iter() {
            let current_price = self.get_token_price(token_address).await?;
            info!("📊 Token: {}, Current Price: {}", token_address, current_price);
            let profit_percent = ((current_price - trade.buy_price) / trade.buy_price * 100.0).round();
    
            let status = if profit_percent >= 0.0 {
                format!("📈 +{}%", profit_percent).green()
            } else {
                format!("📉 {}%", profit_percent).red()
            };
    
            println!("{}", format!("\n💰 Token: {}", token_address).yellow());
            println!("   {}", status);
        }
    
        Ok(())
    }
    
    fn extract_token_info(&self, ws_message: &str) -> Option<(String, bool, bool, bool, u64)> {
        debug!("📥 Processing WebSocket message");
        if let Ok(value) = serde_json::from_str::<serde_json::Value>(ws_message) {
            if let Some(asset) = value["params"]["result"]["value"]["getAsset"].as_object() {
                info!("🎯 New token detected!");
                println!("{}", "\n🎯 TARGET ACQUIRED!".bright_green().bold());
                
                let token_info = (
                    asset["tokenAddress"].as_str()?.to_string(),
                    asset["mutable"].as_bool()?,
                    asset["burnt"].as_bool()?,
                    asset["frozen"].as_bool()?,
                    asset["supply"].as_str()?.parse().ok()?
                );
    
                println!("{}", format!("💰 Token: {}", token_info.0).yellow());
                println!("{}", format!("💲 Supply: {}", token_info.4).cyan());
                println!("{}", format!("🔒 Mutable: {}", token_info.1).blue());
                println!("{}", format!("🔥 Burnt: {}", token_info.2).red());
                println!("{}", format!("❄️ Frozen: {}", token_info.3).white());
    
                return Some(token_info);
            }
        }
        None
    }
    
    async fn manual_buy(&self, token_address: &str) -> Result<()> {
        info!("🎯 Manual Buy Initiated!");
        debug!("💰 Buying Token: {}", token_address);
        self.execute_trade(token_address).await?;
        Ok(())
    }
    async fn run(&self) -> Result<()> {
        println!("{}", "\n🎯 SNIPER ACTIVATED".bright_green().bold());
        println!("{}", "🔥 MARGA FAST MAINNET + HELIUS PRO".bright_yellow());
        
        let ws_url = url::Url::parse(&self.config.urls.ws_url)?;
        let mut tokens_scanned = 0;
        let mut last_count_time = Instant::now();
        let count_interval = Duration::from_secs(30);
        
        loop {
            match connect_async(&ws_url).await {
                Ok((ws_stream, _)) => {
                    let (mut write, mut read) = ws_stream.split();
                    
                    let subscribe_msg = json!({
                        "jsonrpc": "2.0",
                        "id": 1,
                        "method": "programSubscribe",
                        "params": [
                            RAYDIUM_AMM_PROGRAM_ID,
                            {"encoding": "jsonParsed", "commitment": "confirmed"}
                        ]
                    });
    
                    write.send(WsMessage::Text(subscribe_msg.to_string())).await?;
                    println!("{}", "👀 Scanning for new tokens...".bright_blue());
    
                    let mut interval = interval(Duration::from_secs(1));
    
                    loop {
                        tokio::select! {
                            Some(msg) = read.next() => {
                                match msg {
                                    Ok(WsMessage::Text(text)) => {
                                        debug!("📡 Received WebSocket message");
                                        // ADD THIS BLOCK RIGHT HERE
                                        if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&text) {
                                            debug!("📊 JSON Data: {}", serde_json::to_string_pretty(&parsed).unwrap());
                                        }
                                        tokens_scanned += 1;
                                        if let Some((token_address, mutable, burnt, frozen, supply)) = 
                                            self.extract_token_info(&text) 
                                        {
                                            debug!("🎯 Buy Check: Token {}", token_address);
                                            debug!("✨ Supply Check: {} >= {}", supply, self.config.token_requirements.min_supply);
                                            debug!("🔒 Security Checks: Not Mutable={}, Burnt={}, Not Frozen={}", 
                                            !mutable, burnt, !frozen);
                                            
                                            if !mutable && burnt && !frozen && supply >= self.config.token_requirements.min_supply {
                                                self.execute_trade(&token_address).await?;
                                            
                                            } else {
                                                debug!("❌ Buy skipped - Failed requirements");
                                            }
                                        }
                                    }
                                    Ok(WsMessage::Close(_)) => {
                                        println!("{}", "📡 WebSocket connection closed".bright_yellow());
                                        break;
                                    }
                                    Err(e) => {
                                        println!("{}", format!("⚠️ WebSocket error: {}", e).bright_red());
                                        break;
                                    }
                                    _ => {}
                                }
                            }
                            _ = interval.tick() => {
                                self.monitor_trades().await?;
                            }
                        }
                    }
                }
                Err(e) => {
                    println!("{}", format!("🔄 Connection error: {}. Retrying...", e).bright_yellow());
                    tokio::time::sleep(Duration::from_secs(
                        self.config.websocket.reconnect_interval
                    )).await;
                }
            }
        }
    }
}   
#[tokio::main]
async fn main() -> Result<()> {
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
    println!("{}", "=====================================".bright_blue());
    println!("{}", "🔫 SOLANA SNIPER ELITE v2.0 🎯".bright_green().bold());
    println!("{}", "=====================================".bright_blue());
    
    let bot = Arc::new(Bot::new().await?);
    let _bot_clone = Arc::clone(&bot);
    // In main()
    let token_to_buy = "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"; // BONK token address
    bot.manual_buy(token_to_buy).await?;
    
    // Then show balance and config
    let balance = bot.rpc_client.get_balance(&bot.keypair.pubkey())?;
    println!("{}", format!("💰 Balance: {} SOL", balance as f64 / 1e9).bright_yellow());

    ctrlc::set_handler(move || {
        println!("{}", "\n💾 SAVING STATE & SHUTTING DOWN...".bright_red().bold());
        std::process::exit(0);
})?;
    
    let balance = bot.rpc_client.get_balance(&bot.keypair.pubkey())?;
    println!("{}", format!("💰 Balance: {} SOL", balance as f64 / 1e9).bright_yellow());
        
    println!("{}", "\n🔧 Configuration:".bright_blue());
    println!("{}", format!("🎯 Snipe Amount: {} lamports", bot.config.trading.snipe_amount).yellow());
    println!("{}", format!("💫 Slippage: {}%", bot.config.trading.max_slippage).yellow());
    println!("{}", format!("📈 Take Profit: {}%", bot.config.trading.profit_target_percent).green());
    println!("{}", format!("📉 Stop Loss: {}%", bot.config.trading.stop_loss_percent).red());
    println!("{}", format!("⚡ Priority Fee: {}", bot.config.trading.priority_fee).yellow());
    
    bot.run().await?;
    Ok(())
}