Интеграция с MEV Blocker

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Интеграция с MEV Blocker
Простая
~1 рабочий день
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы защиты от flash loan атак

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

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

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

Анатомия 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

Это наиболее распространённая и критичная защита.

Почему spot price уязвим

Uniswap V2/V3 spot price = текущее соотношение резервов. Большой swap в пуле мгновенно меняет spot price. В одной транзакции можно сдвинуть цену на 50–80% в пуле с умеренной ликвидностью.

TWAP (Time-Weighted Average Price)

TWAP — среднее арифметическое цены за период. 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.