Skip to content

Security Considerations

Overview

Moby Market implements multiple layers of security to protect against various attack vectors common in DeFi and trading systems. This document outlines the security architecture, threat model, and mitigation strategies.

Threat Model

Primary Threats

  1. Economic Attacks
  2. Flash loan attacks
  3. Oracle manipulation
  4. MEV (Maximal Extractable Value) exploitation
  5. Market manipulation

  6. Smart Contract Vulnerabilities

  7. Reentrancy attacks
  8. Integer overflow/underflow
  9. Access control bypass
  10. Logic errors in algorithms

  11. Privacy Attacks

  12. Transaction linking
  13. Timing analysis
  14. Side-channel attacks on ZK proofs
  15. Anonymity set reduction

  16. Infrastructure Attacks

  17. Front-running
  18. Sandwich attacks
  19. Denial of service
  20. Sybil attacks

Security Architecture

Defense in Depth

┌─────────────────────────────────────────┐
│          Application Layer              │
│  • Input Validation                     │
│  • Authentication & Authorization       │
│  • Rate Limiting                        │
├─────────────────────────────────────────┤
│          Protocol Layer                 │
│  • Access Control                       │
│  • State Validation                     │
│  • Economic Incentives                  │
├─────────────────────────────────────────┤
│          Cryptographic Layer            │
│  • ZK Proof Verification               │
│  • Signature Validation                │
│  • Commitment Schemes                   │
├─────────────────────────────────────────┤
│          Infrastructure Layer           │
│  • Oracle Security                      │
│  • Network Security                     │
│  • Hardware Security                    │
└─────────────────────────────────────────┘

Smart Contract Security

Access Control

#[derive(Accounts)]
pub struct RestrictedOperation<'info> {
    #[account(mut, has_one = authority)]
    pub controller_state: Account<'info, ControllerState>,
    pub authority: Signer<'info>,
}

impl<'info> RestrictedOperation<'info> {
    fn validate_authority(&self) -> Result<()> {
        // Multi-layer authority validation
        require!(
            self.controller_state.authority == self.authority.key(),
            ErrorCode::UnauthorizedAccess
        );

        // Check if system is paused
        require!(
            !self.controller_state.paused,
            ErrorCode::SystemPaused
        );

        Ok(())
    }
}

Reentrancy Protection

pub struct ReentrancyGuard {
    pub locked: bool,
}

impl ReentrancyGuard {
    pub fn acquire_lock(&mut self) -> Result<()> {
        require!(!self.locked, ErrorCode::ReentrancyDetected);
        self.locked = true;
        Ok(())
    }

    pub fn release_lock(&mut self) {
        self.locked = false;
    }
}

#[derive(Accounts)]
pub struct ProtectedOperation<'info> {
    #[account(mut)]
    pub guard: Account<'info, ReentrancyGuard>,
}

pub fn protected_operation(ctx: Context<ProtectedOperation>) -> Result<()> {
    // Acquire reentrancy lock
    ctx.accounts.guard.acquire_lock()?;

    // Perform operations
    execute_critical_logic()?;

    // Release lock
    ctx.accounts.guard.release_lock();

    Ok(())
}

Integer Safety

use anchor_lang::prelude::*;
use checked_math::CheckedMath;

pub fn safe_arithmetic_operations(
    amount_a: u64,
    amount_b: u64,
    multiplier: u64,
) -> Result<u64> {
    // Use checked arithmetic to prevent overflow
    let sum = amount_a
        .checked_add(amount_b)
        .ok_or(ErrorCode::MathOverflow)?;

    let result = sum
        .checked_mul(multiplier)
        .ok_or(ErrorCode::MathOverflow)?
        .checked_div(1000000) // Assuming 6 decimal precision
        .ok_or(ErrorCode::MathUnderflow)?;

    Ok(result)
}

// Custom error types
#[error_code]
pub enum ErrorCode {
    #[msg("Mathematical overflow detected")]
    MathOverflow,
    #[msg("Mathematical underflow detected")]
    MathUnderflow,
    #[msg("Division by zero")]
    DivisionByZero,
}

State Validation

pub fn validate_order_state(order: &OTCOrder) -> Result<()> {
    // Validate order amounts
    require!(order.amount_a > 0, ErrorCode::InvalidAmount);
    require!(order.amount_b > 0, ErrorCode::InvalidAmount);

    // Validate expiry
    let current_time = Clock::get()?.unix_timestamp;
    require!(order.expiry > current_time, ErrorCode::OrderExpired);

    // Validate token mints
    require!(
        order.token_mint_a != order.token_mint_b,
        ErrorCode::SameTokenTrade
    );

    // Validate partial fill configuration
    if order.partial_fill_allowed {
        require!(
            order.min_fill_size <= order.amount_a,
            ErrorCode::InvalidMinFillSize
        );
    }

    Ok(())
}

MEV Protection

Commit-Reveal Scheme

pub struct CommitRevealOrder {
    pub commitment: [u8; 32],      // Hash of order details + secret
    pub reveal_deadline: i64,      // When commitment must be revealed
    pub execution_deadline: i64,   // When order expires
    pub committed: bool,
    pub revealed: bool,
}

impl CommitRevealOrder {
    pub fn commit_order(
        &mut self,
        order_hash: [u8; 32],
        secret: [u8; 32],
        reveal_window: i64,
    ) -> Result<()> {
        let current_time = Clock::get()?.unix_timestamp;

        // Create commitment
        let mut hasher = Hasher::new();
        hasher.update(&order_hash);
        hasher.update(&secret);
        self.commitment = hasher.finalize().into();

        // Set deadlines
        self.reveal_deadline = current_time + reveal_window;
        self.execution_deadline = self.reveal_deadline + reveal_window;
        self.committed = true;

        Ok(())
    }

    pub fn reveal_order(
        &mut self,
        order_details: &OrderDetails,
        secret: [u8; 32],
    ) -> Result<()> {
        require!(self.committed, ErrorCode::OrderNotCommitted);
        require!(!self.revealed, ErrorCode::OrderAlreadyRevealed);

        let current_time = Clock::get()?.unix_timestamp;
        require!(
            current_time <= self.reveal_deadline,
            ErrorCode::RevealDeadlinePassed
        );

        // Verify commitment
        let order_hash = hash_order_details(order_details);
        let mut hasher = Hasher::new();
        hasher.update(&order_hash);
        hasher.update(&secret);
        let computed_commitment: [u8; 32] = hasher.finalize().into();

        require!(
            computed_commitment == self.commitment,
            ErrorCode::InvalidReveal
        );

        self.revealed = true;
        Ok(())
    }
}

Private Mempool Integration

pub struct PrivateMempoolConfig {
    pub flashbots_relay: bool,
    pub jito_bundle_mode: bool,
    pub custom_relayers: Vec<Pubkey>,
    pub min_tip_lamports: u64,
}

pub fn submit_private_transaction(
    transaction: Transaction,
    config: &PrivateMempoolConfig,
) -> Result<()> {
    if config.jito_bundle_mode {
        submit_jito_bundle(transaction, config.min_tip_lamports)?;
    } else if config.flashbots_relay {
        submit_flashbots_bundle(transaction)?;
    } else {
        // Use custom relayers
        submit_to_custom_relayers(transaction, &config.custom_relayers)?;
    }

    Ok(())
}

Oracle Security

Multi-Oracle Aggregation

pub struct OracleAggregator {
    pub pyth_feed: Option<Pubkey>,
    pub switchboard_feed: Option<Pubkey>,
    pub chainlink_feed: Option<Pubkey>,
    pub custom_feeds: Vec<Pubkey>,
    pub aggregation_method: AggregationMethod,
    pub deviation_threshold: u16,  // Basis points
    pub staleness_threshold: i64,  // Seconds
}

impl OracleAggregator {
    pub fn get_secure_price(&self, token: &Pubkey) -> Result<Price> {
        let mut prices = Vec::new();
        let current_time = Clock::get()?.unix_timestamp;

        // Collect prices from all sources
        if let Some(pyth_feed) = self.pyth_feed {
            if let Ok(price) = self.get_pyth_price(&pyth_feed) {
                if current_time - price.timestamp < self.staleness_threshold {
                    prices.push(price);
                }
            }
        }

        if let Some(switchboard_feed) = self.switchboard_feed {
            if let Ok(price) = self.get_switchboard_price(&switchboard_feed) {
                if current_time - price.timestamp < self.staleness_threshold {
                    prices.push(price);
                }
            }
        }

        // Require minimum number of price sources
        require!(prices.len() >= 2, ErrorCode::InsufficientOracleSources);

        // Detect and filter outliers
        let filtered_prices = self.filter_outliers(prices)?;
        require!(!filtered_prices.is_empty(), ErrorCode::NoValidPrices);

        // Aggregate using configured method
        let aggregated_price = match self.aggregation_method {
            AggregationMethod::Median => self.calculate_median(&filtered_prices),
            AggregationMethod::Mean => self.calculate_mean(&filtered_prices),
            AggregationMethod::TWAP => self.calculate_twap(&filtered_prices),
        }?;

        Ok(aggregated_price)
    }

    fn filter_outliers(&self, prices: Vec<Price>) -> Result<Vec<Price>> {
        if prices.len() < 3 {
            return Ok(prices);
        }

        let median = self.calculate_median(&prices)?;
        let threshold = median.value * self.deviation_threshold as u64 / 10000;

        let filtered: Vec<Price> = prices
            .into_iter()
            .filter(|price| {
                let deviation = if price.value > median.value {
                    price.value - median.value
                } else {
                    median.value - price.value
                };
                deviation <= threshold
            })
            .collect();

        Ok(filtered)
    }
}

Price Manipulation Detection

pub struct PriceManipulationDetector {
    pub historical_prices: VecDeque<Price>,
    pub volatility_threshold: f64,
    pub volume_spike_threshold: f64,
    pub price_impact_threshold: f64,
}

impl PriceManipulationDetector {
    pub fn detect_manipulation(&mut self, new_price: Price) -> ManipulationRisk {
        let risk_score = self.calculate_risk_score(&new_price);

        // Update historical data
        self.historical_prices.push_back(new_price);
        if self.historical_prices.len() > 100 {
            self.historical_prices.pop_front();
        }

        match risk_score {
            score if score > 0.8 => ManipulationRisk::High,
            score if score > 0.5 => ManipulationRisk::Medium,
            _ => ManipulationRisk::Low,
        }
    }

    fn calculate_risk_score(&self, new_price: &Price) -> f64 {
        let mut risk_factors = Vec::new();

        // Check price volatility
        if let Some(volatility) = self.calculate_recent_volatility() {
            if volatility > self.volatility_threshold {
                risk_factors.push(0.3);
            }
        }

        // Check volume spike
        if let Some(avg_volume) = self.calculate_average_volume() {
            if new_price.volume > avg_volume * self.volume_spike_threshold {
                risk_factors.push(0.4);
            }
        }

        // Check price deviation
        if let Some(expected_price) = self.calculate_expected_price() {
            let deviation = (new_price.value as f64 - expected_price) / expected_price;
            if deviation.abs() > self.price_impact_threshold {
                risk_factors.push(0.5);
            }
        }

        // Combine risk factors
        risk_factors.iter().sum::<f64>().min(1.0)
    }
}

Privacy Security

ZK Proof Security

pub struct ProofVerifier {
    pub verification_keys: HashMap<CircuitType, VerificationKey>,
    pub trusted_setup_hash: [u8; 32],
    pub circuit_constraints: HashMap<CircuitType, u32>,
}

impl ProofVerifier {
    pub fn verify_proof_with_security_checks(
        &self,
        proof: &Proof,
        circuit_type: CircuitType,
        public_inputs: &[Fr],
    ) -> Result<bool> {
        // Verify trusted setup integrity
        self.verify_trusted_setup(circuit_type)?;

        // Check proof size and structure
        self.validate_proof_structure(proof, circuit_type)?;

        // Verify the actual proof
        let vk = self.verification_keys.get(&circuit_type)
            .ok_or(ErrorCode::VerificationKeyNotFound)?;

        let is_valid = match proof.protocol {
            ZKProtocol::Groth16 => {
                groth16::verify_proof(vk, proof, public_inputs)?
            },
            ZKProtocol::PLONK => {
                plonk::verify_proof(vk, proof, public_inputs)?
            },
            // ... other protocols
        };

        // Additional security checks
        if is_valid {
            self.perform_additional_security_checks(proof, public_inputs)?;
        }

        Ok(is_valid)
    }

    fn perform_additional_security_checks(
        &self,
        proof: &Proof,
        public_inputs: &[Fr],
    ) -> Result<()> {
        // Check for proof malleability
        self.check_proof_uniqueness(proof)?;

        // Validate public input ranges
        self.validate_public_input_ranges(public_inputs)?;

        // Check for known attack patterns
        self.check_known_attack_patterns(proof)?;

        Ok(())
    }
}

Side-Channel Protection

pub struct ConstantTimeOps;

impl ConstantTimeOps {
    // Constant-time comparison to prevent timing attacks
    pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
        if a.len() != b.len() {
            return false;
        }

        let mut result = 0u8;
        for i in 0..a.len() {
            result |= a[i] ^ b[i];
        }

        result == 0
    }

    // Constant-time conditional selection
    pub fn constant_time_select(condition: bool, a: &[u8], b: &[u8]) -> Vec<u8> {
        let condition_mask = if condition { 0xFF } else { 0x00 };

        a.iter()
            .zip(b.iter())
            .map(|(&a_byte, &b_byte)| {
                (a_byte & condition_mask) | (b_byte & !condition_mask)
            })
            .collect()
    }
}

Economic Security

Flash Loan Protection

pub struct FlashLoanGuard {
    pub min_block_delay: u64,
    pub max_price_impact: u16,  // Basis points
    pub required_collateral_ratio: u16,
}

impl FlashLoanGuard {
    pub fn validate_transaction(
        &self,
        ctx: &Context<'_, '_, '_, '_>,
        transaction_data: &TransactionData,
    ) -> Result<()> {
        // Check if transaction spans multiple blocks
        let current_slot = Clock::get()?.slot;
        if let Some(start_slot) = transaction_data.start_slot {
            require!(
                current_slot >= start_slot + self.min_block_delay,
                ErrorCode::FlashLoanDetected
            );
        }

        // Check price impact
        let price_impact = self.calculate_price_impact(transaction_data)?;
        require!(
            price_impact <= self.max_price_impact,
            ErrorCode::ExcessivePriceImpact
        );

        // Verify collateral requirements
        if transaction_data.involves_borrowing {
            self.verify_collateral_ratio(ctx, transaction_data)?;
        }

        Ok(())
    }

    fn verify_collateral_ratio(
        &self,
        ctx: &Context<'_, '_, '_, '_>,
        transaction_data: &TransactionData,
    ) -> Result<()> {
        let collateral_value = self.calculate_collateral_value(ctx)?;
        let debt_value = self.calculate_debt_value(transaction_data)?;

        let collateral_ratio = if debt_value > 0 {
            (collateral_value * 10000) / debt_value
        } else {
            u64::MAX
        };

        require!(
            collateral_ratio >= self.required_collateral_ratio as u64,
            ErrorCode::InsufficientCollateral
        );

        Ok(())
    }
}

Slippage Protection

pub struct SlippageProtection {
    pub max_slippage_bps: u16,
    pub price_impact_threshold: u16,
    pub minimum_liquidity: u64,
}

impl SlippageProtection {
    pub fn validate_execution(
        &self,
        expected_output: u64,
        actual_output: u64,
        market_conditions: &MarketConditions,
    ) -> Result<()> {
        // Calculate actual slippage
        let slippage_bps = if expected_output > 0 {
            ((expected_output - actual_output) * 10000) / expected_output
        } else {
            0
        };

        require!(
            slippage_bps <= self.max_slippage_bps as u64,
            ErrorCode::ExcessiveSlippage
        );

        // Check market impact
        let price_impact = self.calculate_market_impact(
            expected_output,
            actual_output,
            market_conditions
        )?;

        require!(
            price_impact <= self.price_impact_threshold,
            ErrorCode::ExcessiveMarketImpact
        );

        // Verify sufficient liquidity
        require!(
            market_conditions.available_liquidity >= self.minimum_liquidity,
            ErrorCode::InsufficientLiquidity
        );

        Ok(())
    }
}

Emergency Response

Circuit Breaker

pub struct CircuitBreaker {
    pub triggered: bool,
    pub trigger_conditions: Vec<TriggerCondition>,
    pub emergency_contacts: Vec<Pubkey>,
    pub auto_recovery_enabled: bool,
    pub recovery_delay: i64,
}

impl CircuitBreaker {
    pub fn check_trigger_conditions(&mut self) -> Result<()> {
        for condition in &self.trigger_conditions {
            if self.evaluate_condition(condition)? {
                self.trigger_emergency_stop(condition)?;
                break;
            }
        }
        Ok(())
    }

    pub fn trigger_emergency_stop(&mut self, condition: &TriggerCondition) -> Result<()> {
        self.triggered = true;

        // Log the incident
        emit!(EmergencyStopEvent {
            trigger_condition: condition.clone(),
            timestamp: Clock::get()?.unix_timestamp,
            triggered_by: condition.triggered_by,
        });

        // Notify emergency contacts
        for contact in &self.emergency_contacts {
            // Send emergency notification
            self.notify_emergency_contact(contact, condition)?;
        }

        // Pause all operations
        self.pause_all_operations()?;

        Ok(())
    }

    pub fn manual_recovery(&mut self, authority: &Signer) -> Result<()> {
        // Verify authority
        require!(
            self.emergency_contacts.contains(&authority.key()),
            ErrorCode::UnauthorizedRecovery
        );

        // Reset circuit breaker
        self.triggered = false;

        // Resume operations
        self.resume_operations()?;

        emit!(RecoveryEvent {
            timestamp: Clock::get()?.unix_timestamp,
            recovered_by: authority.key(),
        });

        Ok(())
    }
}

#[derive(Clone, Debug)]
pub enum TriggerCondition {
    PriceDeviation { threshold: u16 },
    VolumeSpike { multiplier: u16 },
    LiquidityDrop { minimum: u64 },
    OracleFailure { max_stale_time: i64 },
    ExploitDetection { pattern: ExploitPattern },
}

Incident Response

pub struct IncidentResponse {
    pub response_team: Vec<Pubkey>,
    pub escalation_levels: Vec<EscalationLevel>,
    pub communication_channels: Vec<CommunicationChannel>,
    pub recovery_procedures: HashMap<IncidentType, RecoveryProcedure>,
}

impl IncidentResponse {
    pub fn handle_security_incident(
        &self,
        incident: SecurityIncident,
    ) -> Result<IncidentResponse> {
        // Assess incident severity
        let severity = self.assess_incident_severity(&incident)?;

        // Execute immediate response
        match severity {
            Severity::Critical => {
                self.execute_critical_response(&incident)?;
            },
            Severity::High => {
                self.execute_high_priority_response(&incident)?;
            },
            Severity::Medium => {
                self.execute_standard_response(&incident)?;
            },
            _ => {
                self.execute_low_priority_response(&incident)?;
            }
        }

        // Initiate recovery procedures
        if let Some(procedure) = self.recovery_procedures.get(&incident.incident_type) {
            self.initiate_recovery(procedure)?;
        }

        Ok(IncidentResponse::InProgress)
    }
}

Audit Requirements

Smart Contract Audits

  1. Code Review Requirements
  2. Minimum 3 independent audit firms
  3. Focus on economic logic and access controls
  4. Formal verification for critical paths
  5. Gas optimization analysis

  6. ZK Circuit Audits

  7. Circuit constraint verification
  8. Trusted setup validation
  9. Soundness and completeness proofs
  10. Implementation security review

  11. Economic Security Audits

  12. Game theory analysis
  13. Incentive mechanism review
  14. Attack vector identification
  15. Economic model validation

Continuous Security

pub struct SecurityMonitoring {
    pub anomaly_detector: AnomalyDetector,
    pub threat_intelligence: ThreatIntelligence,
    pub automated_responses: Vec<AutomatedResponse>,
    pub security_metrics: SecurityMetrics,
}

impl SecurityMonitoring {
    pub fn monitor_security_continuously(&mut self) -> Result<()> {
        // Detect anomalies in real-time
        let anomalies = self.anomaly_detector.scan_for_anomalies()?;

        for anomaly in anomalies {
            // Check against threat intelligence
            let threat_level = self.threat_intelligence
                .assess_threat_level(&anomaly)?;

            // Execute automated response if necessary
            if threat_level >= ThreatLevel::Medium {
                self.execute_automated_response(&anomaly)?;
            }

            // Update security metrics
            self.security_metrics.record_incident(&anomaly);
        }

        Ok(())
    }
}

Best Practices

Development Security

  1. Secure Coding Practices
  2. Input validation on all user inputs
  3. Use checked arithmetic operations
  4. Implement proper access controls
  5. Follow the principle of least privilege

  6. Testing Requirements

  7. Unit tests for all security-critical functions
  8. Integration tests for cross-program interactions
  9. Fuzz testing for input validation
  10. Stress testing for economic attacks

  11. Deployment Security

  12. Use multi-signature wallets for admin functions
  13. Implement timelock for critical upgrades
  14. Monitor all deployed contracts continuously
  15. Maintain incident response procedures

Operational Security

  1. Key Management
  2. Hardware security modules for critical keys
  3. Key rotation procedures
  4. Multi-party computation for sensitive operations
  5. Secure key backup and recovery

  6. Infrastructure Security

  7. Network segmentation
  8. DDoS protection
  9. Regular security updates
  10. Intrusion detection systems

  11. Monitoring and Alerting

  12. Real-time transaction monitoring
  13. Anomaly detection systems
  14. Security incident response team
  15. Regular security assessments