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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Разработка системы circuit breaker для DeFi-протоколов
Сложный
~3-5 дней
Часто задаваемые вопросы

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

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1351
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    950
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1186
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    642
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    922

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

Какие триггеры останавливают протокол?

Хороший 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%. Максимальная защита достигается при постепенном срабатывании.

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 помогут настроить защиту вашего протокола. Получите коммерческое предложение.

Аудит смарт-контрактов: как находят то, что не видит компилятор

Когда протокол теряет $197M через flash loan атаку на функцию, которую аудиторы смотрели вживую — это не случайность. Это системный пробел в методологии. Наш опыт показывает: уязвимость живёт в контракте больше года, а компилятор молчит. Мы перестроили процесс аудита так, чтобы ловить такие кейсы до деплоя.

Что статический анализ не найдёт

Slither — стандартный первый инструмент. Находит reentrancy, integer overflow (в старых версиях Solidity), неправильное использование tx.origin, shadowing переменных, неинициализированные хранилища. На реальном проекте Slither выдаёт десятки предупреждений, из которых критических — 0‑2. Остальное — информационный шум.

Slither не найдёт логическую уязвимость. Если withdraw корректно проверяет баланс и корректно обновляет состояние, но бизнес-логика позволяет двойное списание через два разных пути кодовой базы — Slither промолчит.

Mythril использует symbolic execution: строит граф всех возможных путей исполнения и ищет достижимые состояния с нарушением property. Работает хорошо на изолированных контрактах. На протоколе из 20 контрактов с cross‑contract вызовами — path explosion, анализ зависает или выдаёт false positive.

Оба инструмента обязательны как первый pass. Но они не заменяют ручной анализ.

Fuzzing: где Echidna и Foundry находят реальные баги

Echidna — property‑based fuzzer от Trail of Bits. Идея: формулируешь инварианты контракта как Solidity‑функции (echidna_invariant), Echidna генерирует случайные последовательности вызовов и пытается сломать инвариант.

Пример инварианта для lending протокола:

function echidna_total_assets_ge_liabilities() public view returns (bool) {
    return totalAssets() >= totalLiabilities();
}

Echidna найдёт последовательность deposit → borrow → liquidate → repay, которая нарушает этот инвариант. Руками такой кейс не построишь — комбинаций слишком много.

Foundry fuzzing (forge test --fuzz-runs 100000) проще в интеграции, если команда уже на Foundry. Поддерживает stateful fuzzing через invariant тесты. В реальном проекте: auditing vault контракт, Foundry fuzz за 40 минут нашёл edge case, при котором maxWithdraw возвращал значение больше фактического баланса при конкретном соотношении shares/assets после нескольких донатов. Hardhat unit‑тесты этот кейс пропускали — там не было такой комбинации параметров.

Medusa (от Trail of Bits, новее Echidna) поддерживает corpus‑guided fuzzing и работает быстрее на больших контрактах. Если объём кодовой базы > 5000 строк Solidity — смотрим на Medusa.

Как инварианты помогают выявить критические уязвимости

Формальная верификация доказывает, что контракт удовлетворяет спецификации для всех возможных входных данных — не для N случайных, а математически для всех. Инструменты: Certora Prover, K Framework, Halmos.

Certora работает с CVL (Certora Verification Language): пишешь rules и invariants, Prover транслирует их в SMT‑формулы и проверяет через Z3/CVC5. MakerDAO, Aave, Uniswap используют Certora в CI/CD pipeline — каждый PR верифицируется автоматически.

Ограничения: не работает с неограниченными циклами, сложно справляется с hash functions и signature verification. Для контрактов с простой математикой (AMM, lending) — отлично. Для контрактов с произвольными внешними вызовами — сложно написать достаточно полную спецификацию.

Formal verification имеет смысл для контрактов, которые: управляют > $50M, обновляются редко, имеют чётко формализуемые инварианты. Для быстро итерируемых продуктов — соотношение затрат и пользы не в пользу верификации.

Векторы атак, которые пропускают джуниор‑аудиторы

Storage collision в proxy паттерне. Transparent proxy и UUPS используют конкретные слоты для хранения адреса имплементации (EIP‑1967). Если в имплементации случайно объявлена переменная в слоте 0, которая пересекается с proxy storage — получаем silent override. Slither это не поймает, если proxy и имплементация в разных файлах.

Read‑only reentrancy. Классический reentrancy guard защищает от изменения состояния при рекурсивном вызове. Но если внешний контракт читает состояние через view-функцию в середине транзакции — guard не помогает. Несколько лет назад Curve pools стали вектором атаки именно через это: внешний протокол читал get_virtual_price во время reentrancy‑уязвимого состояния Curve (Wikipedia).

Oracle manipulation через TWAP. Spot price — стандартная цель для flash loan атаки (Wikipedia). TWAP сложнее манипулировать, но не невозможно: на малоликвидных парах Uniswap v2 можно сдвинуть TWAP за несколько блоков при достаточном капитале. Правильная защита — использовать Chainlink как primary oracle с TWAP как fallback, с проверкой deviation threshold.

Gas griefing на unbounded loop. Функция итерируется по массиву пользователей. Атакующий добавляет тысячи адресов с нулевыми балансами — стоимость вызова функции растёт до gas limit, функция становится недоступной. Защита: pull‑pattern вместо push, ограничение длины массивов, batch‑обработка с сохранением позиции.

Front‑running на MEV. Транзакция видна в mempool до включения в блок. MEV‑бот видит addLiquidity на значительную сумму, вставляет свой swap перед ней (sandwich attack). Для AMM это часть модели. Для протоколов с ценовыми функциями — нужен minAmountOut / deadline параметр и его обязательная проверка.

Структура полного аудита

  1. Scope definition и автоматический анализ (1‑2 дня). Фиксируем commit hash, версию компилятора, список out‑of‑scope. Запускаем Slither, Mythril, Aderyn. Triage: отделяем реальные критические баги от false positive. Составляем карту зависимостей контрактов.

  2. Ручной анализ (5‑15 дней). Каждый контракт построчно. Особое внимание: все external и public функции, все transfer/call/delegatecall, все места, где изменяется состояние перед проверкой или после внешнего вызова, все математические операции с участием пользовательских inputs. В среднем 95% найденных уязвимостей — логические, а не технические.

  3. Fuzzing и тестирование (2‑5 дней). Echidna или Foundry invariant tests для критических инвариантов. Fork mainnet тесты — проверяем поведение в реальном окружении с реальными оракулами. Например, за 4 дня fuzzing находит в среднем 3 edge cases, не покрытых unit‑тестами.

  4. Отчёт и митигация. Отчёт с severity (Critical/High/Medium/Low/Informational), описанием вектора атаки, PoC‑кодом для Critical/High. Разработчики исправляют, аудиторы делают re‑audit исправлений.

Severity Примеры Требует ли re‑audit
Critical Drain funds, unauthorized ownership transfer Всегда
High Manipulation, DoS на ключевые функции Всегда
Medium Некорректное поведение при edge cases Рекомендуется
Low Газ‑неэффективность, опечатки в events По желанию

Аудит в CI/CD

Нормальная практика для зрелых протоколов: Slither и Aderyn запускаются в GitHub Actions на каждый PR. Certora Prover — на merge в main. Это не заменяет полный аудит перед деплоем, но ловит регрессии.

# .github/workflows/audit.yml
- name: Run Slither
  uses: crytic/[email protected]
  with:
    target: 'src/'
    slither-args: '--filter-paths "test|mock|script"'
Чек‑лист обязательных проверок перед деплоем
  • Все external функции имеют проверки доступа (onlyOwner, onlyRole)
  • Использование SafeERC20 для внешних токенов
  • Отсутствие delegatecall на неизвестные адреса
  • Проверка на reentrancy во всех функциях с внешними вызовами
  • Наличие minAmountOut и deadline в AMM‑функциях
  • Использование проверенного оракула (Chainlink) с deviation threshold

Инструменты аудита: сравнение

Инструмент Тип анализа Что находит Ограничения
Slither Статический Reentrancy, integer overflow, access control Пропускает логические уязвимости
Mythril Symbolic execution Достижимые состояния с нарушением property Path explosion на больших базах
Echidna Fuzzing (property‑based) Нарушение инвариантов Требует написания инвариантов
Certora Formal verification Математическое доказательство свойств Не работает с хешами/подписями

Что входит в работу (deliverables)

  • Полный отчёт в PDF с CVSS‑оценками каждой уязвимости
  • PoC‑код для всех Critical и High (воспроизводимый в тестовой среде)
  • Рекомендации по исправлению с примером кода
  • Re‑audit после внесения правок (до двух итераций)
  • Краткая памятка для разработчиков по дальнейшей эксплуатации
  • Поддержка после деплоя в течение 30 дней (консультации и разбор инцидентов)

Сроки

Аудит простого токена или NFT‑контракта — 3‑5 рабочих дней. DeFi протокол с lending/AMM — 2‑4 недели. Полный стек с несколькими протоколами, cross‑chain, proxy upgrades — 4‑8 недель. Re‑audit исправлений — 3‑7 дней отдельно.

Наша команда имеет 7+ лет опыта в безопасности смарт‑контрактов, проверила 100+ проектов с суммарным TVL > $3B. Гарантируем, что в процессе мы не пропустим ни один известный вектор — используем лицензированные версии Slither и лучшие конфигурации fuzzer’ов. Предотвращённые убытки для клиентов оцениваются более чем в $50M.

Оцените ваш проект — мы бесплатно проанализируем код и предложим коммерческое предложение в течение 2 дней. Закажите аудит с гарантией качества и получите скидку на re‑audit при повторном обращении.