Интеграция MEV Blocker в dApp: защита от фронтраннинга и сэндвич-атак

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Интеграция MEV Blocker в dApp: защита от фронтраннинга и сэндвич-атак
Простой
~1 день
Часто задаваемые вопросы

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

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

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

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

Интеграция MEV Blocker: защита от сэндвич-атак и фронтраннинга

Ваши пользователи теряют до 15% на сэндвич-атаках при каждом крупном свапе. Валидаторы копируют ваши лимитные ордера и исполняют их раньше вас. Мы решаем эту проблему — интегрируем MEV Blocker в ваш dApp: приватный RPC-ендпоинт, который не даёт MEV-ботам увидеть транзакцию до её подтверждения.

Почему обычные RPC не защищают?

Публичные RPC (Infura, Alchemy) — все транзакции видны в мемпуле. MEV-боты сканируют мемпул и подбирают выгодные транзакции. Сэндвич-атака: бот видит ваш swap, покупает сам, ждёт ваш рост цены, продаёт. Результат — вы покупаете дороже, продаёте дешевле. MEV Blocker лучше публичных RPC в десятки раз по конфиденциальности: транзакция скрыта до включения в блок.

Как работает MEV Blocker?

MEV Blocker — это приватный RPC, который отправляет транзакции напрямую в private mempool (Flashbots, Eden, BloxRoute). Транзакция не публикуется в публичную мемпулу до момента включения в блок. Построители блоков могут видеть транзакцию, но они не могут её атаковать — они получают её только в момент построения блока.

// Пример переключения на MEV Blocker RPC в MetaMask
const provider = new ethers.providers.JsonRpcProvider('https://rpc.mevblocker.io');

// Или через EIP-1193
await window.ethereum.request({
  method: 'wallet_switchEthereumChain',
  params: [{ chainId: '0x1', rpcUrls: ['https://rpc.mevblocker.io'] }],
});

Что входит в нашу работу

Компонент Описание
Настройка RPC Установка приватного MEV Blocker RPC на стороне dApp
Интеграция в кошелёк Автоматическое переключение сети при подключении кошелька
Мониторинг транзакций Отслеживание успешности и скорости включения в блок
Обучение команды Передача документации и best practices по использованию

Почему выбирают нас

10+ лет опыта в блокчейн-разработке, 150+ успешных интеграций DeFi-протоколов, сертифицированные инженеры по Solidity и Rust. Гарантируем конфиденциальность — подписываем NDA. Оценим ваш проект за 1 день. Свяжитесь с нами для консультации — разберём вашу архитектуру, определим точки утечки MEV и предложим оптимальную схему интеграции.

Дополнительно: исходные блоки кода для защиты от flash loan атак (оставлены для справки)

Защита от flash loan атак (сопутствующий сервис)

Flash loan — это необеспеченный займ, который должен быть возвращён в той же транзакции. Если не возвращён — вся транзакция реверсируется. С точки зрения протокола, выдающего flash loan (Aave, Uniswap V3), это безрисковая операция: либо деньги вернулись, либо транзакция не произошла.

Проблема не во flash loan как таковых — это легитимный инструмент для арбитража, ликвидаций, рефинансирования. Проблема в том, что они дают атакующему временный доступ к огромному капиталу (сотни миллионов долларов) без залога. Если протокол принимает экономические решения на основе легко манипулируемых данных (spot price DEX, не-TWAP oracle) — одна транзакция с flash loan может принести атакующему миллионы.

Популярные взломы: Beanstalk ($182M), Cream Finance ($130M), Mango Markets ($114M). Общая черта: протоколы использовали данные, которые можно было сдвинуть одной транзакцией.

Анатомия flash loan атаки

Типичная атака состоит из четырёх шагов:

  1. Взять flash loan (например, 100M USDC из Aave)
  2. Манипулировать состоянием (pump/dump цены в DEX pool)
  3. Эксплойтить протокол (который читает манипулированные данные)
  4. Вернуть flash loan + fee, оставить profit

Конкретный пример — price oracle manipulation:

1. Flash loan: 50M DAI
2. Dump DAI в Uniswap V2 пуле DAI/ETH (spot price DAI падает)
3. Вызов протокола, который читает Uniswap V2 spot price для оценки collateral
   → Collateral в DAI теперь «дешевле», можно получить discount на ликвидацию
   или оценить долг в DAI как меньший
4. Прибыль → вернуть flash loan

Другой тип — governance flash loan:

1. Flash loan governance токенов
2. Моментальное создание пропозала + голосование с огромным весом
3. Исполнение пропозала (drain treasury)
4. Вернуть flash loan

(Именно так был атакован Beanstalk — атакующий одним governance голосом принял пропозал о переводе treasury себе.)

Защита 1: Price oracle — TWAP вместо spot

TWAP (Time-Weighted Average Price) — среднее арифметическое цены за период. Uniswap V2/V3 хранит cumulative price accumulators, из которых можно вычислить TWAP за произвольный период.

contract TWAPOracle {
    IUniswapV3Pool public pool;
    uint32 public constant TWAP_PERIOD = 30 minutes;
    
    function getTWAP() external view returns (uint256 price) {
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = TWAP_PERIOD; // 30 минут назад
        secondsAgos[1] = 0;           // сейчас
        
        (int56[] memory tickCumulatives,) = pool.observe(secondsAgos);
        
        int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
        int24 arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(TWAP_PERIOD)));
        
        // Конвертируем tick в price
        price = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);
        // ... конвертация sqrtPrice → human-readable
    }
}

Выбор периода TWAP — критичный параметр. Слишком короткий (1–5 минут) — атакующий с достаточным капиталом может удерживать манипулированную цену несколько блоков. Слишком длинный (4–8 часов) — TWAP сильно отстаёт от рынка в волатильные периоды, вызывая неправильные ликвидации.

Практика: 30 минут — разумный default для большинства DeFi протоколов. Для высоковолатильных активов — 1–2 часа.

Chainlink как основной oracle

Chainlink price feed — агрегированная цена от множества независимых узлов с heartbeat обновлением. Манипуляция требует компрометации большинства oracle нод — экономически нецелесообразно.

contract PriceConsumer {
    AggregatorV3Interface public priceFeed;
    uint256 public constant HEARTBEAT = 3600; // 1 час
    uint256 public constant MAX_STALENESS = HEARTBEAT * 2; // 2 часа — максимум
    
    function getPrice() external view returns (uint256) {
        (
            uint80 roundId,
            int256 answer,
            ,
            uint256 updatedAt,
            uint80 answeredInRound
        ) = priceFeed.latestRoundData();
        
        // Проверка staleness: данные не старее MAX_STALENESS
        require(block.timestamp - updatedAt <= MAX_STALENESS, "Stale price");
        
        // Проверка корректности раунда
        require(answeredInRound >= roundId, "Stale round");
        
        // Проверка положительности цены
        require(answer > 0, "Invalid price");
        
        return uint256(answer);
    }
}

Общая рекомендация: использовать Chainlink как primary oracle, Uniswap TWAP как sanity check. Если два источника расходятся более чем на X% — приостанавливать операции.

Защита 2: Snapshot voting power

Governance flash loan атаки используют тот факт, что voting power = текущий баланс токенов. ERC-20Votes решает это через checkpoint систему.

// Voting power фиксируется на блоке snapshot (до начала голосования)
uint256 votePower = token.getPastVotes(voter, proposalSnapshot);

// Flash loan ПОСЛЕ snapshot не даёт voting power
// Flash loan ДО snapshot требует удерживать токены через voting delay

Voting delay — минимальный период между созданием пропозала и началом голосования. Если voting delay = 2 дня, атакующий должен держать borrowed токены 2 дня — это экономически невыгодно (fee + opportunity cost).

// OpenZeppelin Governor
constructor(...) GovernorSettings(
    2 days,  // votingDelay — защита от flash loan governance attacks
    5 days,  // votingPeriod
    threshold
) {}

Beanstalk был атакован именно потому что не использовал voting delay: пропозал можно было создать и исполнить в одной транзакции.

Защита 3: Reentrancy guard и same-block checks

Некоторые flash loan атаки эксплойтируют reentrancy или same-block state manipulation.

Same-block checks
contract Vault {
    mapping(address => uint256) private _depositBlock;
    
    function deposit(uint256 amount) external {
        _depositBlock[msg.sender] = block.number;
        // ...
    }
    
    function withdraw(uint256 amount) external {
        // Нельзя deposit и withdraw в одном блоке
        require(
            _depositBlock[msg.sender] < block.number,
            "Flash loan protection: same block"
        );
        // ...
    }
}

Это блокирует паттерн: flash_loan → deposit → вызов функции которая читает баланс vault → withdraw → repay_loan.

Недостаток: legitimate пользователи тоже не могут deposit+withdraw в одном блоке. Для большинства протоколов это приемлемо.

Nonreentrant + view функции

nonReentrant защищает от reentrancy в state-changing функциях. Но view функции не защищены — их можно вызывать из середины другой транзакции.

Если view функция используется внешним протоколом для получения цены или TVL — манипуляция state через reentrancy меняет то, что видит эта view функция.

// УЯЗВИМО: состояние может быть манипулировано через reentrancy
function getSharePrice() external view returns (uint256) {
    return totalAssets() * 1e18 / totalSupply();
}

// totalAssets() читает баланс контракта — который может быть временно раздут
function totalAssets() public view returns (uint256) {
    return IERC20(asset).balanceOf(address(this));
}

Решение: хранить cached value total assets, обновляемое только в protected функциях.

Защита 4: Circuit breakers и rate limiting

Максимальный объём за транзакцию
uint256 public constant MAX_SINGLE_DEPOSIT = 1_000_000e6; // $1M max

function deposit(uint256 amount) external {
    require(amount <= MAX_SINGLE_DEPOSIT, "Exceeds single tx limit");
    // ...
}

Flash loan атаки обычно оперируют сотнями миллионов. Ограничение единичной транзакции снижает максимальный damage от любой атаки.

Pause механизм с автотриггером
contract ProtectedProtocol is Pausable {
    uint256 public lastTVL;
    uint256 public constant TVL_DROP_THRESHOLD = 20; // 20% за транзакцию
    
    modifier checkTVLAnomaly() {
        uint256 tvlBefore = totalValueLocked();
        _;
        uint256 tvlAfter = totalValueLocked();
        
        if (tvlBefore > 0) {
            uint256 dropPercent = ((tvlBefore - tvlAfter) * 100) / tvlBefore;
            if (dropPercent > TVL_DROP_THRESHOLD) {
                _pause();
                emit EmergencyPause(tvlBefore, tvlAfter, dropPercent);
            }
        }
    }
}

Circuit breaker: если за одну транзакцию TVL падает более чем на N% — протокол автоматически pausе. Это не предотвращает атаку, но ограничивает её масштаб.

Time-weighted balances

Вместо current balance использовать time-weighted average balance для критических расчётов:

// ERC-20Votes checkpoint подход применённый к liquidity
function getTimeWeightedLiquidity(address provider, uint256 lookback) 
    external view returns (uint256) 
{
    // Усреднённая ликвидность за lookback период
    // Манипуляция в одной транзакции минимально влияет на average
}

Мониторинг on-chain

Система защиты неполна без мониторинга. Forta Network — decentralized detection network с ботами, которые мониторят on-chain активность.

// Forta бот: детекция потенциальной flash loan атаки
async function handleTransaction(txEvent) {
    const findings = [];
    
    // Проверяем наличие flash loan calldata в транзакции
    const flashLoanCalls = txEvent.filterFunction([
        'flashLoan(address,address,uint256,bytes)',
        'flash(address,address,uint256,uint256,bytes)'
    ]);
    
    if (flashLoanCalls.length > 0) {
        // Проверяем значительные изменения state нашего протокола
        const protocolEvents = txEvent.filterLog(PROTOCOL_EVENTS, PROTOCOL_ADDRESS);
        
        if (protocolEvents.length > 0) {
            findings.push(Finding.fromObject({
                name: "Flash loan + protocol interaction",
                description: `Flash loan detected in same tx as protocol events`,
                alertId: "FLASH-LOAN-INTERACTION",
                severity: FindingSeverity.Medium,
                type: FindingType.Suspicious
            }));
        }
    }
    
    return findings;
}

Алерты из Forta можно отправлять в PagerDuty / Telegram через webhook, давая команде 1–2 минуты на ответ до распространения атаки.

Комплексная архитектура защиты

Ни одна из мер в отдельности не является достаточной. Эффективная система защиты — это слои:

Уровень Механизм Защищает от
Oracle Chainlink primary + TWAP sanity Price manipulation
Governance Voting delay (2+ дней) + ERC-20Votes Flash loan governance
State Same-block check на withdraw Deposit-exploit-withdraw
Flow Max per-tx limits Damage limitation
Circuit breaker Auto-pause при TVL anomaly Early stop на атаку
Monitoring Forta bots Detection и алертинг

Разработка полной системы защиты: аудит существующих oracle зависимостей и governance механизмов — 1 неделя, разработка защитных контрактов — 2–3 недели, интеграция мониторинга — 1 неделя, тесты атак в fork environment — 2 недели.

Стоимость зависит от сложности протокола и количества точек oracle integration.

Ссылки

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

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