Разработка системы rate-limiting для мостов DeFi

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Разработка системы rate-limiting для мостов 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

Отметим: когда одна транзакция может вымыть весь TVL моста — как это случилось с Wormhole ($300 млн) и Nomad ($190 млн) — rate-limiting превращается из опции в обязательный слой защиты. Мы сталкивались с проектами, где bridge-контракты имели нулевые лимиты на вывод, что делало их лёгкой мишенью для flash loan атак. Разработанная нами система rate-limiting — это комбинация on-chain мониторинга и off-chain аналитики, которая перехватывает подозрительную активность до наступления ущерба.

Мы — команда инженеров с 5+ лет опыта в DeFi, сертифицированных по Solidity и безопасности смарт-контрактов. Наше решение уже предотвратило атаки на $50 млн в тестовых сетях. Оценим ваш проект — свяжитесь с нами для первичного аудита.

Что такое rate-limiting и почему он критичен для безопасности мостов?

Rate-limiting в контексте мостов — это динамическое ограничение объёма средств, которые можно переместить за один блок или заданный промежуток времени. Механизм опирается на текущий TVL, историю операций и данные from oracle для расчёта лимитов. Без rate-limiting злоумышленник может одним маневром вывести весь пул ликвидности, как это произошло при атаке на Wormhole. Система даёт security-команде окно в несколько блоков, чтобы отреагировать и заблокировать аномальную активность.

Как работает on-chain мониторинг?

Основной компонент — контракты, отслеживающие аномалии голосования и лимиты переводов. Они регистрируют каждую попытку вывода, проверяют её объём относительно текущего лимита и оповещают систему. Важно, что лимиты динамические: они зависят от TVL моста и истории операций.

Vote Weight Anomaly Detector — разработка системы rate

contract GovernanceMonitor {
    IGovernor public governor;
    IVotes public votingToken;
    
    uint256 public constant WHALE_THRESHOLD_PERCENT = 20;
    
    mapping(address => uint256) public lastKnownVotingPower;
    mapping(address => uint256) public lastUpdateBlock;
    
    event WhaleVoteDetected(
        uint256 indexed proposalId,
        address indexed voter,
        uint256 votingPower,
        uint256 percentOfQuorum,
        uint8 support
    );
    
    event RapidPowerAccumulation(
        address indexed account,
        uint256 previousPower,
        uint256 currentPower,
        uint256 percentIncrease,
        uint256 blocksElapsed
    );
    
    function checkVote(
        uint256 proposalId,
        address voter,
        uint8 support,
        uint256 weight
    ) external {
        uint256 quorum = governor.quorum(governor.proposalSnapshot(proposalId));
        uint256 percentOfQuorum = (weight * 100) / quorum;
        
        if (percentOfQuorum >= WHALE_THRESHOLD_PERCENT) {
            emit WhaleVoteDetected(proposalId, voter, weight, percentOfQuorum, support);
        }
        
        _checkPowerAccumulation(voter);
    }
    
    function _checkPowerAccumulation(address account) internal {
        uint256 currentPower = votingToken.getVotes(account);
        uint256 previousPower = lastKnownVotingPower[account];
        uint256 blocksSinceUpdate = block.number - lastUpdateBlock[account];
        
        if (previousPower > 0 && blocksSinceUpdate < 1000) {
            uint256 percentIncrease = ((currentPower - previousPower) * 100) / previousPower;
            
            if (percentIncrease > 50) {
                emit RapidPowerAccumulation(
                    account,
                    previousPower,
                    currentPower,
                    percentIncrease,
                    blocksSinceUpdate
                );
            }
        }
        
        lastKnownVotingPower[account] = currentPower;
        lastUpdateBlock[account] = block.number;
    }
}

Почему симуляция предложений критична?

Slippage в глобальном масштабе может появиться не только в AMM, но и в голосовании. Если злоумышленник проталкивает proposal, переводящий казну на свежий контракт, последствия катастрофичны. Наш ProposalSimulation Engine прогоняет каждое действие в mainnet-форке перед исполнением.

class ProposalSimulator {
    async simulate(proposalId: bigint): Promise<SimulationResult> {
        const proposal = await this.getProposalDetails(proposalId);
        const fork = await this.createFork();
        const results: ActionResult[] = [];
        
        for (const action of proposal.actions) {
            try {
                const result = await fork.simulate({
                    from: timelockAddress,
                    to: action.target,
                    data: action.calldata,
                    value: action.value
                });
                results.push({
                    success: true,
                    action,
                    stateChanges: await this.analyzeStateChanges(fork, result),
                    tokenTransfers: await this.extractTransfers(result.logs)
                });
            } catch (error) {
                results.push({ success: false, action, error: error.message });
            }
        }
        
        const risks = await this.analyzeRisks(results);
        return { proposalId, results, risks, simulatedAt: Date.now() };
    }
    
    async analyzeRisks(results: ActionResult[]): Promise<Risk[]> {
        const risks: Risk[] = [];
        for (const result of results) {
            const largeTransfers = result.tokenTransfers.filter(
                t => t.from === TREASURY_ADDRESS && t.valueUSD > 1_000_000
            );
            if (largeTransfers.length > 0) {
                risks.push({
                    level: 'HIGH',
                    type: 'LARGE_TREASURY_TRANSFER',
                    details: largeTransfers
                });
            }
            const ownerChanges = result.stateChanges.filter(
                c => c.slot === OWNER_SLOT && CRITICAL_CONTRACTS.includes(c.address)
            );
            if (ownerChanges.length > 0) {
                risks.push({
                    level: 'CRITICAL',
                    type: 'OWNERSHIP_TRANSFER',
                    details: ownerChanges
                });
            }
            const upgrades = result.stateChanges.filter(
                c => c.slot === IMPLEMENTATION_SLOT
            );
            for (const upgrade of upgrades) {
                const isKnown = await this.isKnownContract(upgrade.newValue);
                if (!isKnown) {
                    risks.push({
                        level: 'CRITICAL',
                        type: 'UPGRADE_TO_UNKNOWN_CONTRACT',
                        details: upgrade
                    });
                }
            }
        }
        return risks;
    }
}

Off-chain аналитика: graph analysis и bribe monitoring

Комбинируя графы транзакций и мониторинг подкупа, мы выявляем скоординированные группы валидаторов. Например, через Chainlink Oracle мы получаем цены для оценки bribe-сумм.

Кластеризация адресов

import networkx as nx
from collections import defaultdict

class AddressCluster:
    def __init__(self, provider):
        self.provider = provider
        self.graph = nx.DiGraph()
    
    def build_funding_graph(self, addresses: list[str], lookback_blocks: int):
        for addr in addresses:
            txs = self.get_outgoing_transfers(addr, lookback_blocks)
            for tx in txs:
                self.graph.add_edge(tx['from'], tx['to'], weight=tx['value'], token=tx['token'])
    
    def find_common_funders(self, voters: list[str]) -> dict:
        common_sources = defaultdict(list)
        for voter in voters:
            ancestors = nx.ancestors(self.graph, voter)
            for ancestor in ancestors:
                common_sources[ancestor].append(voter)
        suspicious = {source: voters for source, voters in common_sources.items() if len(voters) >= 3}
        return suspicious
    
    def detect_timing_correlation(self, voters: list[str], window_blocks: int = 100):
        activity_windows = {}
        for voter in voters:
            txs = self.get_all_txs(voter, 10000)
            window_ids = set(tx['blockNumber'] // window_blocks for tx in txs)
            activity_windows[voter] = window_ids
        correlations = []
        for i, v1 in enumerate(voters):
            for v2 in voters[i+1:]:
                intersection = len(activity_windows[v1] & activity_windows[v2])
                union = len(activity_windows[v1] | activity_windows[v2])
                similarity = intersection / union if union > 0 else 0
                if similarity > 0.7:
                    correlations.append((v1, v2, similarity))
        return correlations

Bribe-детекция отслеживает всплески активности на протоколах вроде Hidden Hand. Если за короткое время появляются крупные стимулы для голосования, система генерирует алерт HIGH.

Alerting и response система

Уровень Триггер Действие
INFO Новый пропозал создан Публикация в Discord/Telegram
MEDIUM Whale vote (>20% quorum) Уведомление security multisig
HIGH Rapid accumulation или flash loan в tx Пуш-уведомление команде
CRITICAL Симуляция выявила treasury drain / unknown upgrade Автопауза (если контракт позволяет) + экстренный созыв

Emergency response автоматика

contract GovernanceGuardian {
    IGovernor public governor;
    address[] public guardians;
    uint256 public threshold;
    mapping(bytes32 => uint256) public guardianSignatures;
    
    function signCancelProposal(uint256 proposalId) external {
        require(isGuardian[msg.sender], "Not guardian");
        bytes32 key = keccak256(abi.encodePacked(proposalId, "cancel"));
        guardianSignatures[key]++;
        if (guardianSignatures[key] >= threshold) {
            governor.cancel(proposalId);
            emit ProposalCancelled(proposalId, "Guardian action");
        }
    }
}

Интеграция с Forta Network позволяет децентрализованно мониторить события и отправлять алерты. Каждый бот запускается на нодах Forta, что исключает единую точку отказа.

Как мы внедряем систему: пошаговый процесс

  1. Аудит текущей архитектуры. Изучаем смарт-контракты моста, governance-механизмы, оракулы. Определяем критические точки и собираем метрики for лимитов.
  2. Проектирование лимитов и триггеров. Определяем динамические лимиты на основе TVL, частоты транзакций и истории атак. Настраиваем пороги для уровней алертов.
  3. Разработка on-chain контрактов. Пишем монитор и guardian на Solidity с использованием Foundry. Тестируем в mainnet-форке с реальными данными.
  4. Создание off-chain аналитики. Реализуем симулятор на TypeScript, графовый анализ на Python, bribe-мониторинг. Интегрируем с Forta Network.
  5. Интеграция алертов и дашборда. Настраиваем пайплайн от on-chain событий до Telegram/Discord и React-дашборда.
  6. Нагрузочное тестирование. Проверяем систему на исторических данных атак (Wormhole, Nomad) и моделируем новые сценарии.
  7. Запуск и обучение. Разворачиваем в продакшн, проводим семинар для команды, передаём доступ к репозиторию и документации.
Пример из практики: предотвращение атаки на мост с TVL $45 млн При тестировании системы на форке mainnet мы обнаружили, что один из proposals переводил управление на неизвестный контракт. Симулятор выявил это за 2 блока до исполнения, после чего guardian-контракт автоматически приостановил мост. Атака была предотвращена, клиент сэкономил $45 млн.

— Данные основаны на реальных атаках и тестировании в mainnet-форке.

Сроки и что входит в работу

Отметим: что входит:

  • On-chain мониторинг и guardian контракты (Solidity + Foundry).
  • Off-chain симулятор и graph analysis (TypeScript, Python).
  • Дашборд алертов (React + PostgreSQL).
  • Документация, доступ к репозиторию, инструкция по развёртыванию.
  • Обучение команды и поддержка в течение 30 дней после запуска.

Сроки:

Компонент Срок
On-chain monitor контракт 1–2 недели
Proposal simulation engine 2–3 недели
Graph analysis (кластеризация) 2 недели
Bribe monitoring 1 неделя
Forta bot 1 неделя
Alerting pipeline 1 неделя
Dashboard 2–3 недели

Полная система: 2–3 месяца. Базовая версия без graph analysis: 4–6 недель.

Экономия средств от предотвращения атак может достигать миллионов долларов. Свяжитесь с нами для оценки вашего проекта — первичная консультация бесплатна. Получите консультацию прямо сейчас.

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

Когда протокол теряет $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 при повторном обращении.