use crate::error::CoreError;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MitigationStrategy {
BundleSubmission,
FairOrdering,
MevRebate,
PrivateExecution,
TimeDelayed,
ThresholdEncryption,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MevRebateConfig {
pub rebate_percentage: Decimal,
pub min_rebate_amount: Decimal,
pub processing_delay: Duration,
}
impl Default for MevRebateConfig {
fn default() -> Self {
Self {
rebate_percentage: Decimal::new(50, 0), min_rebate_amount: Decimal::new(1, 3), processing_delay: Duration::from_secs(3600), }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MevRebate {
pub rebate_id: String,
pub victim_user_id: String,
pub attacker_user_id: String,
pub mev_amount: Decimal,
pub rebate_amount: Decimal,
pub transaction_ids: Vec<String>,
pub created_at: SystemTime,
pub processed_at: Option<SystemTime>,
pub status: RebateStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RebateStatus {
Pending,
Processing,
Completed,
Failed,
}
pub struct MevRebateManager {
config: MevRebateConfig,
pending_rebates: HashMap<String, MevRebate>,
rebate_counter: u64,
}
impl MevRebateManager {
pub fn new(config: MevRebateConfig) -> Self {
Self {
config,
pending_rebates: HashMap::new(),
rebate_counter: 0,
}
}
pub fn with_defaults() -> Self {
Self::new(MevRebateConfig::default())
}
pub fn create_rebate(
&mut self,
victim_user_id: String,
attacker_user_id: String,
mev_amount: Decimal,
transaction_ids: Vec<String>,
) -> Result<MevRebate, CoreError> {
if mev_amount < self.config.min_rebate_amount {
return Err(CoreError::Validation(
"MEV amount below minimum threshold".to_string(),
));
}
let rebate_amount = mev_amount * self.config.rebate_percentage / Decimal::new(100, 0);
self.rebate_counter += 1;
let rebate_id = format!("rebate_{}", self.rebate_counter);
let rebate = MevRebate {
rebate_id: rebate_id.clone(),
victim_user_id,
attacker_user_id,
mev_amount,
rebate_amount,
transaction_ids,
created_at: SystemTime::now(),
processed_at: None,
status: RebateStatus::Pending,
};
self.pending_rebates.insert(rebate_id, rebate.clone());
Ok(rebate)
}
pub fn process_pending_rebates(&mut self) -> Vec<MevRebate> {
let now = SystemTime::now();
let mut processed = Vec::new();
for rebate in self.pending_rebates.values_mut() {
if rebate.status != RebateStatus::Pending {
continue;
}
let elapsed = now
.duration_since(rebate.created_at)
.unwrap_or(Duration::ZERO);
if elapsed >= self.config.processing_delay {
rebate.status = RebateStatus::Processing;
processed.push(rebate.clone());
}
}
processed
}
pub fn complete_rebate(&mut self, rebate_id: &str) -> Result<(), CoreError> {
let rebate = self
.pending_rebates
.get_mut(rebate_id)
.ok_or_else(|| CoreError::NotFound(format!("Rebate {} not found", rebate_id)))?;
rebate.status = RebateStatus::Completed;
rebate.processed_at = Some(SystemTime::now());
Ok(())
}
pub fn fail_rebate(&mut self, rebate_id: &str) -> Result<(), CoreError> {
let rebate = self
.pending_rebates
.get_mut(rebate_id)
.ok_or_else(|| CoreError::NotFound(format!("Rebate {} not found", rebate_id)))?;
rebate.status = RebateStatus::Failed;
Ok(())
}
pub fn get_victim_rebates(&self, user_id: &str) -> Vec<&MevRebate> {
self.pending_rebates
.values()
.filter(|r| r.victim_user_id == user_id)
.collect()
}
pub fn get_total_rebate_amount(&self, user_id: &str) -> Decimal {
self.pending_rebates
.values()
.filter(|r| r.victim_user_id == user_id && r.status == RebateStatus::Completed)
.map(|r| r.rebate_amount)
.sum()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionBundle {
pub bundle_id: String,
pub transactions: Vec<BundledTransaction>,
pub min_timestamp: Option<SystemTime>,
pub max_timestamp: Option<SystemTime>,
pub revert_on_failure: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BundledTransaction {
pub tx_id: String,
pub user_id: String,
pub order_index: usize,
pub data: Vec<u8>,
}
impl TransactionBundle {
pub fn new(bundle_id: String) -> Self {
Self {
bundle_id,
transactions: Vec::new(),
min_timestamp: None,
max_timestamp: None,
revert_on_failure: true,
}
}
pub fn add_transaction(&mut self, tx: BundledTransaction) -> Result<(), CoreError> {
let expected_index = self.transactions.len();
if tx.order_index != expected_index {
return Err(CoreError::Validation(format!(
"Invalid order index. Expected {}, got {}",
expected_index, tx.order_index
)));
}
self.transactions.push(tx);
Ok(())
}
pub fn validate(&self) -> Result<(), CoreError> {
if self.transactions.is_empty() {
return Err(CoreError::Validation("Bundle cannot be empty".to_string()));
}
if let (Some(min), Some(max)) = (self.min_timestamp, self.max_timestamp) {
if min > max {
return Err(CoreError::Validation(
"min_timestamp cannot be greater than max_timestamp".to_string(),
));
}
}
Ok(())
}
pub fn with_time_constraints(
mut self,
min_timestamp: SystemTime,
max_timestamp: SystemTime,
) -> Self {
self.min_timestamp = Some(min_timestamp);
self.max_timestamp = Some(max_timestamp);
self
}
}
#[derive(Debug)]
pub struct FairOrderingManager {
pending_queue: VecDeque<PendingTransaction>,
batch_interval: Duration,
last_batch_time: SystemTime,
}
#[derive(Debug, Clone)]
struct PendingTransaction {
tx_id: String,
#[allow(dead_code)]
user_id: String,
received_at: SystemTime,
#[allow(dead_code)]
priority_score: Decimal,
}
impl FairOrderingManager {
pub fn new(batch_interval: Duration) -> Self {
Self {
pending_queue: VecDeque::new(),
batch_interval,
last_batch_time: SystemTime::now(),
}
}
pub fn with_defaults() -> Self {
Self::new(Duration::from_secs(10)) }
pub fn submit_transaction(&mut self, tx_id: String, user_id: String) -> Result<(), CoreError> {
let tx = PendingTransaction {
tx_id,
user_id,
received_at: SystemTime::now(),
priority_score: Decimal::ZERO,
};
self.pending_queue.push_back(tx);
Ok(())
}
pub fn process_batch(&mut self) -> Result<Vec<String>, CoreError> {
let now = SystemTime::now();
let elapsed = now
.duration_since(self.last_batch_time)
.unwrap_or(Duration::ZERO);
if elapsed < self.batch_interval {
return Ok(Vec::new()); }
let mut batch = Vec::new();
while let Some(tx) = self.pending_queue.pop_front() {
batch.push(tx);
}
if batch.is_empty() {
return Ok(Vec::new());
}
batch.sort_by(|a, b| a.received_at.cmp(&b.received_at));
let ordered_tx_ids: Vec<String> = batch.iter().map(|tx| tx.tx_id.clone()).collect();
self.last_batch_time = now;
Ok(ordered_tx_ids)
}
pub fn pending_count(&self) -> usize {
self.pending_queue.len()
}
pub fn time_until_next_batch(&self) -> Duration {
let elapsed = SystemTime::now()
.duration_since(self.last_batch_time)
.unwrap_or(Duration::ZERO);
self.batch_interval.saturating_sub(elapsed)
}
}
#[derive(Debug)]
pub struct PrivateExecutionEngine {
encrypted_pool: HashMap<String, EncryptedTransaction>,
}
#[derive(Debug, Clone)]
struct EncryptedTransaction {
#[allow(dead_code)]
tx_id: String,
encrypted_data: Vec<u8>,
#[allow(dead_code)]
submitted_at: SystemTime,
#[allow(dead_code)]
decryption_key_hash: String,
}
impl PrivateExecutionEngine {
pub fn new() -> Self {
Self {
encrypted_pool: HashMap::new(),
}
}
pub fn submit_encrypted_transaction(
&mut self,
tx_id: String,
encrypted_data: Vec<u8>,
decryption_key_hash: String,
) -> Result<(), CoreError> {
let encrypted_tx = EncryptedTransaction {
tx_id: tx_id.clone(),
encrypted_data,
submitted_at: SystemTime::now(),
decryption_key_hash,
};
self.encrypted_pool.insert(tx_id, encrypted_tx);
Ok(())
}
pub fn decrypt_and_execute(
&mut self,
tx_id: &str,
_decryption_key: &[u8],
) -> Result<Vec<u8>, CoreError> {
let encrypted_tx = self
.encrypted_pool
.remove(tx_id)
.ok_or_else(|| CoreError::NotFound(format!("Transaction {} not found", tx_id)))?;
Ok(encrypted_tx.encrypted_data)
}
pub fn pool_size(&self) -> usize {
self.encrypted_pool.len()
}
}
impl Default for PrivateExecutionEngine {
fn default() -> Self {
Self::new()
}
}
pub struct MevProtectionCoordinator {
rebate_manager: MevRebateManager,
fair_ordering: FairOrderingManager,
private_execution: PrivateExecutionEngine,
enabled_strategies: Vec<MitigationStrategy>,
}
impl MevProtectionCoordinator {
pub fn new(enabled_strategies: Vec<MitigationStrategy>) -> Self {
Self {
rebate_manager: MevRebateManager::with_defaults(),
fair_ordering: FairOrderingManager::with_defaults(),
private_execution: PrivateExecutionEngine::new(),
enabled_strategies,
}
}
pub fn with_all_strategies() -> Self {
Self::new(vec![
MitigationStrategy::FairOrdering,
MitigationStrategy::MevRebate,
MitigationStrategy::PrivateExecution,
])
}
pub fn is_strategy_enabled(&self, strategy: MitigationStrategy) -> bool {
self.enabled_strategies.contains(&strategy)
}
pub fn rebate_manager(&mut self) -> &mut MevRebateManager {
&mut self.rebate_manager
}
pub fn fair_ordering(&mut self) -> &mut FairOrderingManager {
&mut self.fair_ordering
}
pub fn private_execution(&mut self) -> &mut PrivateExecutionEngine {
&mut self.private_execution
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mev_rebate_creation() {
let mut manager = MevRebateManager::with_defaults();
let rebate = manager
.create_rebate(
"victim".to_string(),
"attacker".to_string(),
Decimal::new(100, 2),
vec!["tx1".to_string(), "tx2".to_string()],
)
.unwrap();
assert_eq!(rebate.victim_user_id, "victim");
assert_eq!(rebate.rebate_amount, Decimal::new(50, 2)); assert_eq!(rebate.status, RebateStatus::Pending);
}
#[test]
fn test_rebate_below_minimum() {
let mut manager = MevRebateManager::with_defaults();
let result = manager.create_rebate(
"victim".to_string(),
"attacker".to_string(),
Decimal::new(1, 4), vec!["tx1".to_string()],
);
assert!(result.is_err());
}
#[test]
fn test_transaction_bundle() {
let mut bundle = TransactionBundle::new("bundle1".to_string());
let tx1 = BundledTransaction {
tx_id: "tx1".to_string(),
user_id: "user1".to_string(),
order_index: 0,
data: vec![1, 2, 3],
};
let tx2 = BundledTransaction {
tx_id: "tx2".to_string(),
user_id: "user2".to_string(),
order_index: 1,
data: vec![4, 5, 6],
};
assert!(bundle.add_transaction(tx1).is_ok());
assert!(bundle.add_transaction(tx2).is_ok());
assert!(bundle.validate().is_ok());
assert_eq!(bundle.transactions.len(), 2);
}
#[test]
fn test_fair_ordering() {
let mut manager = FairOrderingManager::new(Duration::from_millis(100));
manager
.submit_transaction("tx1".to_string(), "user1".to_string())
.unwrap();
std::thread::sleep(Duration::from_millis(50));
manager
.submit_transaction("tx2".to_string(), "user2".to_string())
.unwrap();
std::thread::sleep(Duration::from_millis(100));
let ordered = manager.process_batch().unwrap();
assert_eq!(ordered.len(), 2);
assert_eq!(ordered[0], "tx1"); assert_eq!(ordered[1], "tx2");
}
#[test]
fn test_private_execution_engine() {
let mut engine = PrivateExecutionEngine::new();
let encrypted_data = vec![1, 2, 3, 4, 5];
engine
.submit_encrypted_transaction(
"tx1".to_string(),
encrypted_data.clone(),
"key_hash".to_string(),
)
.unwrap();
assert_eq!(engine.pool_size(), 1);
let decrypted = engine.decrypt_and_execute("tx1", b"key").unwrap();
assert_eq!(decrypted, encrypted_data);
assert_eq!(engine.pool_size(), 0);
}
#[test]
fn test_mev_protection_coordinator() {
let coordinator = MevProtectionCoordinator::with_all_strategies();
assert!(coordinator.is_strategy_enabled(MitigationStrategy::FairOrdering));
assert!(coordinator.is_strategy_enabled(MitigationStrategy::MevRebate));
assert!(coordinator.is_strategy_enabled(MitigationStrategy::PrivateExecution));
}
#[test]
fn test_rebate_completion() {
let mut manager = MevRebateManager::with_defaults();
let rebate = manager
.create_rebate(
"victim".to_string(),
"attacker".to_string(),
Decimal::new(100, 2),
vec!["tx1".to_string()],
)
.unwrap();
manager.complete_rebate(&rebate.rebate_id).unwrap();
let victim_rebates = manager.get_victim_rebates("victim");
assert_eq!(victim_rebates[0].status, RebateStatus::Completed);
}
}