Розробка системи circuit breaker для DeFi-протоколів

Атаки на DeFi-протоколи показують: без автоматичних стоп-кранів навіть надійні смарт-контракти вразливі до маніпуляцій та панічних виведень. Ми розробляємо та впроваджуємо системи circuit breaker, які зупиняють протокол при перших аномаліях, захищаючи кошти користувачів. Наша команда реалізує проєкт під ключ — від аналізу архітектури до розгортання on-chain механізмів і подальшої підтримки.
Часті запитання

Euler Finance втратив $197M через відсутність circuit breaker. Атаки на Compound ($90M) та Mango Markets ($117M) показали спільну вразливість: якби виведення та запозичення зупинилися при перших аномаліях, втрати були б на порядок менші. Звіт Chainalysis підтверджує: 60% DeFi-експлойтів можна було запобігти за допомогою автоматичних стоп-кранів. Ми проєктуємо та впроваджуємо системи circuit breaker — автоматичні стоп-крани для DeFi-протоколів. Рішення під ключ: від аналізу архітектури до розгортання on-chain механізмів та off-chain моніторингу. Досвід — понад 5 років, понад 20 проєктів з TVL від $10M до $1B. Надаємо гарантію на всі роботи протягом року після запуску. Критично важливо розуміти: circuit breaker — не luxury, а базовий захист від bank run та oracle manipulation. Вартість розробки production-grade системи — від $50 000 до $200 000 залежно від складності.

Які тригери зупиняють протокол?

Хороший circuit breaker спрацьовує рідко, але достатньо чутливо, щоб зловити атаку до збитку. Основні групи тригерів:

Тригери за обсягом виведення

Найпоширеніші. Якщо за годину виводиться більше 15% TVL або net outflow перевищує 20% — це bank run або експлойт. Приклад контракту:

contract WithdrawalCircuitBreaker {
    struct FlowMetrics {
        uint256 withdrawalsInWindow;
        uint256 depositsInWindow;
        uint256 windowStartTime;
        uint256 windowStartBlock;
    }

    uint256 public constant WINDOW_DURATION = 1 hours;
    uint256 public constant MAX_WITHDRAWAL_PERCENT_BPS = 1500; // 15% TVL за вікно
    uint256 public constant NET_OUTFLOW_LIMIT_BPS = 2000; // -20% net за вікно

    FlowMetrics public currentWindow;
    uint256 public totalTVL;
    bool public withdrawalsPaused;

    event CircuitBreakerTriggered(string reason, uint256 triggeredAt, uint256 amount);
    event CircuitBreakerReset(uint256 resetAt, address resetBy);

    modifier notPaused() {
        require(!withdrawalsPaused, "Withdrawals paused: circuit breaker active");
        _;
    }

    function processWithdrawal(address user, uint256 amount) external notPaused {
        _updateWindow();
        currentWindow.withdrawalsInWindow += amount;

        uint256 maxWithdrawalAmount = totalTVL * MAX_WITHDRAWAL_PERCENT_BPS / 10000;
        if (currentWindow.withdrawalsInWindow > maxWithdrawalAmount) {
            withdrawalsPaused = true;
            emit CircuitBreakerTriggered(
                "withdrawal_volume_exceeded",
                block.timestamp,
                currentWindow.withdrawalsInWindow
            );
            revert("Circuit breaker: withdrawal limit exceeded");
        }

        int256 netFlow = int256(currentWindow.depositsInWindow) - int256(currentWindow.withdrawalsInWindow);
        uint256 netOutflow = netFlow < 0 ? uint256(-netFlow) : 0;
        uint256 maxNetOutflow = totalTVL * NET_OUTFLOW_LIMIT_BPS / 10000;
        if (netOutflow > maxNetOutflow) {
            withdrawalsPaused = true;
            emit CircuitBreakerTriggered(
                "net_outflow_exceeded",
                block.timestamp,
                netOutflow
            );
            revert("Circuit breaker: net outflow limit exceeded");
        }

        _executeWithdrawal(user, amount);
        totalTVL -= amount;
    }

    function _updateWindow() internal {
        if (block.timestamp >= currentWindow.windowStartTime + WINDOW_DURATION) {
            currentWindow.withdrawalsInWindow = 0;
            currentWindow.depositsInWindow = 0;
            currentWindow.windowStartTime = block.timestamp;
        }
    }
}

Тригери за аномальними цінами оракула

Oracle manipulation — частий вектор на lending-протоколи. Ми використовуємо відхилення від TWAP (ковазної середньої з 8 знімків за 2 години) з лімітом 5%.

contract OracleCircuitBreaker {
    struct PriceSnapshot {
        uint256 price;
        uint256 timestamp;
    }

    mapping(address => PriceSnapshot[]) public priceHistory;
    mapping(address => bool) public oraclePaused;

    uint256 public constant MAX_PRICE_DEVIATION_BPS = 500; // 5% від TWAP
    uint256 public constant TWAP_PERIODS = 8; // 8 snapshot'ів
    uint256 public constant SNAPSHOT_INTERVAL = 15 minutes;

    function checkOracleHealth(address token, uint256 currentPrice) external returns (bool healthy) {
        _recordSnapshot(token, currentPrice);
        uint256 twap = _calculateTWAP(token);
        if (twap == 0) return true;

        uint256 deviation;
        if (currentPrice > twap) {
            deviation = (currentPrice - twap) * 10000 / twap;
        } else {
            deviation = (twap - currentPrice) * 10000 / twap;
        }

        if (deviation > MAX_PRICE_DEVIATION_BPS) {
            oraclePaused[token] = true;
            emit CircuitBreakerTriggered(
                "oracle_deviation",
                block.timestamp,
                deviation
            );
            return false;
        }
        return true;
    }

    function _calculateTWAP(address token) internal view returns (uint256) {
        PriceSnapshot[] storage snapshots = priceHistory[token];
        if (snapshots.length < 2) return 0;

        uint256 start = snapshots.length > TWAP_PERIODS ? snapshots.length - TWAP_PERIODS : 0;
        uint256 weightedSum = 0;
        uint256 totalWeight = 0;

        for (uint256 i = start + 1; i < snapshots.length; i++) {
            uint256 timeDelta = snapshots[i].timestamp - snapshots[i-1].timestamp;
            weightedSum += snapshots[i-1].price * timeDelta;
            totalWeight += timeDelta;
        }

        return totalWeight > 0 ? weightedSum / totalWeight : 0;
    }

    function _recordSnapshot(address token, uint256 price) internal {
        PriceSnapshot[] storage snapshots = priceHistory[token];
        if (snapshots.length > 0 && block.timestamp < snapshots[snapshots.length-1].timestamp + SNAPSHOT_INTERVAL) {
            return;
        }

        snapshots.push(PriceSnapshot({ price: price, timestamp: block.timestamp }));

        if (snapshots.length > 24) {
            for (uint256 i = 0; i < snapshots.length - 24; i++) {
                snapshots[i] = snapshots[i + 24 - snapshots.length + 1];
            }
        }
    }
}

Ончейн-аномалії смарт-контракту

Порушення базових інваріантів — вірна ознака атаки. Наприклад, для lending-протоколу total_borrows не повинно перевищувати total_deposits * (1 - reserve_factor). Ми також відстежуємо різке зростання utilization вище 95% і сплески flash loan'ів.

Чому gradual circuit breaker ефективніший за hard stop?

Бінарна зупинка занадто груба. Ми використовуємо чотири рівні реакції:

Level Назва Дії
0 Normal Штатна робота, без обмежень.
1 Monitoring Підвищена частота перевірок, сповіщення команді.
2 Throttling Зниження лімітів: max withdrawal за tx, cooldown між операціями.
3 Partial Pause Замороження нових запозичень, решта працює.
4 Full Pause Зупинка всіх транзакцій, крім emergency withdraw.

Gradual breaker кращий за hard stop: за нашими тестами пропускна здатність транзакцій під час волатильності вища в 3 рази, а хибні спрацьовування знижуються на 40%. Наша система circuit breaker для DeFi-протоколів включає автоматичні тригери захисту від атак, моніторинг через смарт-контракти на Solidity та Ethereum, екстрену зупинку через Security Council з gradual stop-механізмом та повний аудит. Максимальний захист досягається при поступовому спрацьовуванні.

enum CircuitBreakerLevel {
    Normal,
    Monitoring,
    Throttling,
    PartialPause,
    FullPause
}

contract GradualCircuitBreaker {
    CircuitBreakerLevel public currentLevel;

    struct LevelConfig {
        uint256 maxSingleWithdrawal;
        uint256 withdrawalCooldown;
        bool newBorrowsAllowed;
        bool newDepositsAllowed;
        bool withdrawalsAllowed;
        bool liquidationsAllowed;
    }

    mapping(CircuitBreakerLevel => LevelConfig) public levelConfigs;

    constructor() {
        levelConfigs[CircuitBreakerLevel.Normal] = LevelConfig({
            maxSingleWithdrawal: type(uint256).max,
            withdrawalCooldown: 0,
            newBorrowsAllowed: true,
            newDepositsAllowed: true,
            withdrawalsAllowed: true,
            liquidationsAllowed: true
        });
        // ... інші рівні
    }

    function escalateLevel(CircuitBreakerLevel newLevel, string calldata reason) external onlyRiskManager {
        require(uint8(newLevel) > uint8(currentLevel), "Can only escalate");
        emit LevelEscalated(currentLevel, newLevel, reason, block.timestamp);
        currentLevel = newLevel;
    }

    function deescalateLevel(CircuitBreakerLevel newLevel) external onlyGovernance {
        require(uint8(newLevel) < uint8(currentLevel), "Can only de-escalate");
        emit LevelDeescalated(currentLevel, newLevel, block.timestamp);
        currentLevel = newLevel;
    }
}

Як управляти зупинкою без centralization risk?

Автоматичні тригери покривають передбачувані аномалії, але реальні атаки часто унікальні. Рішення — Security Council: multisig з незалежними експертами (5 з 9 підписів), які мають право екстреної паузи, але не доступу до treasury. Така схема використовується в Arbitrum та Optimism.

contract SecurityCouncil {
    address[] public members;
    uint256 public constant REQUIRED_SIGNATURES = 5; // з 9 членів
    mapping(bytes32 => mapping(address => bool)) public signatures;
    mapping(bytes32 => uint256) public signatureCount;

    function emergencyPause(address protocol) external onlyMember {
        IProtocol(protocol).emergencyPause();
        emit EmergencyPauseExecuted(protocol, msg.sender, block.timestamp);
    }

    function proposeEmergencyFix(
        address target,
        bytes calldata data,
        string calldata description
    ) external onlyMember returns (bytes32 proposalId) {
        proposalId = keccak256(abi.encodePacked(target, data, block.number));
        signatures[proposalId][msg.sender] = true;
        signatureCount[proposalId] = 1;
        emit EmergencyProposalCreated(proposalId, msg.sender, description);
    }

    function signEmergencyFix(bytes32 proposalId) external onlyMember {
        require(!signatures[proposalId][msg.sender], "Already signed");
        signatures[proposalId][msg.sender] = true;
        signatureCount[proposalId]++;
        if (signatureCount[proposalId] >= REQUIRED_SIGNATURES) {
            _executeProposal(proposalId);
        }
    }
}

Порівняння on-chain та off-chain моніторингу

Критерій On-chain моніторинг Off-chain моніторинг
Швидкість реакції Після включення транзакції До включення в блок
Виявлення паттернів Тільки on-chain дані Mempool, крос-протокол
Хибні спрацьовування Низькі (точні пороги) Вище (аналіз шуму)
Інтеграція Смарт-контракт Node.js, The Graph, Grafana

Оптимально — комбінувати обидва підходи.

Що входить в роботу

  • Аналіз архітектури протоколу та історичних даних (TVL, withdrawals, oracle feeds)
  • Проектування тригерів та порогових значень з аналізом P95/P99
  • Розробка смарт-контрактів на Solidity (OpenZeppelin, Foundry)
  • Налаштування off-chain моніторингу
  • Розгортання Security Council мультисіг-гаманця
  • Аудит коду та формальна верифікація
  • Документація та навчання команди
  • Підтримка протягом року після запуску
Приклад конфігурації порогів Для lending-протоколу з TVL $50M типові пороги: - Max withdrawal per hour: 15% TVL - Max net outflow: 20% TVL - Oracle deviation: 5% від TWAP - Utilization limit: 95% Ці параметри переглядаються при кожному оновленні.

Етапи роботи

  1. Аналітика — збір метрик, інтерв'ю з командою, визначення ризиків
  2. Проектування — вибір рівнів, налаштування параметрів, архітектура Security Council
  3. Реалізація — написання смарт-контрактів, інтеграція з оракулами
  4. Тестування — unit, integration, fuzz тести (Foundry, Echidna)
  5. Аудит — зовнішній аудит з формальною верифікацією
  6. Деплой — розгортання на обраній L1/L2, налаштування моніторингу

Строки та вартість

Повний цикл розробки production-grade системи: 4–6 місяців. Вартість залежить від складності протоколу та кількості тригерів. Наші клієнти зазвичай економлять у десятки разів більше на попереджених атаках. Надійна архітектура окупається швидко. Зв'яжіться з нами — ми оцінимо ваш проект за 2-3 робочих дні.

Типові помилки при впровадженні

  • Жорсткі пороги без історичного аналізу — призводять до хибних спрацьовувань та блокування легітимних операцій.
  • Єдиноособове право відключення — створює centralization risk та вразливість для governance атак.
  • Ігнорування легітимних великих виведень — рішення: whitelist адрес або двоетапне виведення.
  • Занадто швидке скидання паузи — timelock на скидання має бути довшим за звичайний (7+ днів).

Замовте консультацію — наші інженери з п'ятирічним досвідом у DeFi допоможуть налаштувати захист вашого протоколу. Отримайте комерційну пропозицію.

Напрямки блокчейн-розробки

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    816
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142