Інтеграція MEV Blocker: захист від фронт- та сендвіч-атак

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1305 послуг
Інтеграція MEV Blocker: захист від фронт- та сендвіч-атак
Простий
~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 з Aave
  2. Маніпулювати станом (pump/dump ціни в DEX pool)
  3. Експлойтити протокол (який читає маніпульовані дані)
  4. Повернути flash loan + fee, залишити profit

Конкретний приклад — price oracle manipulation:

1. Flash loan
2. Dumping 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-withdra

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

Коли протокол втрачає значні кошти через 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 має сенс для контрактів, які: керують значним TVL, оновлюються рідко, мають чітко формалізовані інваріанти. Для продуктів, що швидко ітеруються, співвідношення витрат і користі не на користь верифікації.

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

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.

Oracle manipulation через TWAP. Spot price — стандартна ціль для flash loan атаки. 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 Виведення коштів, несанкціоноване перенесення власності Завжди
High Маніпуляція, 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 понад значну суму. Гарантуємо, що в процесі ми не пропустимо жоден відомий вектор — використовуємо ліцензовані версії Slither та найкращі конфігурації fuzzer'ів. Запобігнуті збитки для клієнтів оцінюються в значну суму.

Оцініть ваш проект — ми безкоштовно проаналізуємо код і запропонуємо комерційну пропозицію протягом 2 днів. Замовте аудит з гарантією якості та отримайте знижку на re‑audit при повторному зверненні.