fn get_swap_accounts(&self, pool: &PoolState, wsol_account: &str) -> Vec<AccountMeta> {
    vec![
        AccountMeta::new_readonly(
            Pubkey::from_str(&env::var("TOKEN_PROGRAM_ID").expect("TOKEN_PROGRAM_ID not set"))
                .expect("Invalid token program ID"),
            false,
        ),
        AccountMeta::new(Pubkey::from_str(&pool.amm_id).unwrap(), false),
        AccountMeta::new_readonly(Pubkey::from_str(&pool.authority).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.open_orders).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.target_orders).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.base_vault).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.quote_vault).unwrap(), false),
        AccountMeta::new_readonly(Pubkey::from_str(&pool.market).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.bids).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.asks).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.event_queue).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(wsol_account).unwrap(), false),
        AccountMeta::new(self.keypair.pubkey(), true),
    ]
}

fn create_swap_data(&self, amount_in: u64, min_amount_out: u64) -> Vec<u8> {
    let mut data = Vec::with_capacity(17);
    data.push(9); // Raydium swap instruction discriminator
    data.extend_from_slice(&amount_in.to_le_bytes());
    data.extend_from_slice(&min_amount_out.to_le_bytes());
    data
}
}

#[pymodule]
fn solana_rust_bot(_py: Python, m: &PyModule) -> PyResult<()> {
dotenv().ok();
env_logger::init_from_env(envuse base64::{engine::general_purpose::STANDARD as base64_engine, Engine};
use dashmap::DashMap;
use dotenv::dotenv;
use log::{debug, error, info};
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use reqwest::{header, Client};
use serde::{Deserialize, Serialize};
use solana_client::{rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig};
use solana_sdk::{
commitment_config::CommitmentConfig,
compute_budget::ComputeBudgetInstruction,
instruction::{AccountMeta, Instruction},
message::Message,
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
transaction::Transaction,
};
use std::{
collections::HashMap,
env,
str::FromStr,
sync::Arc,
time::{Duration, Instant},
};
use tokio::runtime::Runtime;
use tokio::sync::Mutex;

const LAMPORTS_PER_SOL: u64 = 1_000_000_000;
const MIN_PRIORITY_FEE: u64 = 500_000;
const PRIORITY_FEE_INTERVAL: Duration = Duration::from_secs(30);
const SOLANA_CLI_VERSION: &str = "solana-cli/1.18.23 (src:aeb3a2e1; feat:4215500110, client:SolanaLabs)";

#[derive(Debug, Serialize, Deserialize)]
struct PriorityFeeResponse {
jsonrpc: String,
result: PriorityFeeResult,
id: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct PriorityFeeResult {
priority_fee_estimate: Option<f64>,
}

#[pyclass]
#[derive(Clone, Debug)]
pub struct PoolState {
#[pyo3(get)]
pub amm_id: String,
#[pyo3(get)]
pub authority: String,
#[pyo3(get)]
pub open_orders: String,
#[pyo3(get)]
pub target_orders: String,
#[pyo3(get)]
pub base_vault: String,
#[pyo3(get)]
pub quote_vault: String,
#[pyo3(get)]
pub market: String,
#[pyo3(get)]
pub bids: String,
#[pyo3(get)]
pub asks: String,
#[pyo3(get)]
pub event_queue: String,
#[pyo3(get)]
pub vault_signer: String,
}

#[pyclass]
#[derive(Deserialize, Clone, Debug)]
pub struct TradeConfig {
#[pyo3(get, set)]
pub compute_limit: u32,
#[pyo3(get, set)]
pub priority_fee: u64,
#[pyo3(get, set)]
pub skip_preflight: bool,
#[pyo3(get, set)]
pub max_retries: u32,
#[pyo3(get, set)]
pub commitment: String,
}

#[pymethods]
impl TradeConfig {
#[new]
fn new() -> Self {
    Self {
        compute_limit: 800000,
        priority_fee: MIN_PRIORITY_FEE,
        skip_preflight: true,
        max_retries: 4,
        commitment: "confirmed".to_string(),
    }
}
}

#[pyclass]
#[derive(Deserialize, Clone, Debug)]
pub struct TradeParams {
#[pyo3(get, set)]
pub pool_address: String,
#[pyo3(get, set)]
pub amount_in: f64,
#[pyo3(get, set)]
pub min_amount_out: f64,
#[pyo3(get, set)]
pub wsol_account: String,
}

#[pymethods]
impl TradeParams {
#[new]
fn new(pool_address: String, amount_in: f64, min_amount_out: f64, wsol_account: String) -> Self {
    Self {
        pool_address,
        amount_in,
        min_amount_out,
        wsol_account,
    }
}
}

#[pyclass]
pub struct TradingCore {
client: Arc<RpcClient>,
keypair: Arc<Keypair>,
pool_cache: Arc<DashMap<String, PoolState>>,
http_client: Arc<Client>,
config: Arc<Mutex<TradeConfig>>,
last_priority_update: Arc<Mutex<Instant>>,
runtime: Arc<Runtime>,
}

#[pymethods]
impl TradingCore {
#[new]
#[pyo3(signature = (private_key_bytes))]
fn new(private_key_bytes: &[u8]) -> PyResult<Self> {
    info!("Initializing TradingCore");
    dotenv().ok();

    let runtime = Arc::new(Runtime::new().unwrap());

    let keypair = match Keypair::from_bytes(private_key_bytes) {
        Ok(keypair) => {
            info!("Created keypair with pubkey: {}", keypair.pubkey());
            Arc::new(keypair)
        }
        Err(e) => {
            error!("Failed to create keypair: {}", e);
            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
                "Invalid keypair bytes",
            ));
        }
    };

    let rpc_url = env::var("RPC_URL").expect("RPC_URL not set");
    info!("Connecting to RPC: {}", rpc_url);
    let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());

    let http_client = Client::builder()
        .timeout(Duration::from_secs(30))
        .default_headers({
            let mut headers = header::HeaderMap::new();
            headers.insert(
                "User-Agent",
                header::HeaderValue::from_static(SOLANA_CLI_VERSION),
            );
            headers.insert(
                "Content-Type",
                header::HeaderValue::from_static("application/json"),
            );
            headers
        })
        .build()
        .map_err(|e| {
            error!("Failed to create HTTP client: {}", e);
            PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string())
        })?;

    Ok(Self {
        client: Arc::new(client),
        keypair,
        pool_cache: Arc::new(DashMap::new()),
        http_client: Arc::new(http_client),
        config: Arc::new(Mutex::new(TradeConfig::new())),
        last_priority_update: Arc::new(Mutex::new(Instant::now())),
        runtime,
    })
}

fn get_wsol_balance(&self, token_account: String) -> PyResult<f64> {
    let wsol_pubkey = match Pubkey::from_str(&token_account) {
        Ok(pubkey) => pubkey,
        Err(_) => {
            error!("Invalid WSOL account provided: {}", token_account);
            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
                "Invalid WSOL account",
            ));
        }
    };

    match self.client.get_token_account_balance(&wsol_pubkey) {
        Ok(balance) => {
            let amount = balance.ui_amount.unwrap_or(0.0);
            info!("Retrieved WSOL balance for account {}: {}", token_account, amount);
            Ok(amount)
        }
        Err(e) => {
            error!(
                "Failed to get WSOL balance for account {}: {}",
                token_account, e
            );
            Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
                e.to_string(),
            ))
        }
    }
}

fn get_wallet_balance(&self) -> PyResult<f64> {
    match self.client.get_balance(&self.keypair.pubkey()) {
        Ok(balance) => {
            let sol_balance = balance as f64 / LAMPORTS_PER_SOL as f64;
            info!("Retrieved wallet balance: {} SOL", sol_balance);
            Ok(sol_balance)
        }
        Err(e) => {
            error!("Failed to get wallet balance: {}", e);
            Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
                e.to_string(),
            ))
        }
    }
}

fn update_config(&self, config: TradeConfig) -> PyResult<()> {
    let runtime = self.runtime.clone();
    runtime.block_on(async {
        *self.config.lock().await = config;
    });
    Ok(())
}

fn execute_trade(&self, params: TradeParams) -> PyResult<Option<String>> {
    let runtime = self.runtime.clone();
    runtime.block_on(async {
        let config = self.config.lock().await;
        let pool = match self.pool_cache.get(&params.pool_address) {
            Some(pool) => pool,
            None => {
                error!("Pool not found: {}", params.pool_address);
                return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("Pool not found"));
            }
        };

        let priority_fee = self.get_helius_priority_fee().await?;

        let mut instructions = vec![
            ComputeBudgetInstruction::set_compute_unit_limit(config.compute_limit),
            ComputeBudgetInstruction::set_compute_unit_price(priority_fee),
        ];

        let amount_in = (params.amount_in * LAMPORTS_PER_SOL as f64) as u64;
        let min_amount_out = (params.min_amount_out * LAMPORTS_PER_SOL as f64) as u64;

        instructions.push(Instruction {
            program_id: Pubkey::from_str(&env::var("RAYDIUM_PROGRAM_ID").expect("RAYDIUM_PROGRAM_ID not set"))
                .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?,
            accounts: self.get_swap_accounts(&pool, &params.wsol_account),
            data: self.create_swap_data(amount_in, min_amount_out),
        });

        let blockhash = self.client.get_latest_blockhash()
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;

        let message = Message::new(&instructions, Some(&self.keypair.pubkey()));
        let mut transaction = Transaction::new_unsigned(message);
        transaction.sign(&[&*self.keypair], blockhash);

        match self.client.send_and_confirm_transaction_with_spinner_and_config(
            &transaction,
            CommitmentConfig::from_str(&config.commitment).unwrap(),
            RpcSendTransactionConfig {
                skip_preflight: config.skip_preflight,
                preflight_commitment: None,
                encoding: None,
                max_retries: Some(config.max_retries as usize),
                min_context_slot: None,
            },
        ) {
            Ok(signature) => {
                info!("Transaction successful: {}", signature);
                Ok(Some(signature.to_string()))
            }
            Err(e) => {
                error!("Transaction failed: {}", e);
                Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))
            }
        }
    })
}

fn update_pool(&self, pool_data: HashMap<String, String>) -> PyResult<()> {
    info!("Updating pool state with data: {:?}", pool_data);

    let pool_state = PoolState {
        amm_id: pool_data["amm_id"].clone(),
        authority: pool_data["amm_authority"].clone(),
        open_orders: pool_data["amm_open_orders"].clone(),
        target_orders: pool_data["amm_target"].clone(),
        base_vault: pool_data["pool_coin_token_account"].clone(),
        quote_vault: pool_data["pool_pc_token_account"].clone(),
        market: pool_data["serum_market"].clone(),
        bids: pool_data["serum_bids"].clone(),
        asks: pool_data["serum_asks"].clone(),
        event_queue: pool_data["serum_events"].clone(),
        vault_signer: pool_data["vault_signer"].clone(),
    };

    self.pool_cache.insert(pool_data["amm_id"].clone(), pool_state);
    info!("Pool state updated successfully");
    Ok(())
}
}

impl TradingCore {
async fn get_helius_priority_fee(&self) -> PyResult<u64> {
    let mut last_update = self.last_priority_update.lock().await;
    let now = Instant::now();

    if now.duration_since(*last_update) < PRIORITY_FEE_INTERVAL {
        debug!(
            "Using cached priority fee: {}",
            self.config.lock().await.priority_fee
        );
        return Ok(self.config.lock().await.priority_fee);
    }

    let helius_url = env::var("RPC_URL").expect("RPC_URL not set");
    let request = serde_json::json!({
        "jsonrpc": "2.0",
        "id": "helius-priority-fee",
        "method": "getPriorityFeeEstimate",
        "params": [{
            "accountKeys": [env::var("RAYDIUM_PROGRAM_ID").expect("RAYDIUM_PROGRAM_ID not set")],
            "options": {
                "includeAllPriorityFeeLevels": false,
                "priorityLevel": "High"
            }
        }]
    });

    let response = self
        .http_client
        .post(&helius_url)
        .json(&request)
        .send()
        .await
        .map_err(|e| {
            error!("Priority fee request failed: {}", e);
            PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string())
        })?;

    let fee_response: PriorityFeeResponse = response.json().await.map_err(|e| {
        error!("Failed to parse priority fee response: {}", e);
        PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string())
    })?;

    let fee = fee_response.result.priority_fee_estimate.unwrap_or(10000.0);
    *last_update = now;

    info!("Retrieved priority fee: {}", fee);
    Ok((fee.max(MIN_PRIORITY_FEE as f64)) as u64)
}

fn get_swap_accounts(&self, pool: &PoolState, wsol_account: &str) -> Vec<AccountMeta> {
    vec![
        AccountMeta::new_readonly(
            Pubkey::from_str(&env::var("TOKEN_PROGRAM_ID").expect("TOKEN_PROGRAM_ID not set"))
                .expect("Invalid token program ID"),
            false,
        ),
        AccountMeta::new(Pubkey::from_str(&pool.amm_id).unwrap(), false),
        AccountMeta::new_readonly(Pubkey::from_str(&pool.authority).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.open_orders).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.target_orders).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.base_vault).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.quote_vault).unwrap(), false),
        AccountMeta::new_readonly(Pubkey::from_str(&pool.market).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.bids).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.asks).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(&pool.event_queue).unwrap(), false),
        AccountMeta::new(Pubkey::from_str(wsol_account).unwrap(), false),
        AccountMeta::new(self.keypair.pubkey(), true),
    ]
}

fn create_swap_data(&self, amount_in: u64, min_amount_out: u64) -> Vec<u8> {
    let mut data = Vec::with_capacity(17);
    data.push(9); // Raydium swap instruction discriminator
    data.extend_from_slice(&amount_in.to_le_bytes());
    data.extend_from_slice(&min_amount_out.to_le_bytes());
    data
}
}

#[pymodule]
fn solana_rust_bot(_py: Python, m: &PyModule) -> PyResult<()> {
dotenv().ok();
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));

m.add_class::<TradingCore>()?;
m.add_class::<TradeConfig>()?;
m.add_class::<TradeParams>()?;
m.add_class::<PoolState>()?;

info!("solana_rust_bot module loaded successfully");
Ok(())
} rewrite word for word just fix the spacing indentation issues