Інтеграція 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 атаки
Типова атака складається з чотирьох кроків:
- Взяти flash loan з Aave
- Маніпулювати станом (pump/dump ціни в DEX pool)
- Експлойтити протокол (який читає маніпульовані дані)
- Повернути 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 |







