use anyhow::Result;
use swap_kit_types::{SimulateRequest, SimulateResponse};
pub async fn simulate(req: &SimulateRequest) -> Result<SimulateResponse> {
let from_amount: u128 = req.from_amount.parse().unwrap_or(0);
let amount_out: u128 = req.amount_out.parse().unwrap_or(0);
let slippage_bps: u64 = req.slippage_bps as u64;
let trade_size_eth = from_amount as f64 / 1e18;
let is_mainnet = req.chain_id == 1;
let sandwich_risk = classify_risk(trade_size_eth, slippage_bps, is_mainnet);
let mev_fraction = (slippage_bps * 70) / 10000; let estimated_mev = (amount_out as u128)
.checked_mul(mev_fraction as u128)
.unwrap_or(0)
/ 10000;
let recommended_slippage = if sandwich_risk == "high" {
std::cmp::min(slippage_bps as u32, 30)
} else if sandwich_risk == "medium" {
std::cmp::min(slippage_bps as u32, 50)
} else {
slippage_bps as u32
};
Ok(SimulateResponse {
sandwich_risk: sandwich_risk.to_string(),
estimated_mev_wei: estimated_mev.to_string(),
recommended_slippage_bps: recommended_slippage,
detected_bots: vec![], })
}
pub fn safe_default() -> SimulateResponse {
SimulateResponse {
sandwich_risk: "low".to_string(),
estimated_mev_wei: "0".to_string(),
recommended_slippage_bps: 50,
detected_bots: vec![],
}
}
fn classify_risk(trade_size_eth: f64, slippage_bps: u64, is_mainnet: bool) -> &'static str {
let risk_multiplier = if is_mainnet { 1.0 } else { 0.5 };
let risk_score = trade_size_eth * (slippage_bps as f64) * risk_multiplier;
if risk_score > 5000.0 {
"high"
} else if risk_score > 500.0 {
"medium"
} else if risk_score > 50.0 {
"low"
} else {
"none"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_simulate_low_risk() {
let req = SimulateRequest {
from_token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".to_string(),
to_token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".to_string(),
from_amount: "100000000000000000".to_string(), chain_id: 1,
protocol: "uniswap-v4".to_string(),
amount_out: "200000000".to_string(), slippage_bps: 50,
};
let result = simulate(&req).await.unwrap();
assert!(result.sandwich_risk == "none" || result.sandwich_risk == "low");
}
#[tokio::test]
async fn test_simulate_high_risk() {
let req = SimulateRequest {
from_token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".to_string(),
to_token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".to_string(),
from_amount: "100000000000000000000".to_string(), chain_id: 1,
protocol: "uniswap-v4".to_string(),
amount_out: "200000000000".to_string(), slippage_bps: 200,
};
let result = simulate(&req).await.unwrap();
assert_eq!(result.sandwich_risk, "high");
assert!(result.recommended_slippage_bps <= 30);
}
#[test]
fn test_safe_default() {
let result = safe_default();
assert_eq!(result.sandwich_risk, "low");
assert_eq!(result.estimated_mev_wei, "0");
}
}