Интеграция 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 (например, 100M USDC из Aave)
- Манипулировать состоянием (pump/dump цены в DEX pool)
- Эксплойтить протокол (который читает манипулированные данные)
- Вернуть 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.







