Інтеграція 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 |







