LPsLux Proposals
DEX & Trading
LP-9016

Emergency Procedures & Circuit Breakers

Draft

Production-grade emergency response system for DeFi protocols handling billions in liquidity

Category
Core
Created
2025-01-15

LP-9016: Emergency Procedures & Circuit Breakers

Abstract

This LP defines a comprehensive emergency response system for Lux DeFi infrastructure, including circuit breakers, pause mechanisms, emergency shutdown procedures, and recovery protocols. Designed to protect billions in liquidity from exploits, market manipulation, and black swan events.

Motivation

High-value DeFi protocols require robust emergency mechanisms:

  • Flash crash protection
  • Exploit detection and response
  • Graceful degradation under attack
  • Capital preservation during emergencies
  • Regulatory compliance capabilities

Specification

1. Emergency Guardian System

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IEmergencyGuardian {
    enum EmergencyLevel {
        NORMAL,           // Normal operation
        ELEVATED,         // Enhanced monitoring, reduced limits
        WARNING,          // Restricted operations, withdrawal priority
        CRITICAL,         // Emergency mode, essential functions only
        SHUTDOWN          // Full shutdown, emergency withdrawal only
    }

    struct EmergencyState {
        EmergencyLevel level;
        uint256 activatedAt;
        uint256 expiresAt;
        bytes32 reason;
        address activatedBy;
    }

    struct CircuitBreaker {
        bool triggered;
        uint256 triggeredAt;
        uint256 cooldownEnds;
        uint256 triggerCount;
        uint256 lastResetAt;
    }

    // Emergency level management
    function currentLevel() external view returns (EmergencyLevel);
    function escalate(EmergencyLevel level, bytes32 reason) external;
    function deescalate(EmergencyLevel level) external;
    function getEmergencyState() external view returns (EmergencyState memory);

    // Circuit breaker functions
    function checkCircuitBreaker(bytes32 breakerId) external view returns (bool triggered);
    function triggerCircuitBreaker(bytes32 breakerId, string calldata reason) external;
    function resetCircuitBreaker(bytes32 breakerId) external;

    // Emergency actions
    function emergencyPause() external;
    function emergencyUnpause() external;
    function emergencyWithdraw(address token, address recipient) external;
    function emergencyShutdown() external;

    // Events
    event EmergencyLevelChanged(EmergencyLevel oldLevel, EmergencyLevel newLevel, bytes32 reason);
    event CircuitBreakerTriggered(bytes32 indexed breakerId, string reason, uint256 cooldownEnds);
    event CircuitBreakerReset(bytes32 indexed breakerId);
    event EmergencyAction(string action, address indexed actor, uint256 timestamp);
}

2. Circuit Breaker Types

library CircuitBreakers {
    // Volume-based breakers
    bytes32 constant VOLUME_SPIKE = keccak256("VOLUME_SPIKE");           // 10x normal volume
    bytes32 constant LIQUIDITY_DRAIN = keccak256("LIQUIDITY_DRAIN");     // >20% TVL withdrawn in 1hr
    bytes32 constant LARGE_WITHDRAWAL = keccak256("LARGE_WITHDRAWAL");   // Single withdrawal >5% TVL

    // Price-based breakers
    bytes32 constant PRICE_DEVIATION = keccak256("PRICE_DEVIATION");     // >10% from oracle
    bytes32 constant ORACLE_FAILURE = keccak256("ORACLE_FAILURE");       // Oracle stale/invalid
    bytes32 constant FLASH_CRASH = keccak256("FLASH_CRASH");             // >25% drop in 5min

    // Security-based breakers
    bytes32 constant REENTRANCY_DETECTED = keccak256("REENTRANCY");      // Reentrancy pattern
    bytes32 constant EXPLOIT_SIGNATURE = keccak256("EXPLOIT");           // Known exploit pattern
    bytes32 constant ANOMALY_DETECTED = keccak256("ANOMALY");            // ML anomaly detection

    // Cross-chain breakers
    bytes32 constant BRIDGE_DELAY = keccak256("BRIDGE_DELAY");           // Bridge messages delayed
    bytes32 constant CHAIN_REORG = keccak256("CHAIN_REORG");             // Deep reorg detected
    bytes32 constant CONSENSUS_ISSUE = keccak256("CONSENSUS");           // Consensus problems
}

3. Emergency Guardian Implementation

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

contract EmergencyGuardian is IEmergencyGuardian, AccessControlUpgradeable, PausableUpgradeable {
    bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    bytes32 public constant EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE");

    EmergencyState public emergencyState;
    mapping(bytes32 => CircuitBreaker) public circuitBreakers;
    mapping(bytes32 => uint256) public breakerCooldowns;

    // Thresholds
    uint256 public constant VOLUME_SPIKE_MULTIPLIER = 10;
    uint256 public constant LIQUIDITY_DRAIN_THRESHOLD = 2000; // 20% in basis points
    uint256 public constant LARGE_WITHDRAWAL_THRESHOLD = 500; // 5% in basis points
    uint256 public constant PRICE_DEVIATION_THRESHOLD = 1000; // 10%
    uint256 public constant FLASH_CRASH_THRESHOLD = 2500; // 25%

    // Cooldowns
    uint256 public constant DEFAULT_COOLDOWN = 1 hours;
    uint256 public constant CRITICAL_COOLDOWN = 24 hours;

    // Protected contracts
    mapping(address => bool) public protectedContracts;
    address[] public protectedContractList;

    function initialize(address admin) external initializer {
        __AccessControl_init();
        __Pausable_init();

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(GUARDIAN_ROLE, admin);
        _grantRole(EMERGENCY_ROLE, admin);

        emergencyState.level = EmergencyLevel.NORMAL;

        // Set default cooldowns
        breakerCooldowns[CircuitBreakers.VOLUME_SPIKE] = 30 minutes;
        breakerCooldowns[CircuitBreakers.LIQUIDITY_DRAIN] = 2 hours;
        breakerCooldowns[CircuitBreakers.PRICE_DEVIATION] = 1 hours;
        breakerCooldowns[CircuitBreakers.ORACLE_FAILURE] = 4 hours;
        breakerCooldowns[CircuitBreakers.FLASH_CRASH] = 6 hours;
        breakerCooldowns[CircuitBreakers.EXPLOIT_SIGNATURE] = 24 hours;
    }

    function currentLevel() external view override returns (EmergencyLevel) {
        // Auto-expire emergency states
        if (emergencyState.expiresAt > 0 && block.timestamp > emergencyState.expiresAt) {
            return EmergencyLevel.NORMAL;
        }
        return emergencyState.level;
    }

    function escalate(EmergencyLevel level, bytes32 reason)
        external
        override
        onlyRole(GUARDIAN_ROLE)
    {
        require(level > emergencyState.level, "Can only escalate");

        EmergencyLevel oldLevel = emergencyState.level;
        emergencyState = EmergencyState({
            level: level,
            activatedAt: block.timestamp,
            expiresAt: _getExpiration(level),
            reason: reason,
            activatedBy: msg.sender
        });

        // Trigger protective actions based on level
        if (level >= EmergencyLevel.WARNING) {
            _pauseAllProtected();
        }

        emit EmergencyLevelChanged(oldLevel, level, reason);
    }

    function triggerCircuitBreaker(bytes32 breakerId, string calldata reason)
        external
        override
        onlyRole(OPERATOR_ROLE)
    {
        CircuitBreaker storage breaker = circuitBreakers[breakerId];

        breaker.triggered = true;
        breaker.triggeredAt = block.timestamp;
        breaker.cooldownEnds = block.timestamp + breakerCooldowns[breakerId];
        breaker.triggerCount++;

        emit CircuitBreakerTriggered(breakerId, reason, breaker.cooldownEnds);

        // Auto-escalate if multiple breakers triggered
        if (_countActiveBreakers() >= 3) {
            _autoEscalate();
        }
    }

    function emergencyPause() external override onlyRole(EMERGENCY_ROLE) {
        _pauseAllProtected();
        emit EmergencyAction("PAUSE_ALL", msg.sender, block.timestamp);
    }

    function emergencyShutdown() external override onlyRole(DEFAULT_ADMIN_ROLE) {
        emergencyState.level = EmergencyLevel.SHUTDOWN;
        _pauseAllProtected();
        emit EmergencyAction("SHUTDOWN", msg.sender, block.timestamp);
    }

    function emergencyWithdraw(address token, address recipient)
        external
        override
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(emergencyState.level == EmergencyLevel.SHUTDOWN, "Not in shutdown");
        // Emergency withdrawal logic - transfer stuck funds
        emit EmergencyAction("EMERGENCY_WITHDRAW", msg.sender, block.timestamp);
    }

    function _pauseAllProtected() internal {
        for (uint i = 0; i < protectedContractList.length; i++) {
            IPausable(protectedContractList[i]).pause();
        }
    }

    function _countActiveBreakers() internal view returns (uint256 count) {
        bytes32[6] memory breakers = [
            CircuitBreakers.VOLUME_SPIKE,
            CircuitBreakers.LIQUIDITY_DRAIN,
            CircuitBreakers.PRICE_DEVIATION,
            CircuitBreakers.ORACLE_FAILURE,
            CircuitBreakers.FLASH_CRASH,
            CircuitBreakers.EXPLOIT_SIGNATURE
        ];

        for (uint i = 0; i < breakers.length; i++) {
            if (circuitBreakers[breakers[i]].triggered &&
                circuitBreakers[breakers[i]].cooldownEnds > block.timestamp) {
                count++;
            }
        }
    }

    function _autoEscalate() internal {
        if (emergencyState.level < EmergencyLevel.WARNING) {
            emergencyState.level = EmergencyLevel.WARNING;
            emit EmergencyLevelChanged(EmergencyLevel.NORMAL, EmergencyLevel.WARNING, "AUTO_ESCALATE");
        }
    }

    function _getExpiration(EmergencyLevel level) internal view returns (uint256) {
        if (level == EmergencyLevel.ELEVATED) return block.timestamp + 4 hours;
        if (level == EmergencyLevel.WARNING) return block.timestamp + 24 hours;
        if (level == EmergencyLevel.CRITICAL) return block.timestamp + 72 hours;
        return 0; // SHUTDOWN has no expiration
    }
}

interface IPausable {
    function pause() external;
    function unpause() external;
}

4. Automated Anomaly Detection

interface IAnomalyDetector {
    struct MetricSnapshot {
        uint256 volume24h;
        uint256 tvl;
        uint256 priceUSD;
        uint256 volatility;
        uint256 uniqueUsers;
        uint256 avgTxSize;
        uint256 timestamp;
    }

    struct AnomalyThresholds {
        uint256 volumeZScore;      // Standard deviations from mean
        uint256 tvlChangeRate;     // Max % change per hour
        uint256 volatilitySpike;   // Max volatility increase
        uint256 userConcentration; // Max % from single user
        uint256 txSizeAnomaly;     // Z-score for tx size
    }

    function recordMetrics(MetricSnapshot calldata snapshot) external;
    function checkForAnomalies() external view returns (bool hasAnomaly, bytes32[] memory anomalyTypes);
    function getHistoricalMetrics(uint256 periods) external view returns (MetricSnapshot[] memory);
    function setThresholds(AnomalyThresholds calldata thresholds) external;
}

5. Multi-Signature Emergency Actions

interface IEmergencyMultisig {
    struct EmergencyProposal {
        bytes32 actionHash;
        address[] approvers;
        uint256 approvalCount;
        uint256 requiredApprovals;
        uint256 createdAt;
        uint256 expiresAt;
        bool executed;
        EmergencyAction action;
    }

    enum EmergencyAction {
        PAUSE_SINGLE,
        PAUSE_ALL,
        UNPAUSE_SINGLE,
        UNPAUSE_ALL,
        ESCALATE_LEVEL,
        DEESCALATE_LEVEL,
        EMERGENCY_WITHDRAW,
        SHUTDOWN,
        UPGRADE_CONTRACT
    }

    function proposeEmergencyAction(EmergencyAction action, bytes calldata params) external returns (bytes32 proposalId);
    function approveProposal(bytes32 proposalId) external;
    function executeProposal(bytes32 proposalId) external;
    function cancelProposal(bytes32 proposalId) external;

    // Fast-track for critical emergencies (higher threshold)
    function fastTrackExecute(EmergencyAction action, bytes calldata params, bytes[] calldata signatures) external;
}

6. Emergency Response Playbooks

Level 1: ELEVATED

  • Increase monitoring frequency to 1 minute
  • Reduce position limits by 50%
  • Enable enhanced logging
  • Alert on-call team

Level 2: WARNING

  • Pause new deposits (withdrawals allowed)
  • Reduce leverage limits
  • Activate backup oracles
  • Notify security team
  • Begin user communications

Level 3: CRITICAL

  • Pause all non-essential operations
  • Enable emergency withdrawal only
  • Activate incident response team
  • Contact external security partners
  • Prepare post-mortem

Level 4: SHUTDOWN

  • Full protocol pause
  • Emergency withdrawal mode only
  • Admin-only access
  • External audit triggered
  • Recovery planning

7. Recovery Procedures

interface IRecoveryManager {
    enum RecoveryPhase {
        ASSESSMENT,       // Evaluate damage
        CONTAINMENT,      // Stop further damage
        ERADICATION,      // Remove vulnerability
        RECOVERY,         // Restore services
        POST_INCIDENT     // Analysis and improvements
    }

    struct RecoveryPlan {
        RecoveryPhase currentPhase;
        uint256 estimatedCompletion;
        bytes32[] completedSteps;
        bytes32[] remainingSteps;
        address[] recoveryTeam;
    }

    function initiateRecovery(bytes32 incidentId) external;
    function advancePhase(RecoveryPhase phase) external;
    function getRecoveryStatus() external view returns (RecoveryPlan memory);
    function completeRecovery() external;
}

8. Communication Templates

interface IIncidentCommunication {
    struct Incident {
        bytes32 id;
        uint256 detectedAt;
        uint256 severity;
        string summary;
        string impact;
        string[] updates;
        bool resolved;
    }

    function declareIncident(uint256 severity, string calldata summary) external returns (bytes32);
    function addUpdate(bytes32 incidentId, string calldata update) external;
    function resolveIncident(bytes32 incidentId, string calldata resolution) external;
    function getIncidentHistory() external view returns (Incident[] memory);
}

9. DEX Multi-Layer Halt

The DEX is haltable at both layers — the D-Chain matcher and the 0x9999 receipt-settlement precompile (LP-9999). Halt state is replicated consensus state (not per-node), authorized by the protocol-fee controller / governance.

LayerHalt modes
D-Chain (dexvm)HALT_NEW_ORDERS, HALT_MATCHING, CANCEL_ONLY, SETTLE_ONLY, FULL_HALT
0x9999 precompileHALT_NEW_SWAPS, SETTLE_ONLY, DISABLE_RECEIPT_TYPE, DISABLE_ASSET, DISABLE_MARKET

Halt keys are fine-grainedhaltGlobal, haltMarket[id], haltAsset[id], haltReceiptType[certType], haltValidatorSet[id] — so an incident can be scoped to one market, asset, or certificate type without stopping the whole venue. The safe default on incident is: stop new swaps; allow cancel, settle of already-certified safe receipts, and withdrawals/refunds. A halted swap reverts cleanly with no partial state. See LP-9999 §7 for the normative halt specification.

Performance Requirements

MetricRequirement
Circuit breaker activation< 100ms
Full pause propagation< 1 second
Emergency withdrawal< 1 block
Anomaly detection latency< 5 seconds
Recovery time objective< 4 hours
Recovery point objectiveZero data loss

Rationale

Emergency procedures must balance speed of response with security controls. The tiered escalation system ensures minor issues don't trigger unnecessary shutdowns while critical threats receive immediate attention. Multi-sig requirements prevent single points of failure while time-locks provide review windows for non-urgent changes.

Backwards Compatibility

Emergency procedures are additive infrastructure that works alongside existing protocol contracts. Circuit breakers and pause mechanisms are designed to be non-invasive until triggered, ensuring normal operation is unaffected.

Security Considerations

  1. Multi-sig requirements - Critical actions require 3-of-5 or higher
  2. Time-locks - Non-emergency changes require 24-48hr delay
  3. Role separation - Operators can trigger, only admins can shutdown
  4. Audit trail - All emergency actions logged on-chain
  5. External notification - Automatic alerts to monitoring services

Test Cases

  1. Circuit breaker triggers on 10x volume spike
  2. Auto-escalation when 3+ breakers active
  3. Emergency pause propagates to all contracts < 1s
  4. Recovery workflow completes all phases
  5. Multi-sig prevents unauthorized shutdown

Copyright and related rights waived via CC0.