Emergency Procedures & Circuit Breakers
Production-grade emergency response system for DeFi protocols handling billions in liquidity
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.
| Layer | Halt modes |
|---|---|
D-Chain (dexvm) | HALT_NEW_ORDERS, HALT_MATCHING, CANCEL_ONLY, SETTLE_ONLY, FULL_HALT |
0x9999 precompile | HALT_NEW_SWAPS, SETTLE_ONLY, DISABLE_RECEIPT_TYPE, DISABLE_ASSET, DISABLE_MARKET |
Halt keys are fine-grained — haltGlobal, 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
| Metric | Requirement |
|---|---|
| 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 objective | Zero 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
- Multi-sig requirements - Critical actions require 3-of-5 or higher
- Time-locks - Non-emergency changes require 24-48hr delay
- Role separation - Operators can trigger, only admins can shutdown
- Audit trail - All emergency actions logged on-chain
- External notification - Automatic alerts to monitoring services
Test Cases
- Circuit breaker triggers on 10x volume spike
- Auto-escalation when 3+ breakers active
- Emergency pause propagates to all contracts < 1s
- Recovery workflow completes all phases
- Multi-sig prevents unauthorized shutdown
Copyright
Copyright and related rights waived via CC0.