Разработка Uniswap v4 хуков (hooks)
Uniswap v4 переворачивает архитектуру DEX: вместо тысяч отдельных пул-контрактов — один singleton PoolManager и бесконечно расширяемая логика через hooks. Это не просто refactor — это платформа, где кастомный хук может реализовать динамические комиссии, встроенный TWAP-оракул, автоматический rebalancing или пермишн-систему, не форкая ядро протокола. Мы разрабатываем production-ready хуки с полным покрытием тестами и audit-prep документацией.
Архитектура хуков: что важно понять до написания первой строки
Hook address как конфигурация
В v4 адрес хука — это его конфигурация. Последние 14 бит адреса кодируют, какие callback-функции реализует хук: beforeSwap, afterSwap, beforeAddLiquidity, afterAddLiquidity и другие — итого 14 флагов. PoolManager при регистрации пула читает адрес хука и знает, какие callback вызывать, без дополнительных on-chain запросов.
Следствие: нельзя просто задеплоить контракт и получить нужный адрес. Нужен mining — перебор salt в CREATE2, пока адрес не будет содержать правильные биты. Foundry позволяет это автоматизировать:
bytes32 salt = HookMiner.find(deployer, flags, creationCode, constructorArgs);
При неправильных флагах PoolManager.initialize делает revert с HookAddressNotValid. Это первое, на чём спотыкаются при переходе с v3.
BeforeSwap и AfterSwap: асимметрия контроля
beforeSwap может вернуть (bytes4 selector, BeforeSwapDelta delta, uint24 lpFeeOverride). Через delta хук может изменить количество токенов, участвующих в свапе — по сути, перехватить часть потока. Через lpFeeOverride — выставить динамическую комиссию в конкретном хопе вместо статической, заданной при инициализации пула.
afterSwap получает BalanceDelta — итог свапа — и может добавить собственную логику поверх. Типичный кейс: rebate хук, который возвращает часть fee активным LP в виде токенов.
Важный нюанс: хуки работают в контексте транзакции PoolManager. Все операции с токенами идут через flash accounting — реальные ERC-20 трансферы происходят только в конце через settle/take. Если хук пытается сделать прямой transfer внутри callback — это сломает flash accounting и вызовет revert или, хуже, некорректное состояние баланса.
Reentrancy в v4: новые правила
PoolManager защищён Lock модификатором — только один lock-вызов активен одновременно. Хук, пытающийся вызвать PoolManager.swap внутри beforeSwap, получит revert. Это означает, что архитектуры, где хук должен инициировать вторичный свап (например, автоматический rebalancing), требуют отложенного исполнения — через очередь во внешнем контракте или через afterSwap с последующим отдельным вызовом.
Как мы разрабатываем хуки
Типовые кейсы из практики
Динамические комиссии на основе волатильности. Хук читает TWAP из собственного аккумулятора (обновляется в afterSwap), вычисляет σ за последние N блоков, маппит σ на fee tier: низкая волатильность → 0.01%, высокая → 1%. lpFeeOverride возвращается в beforeSwap. LP автоматически получают защиту при резких движениях рынка без ручного вмешательства.
Whitelist хук для permissioned пула. В beforeSwap и beforeAddLiquidity проверяем merkleProof или ERC-721 balance свипера. Адреса без доступа получают revert. Используется для институциональных пулов с KYC-ограничениями.
LVR mitigation через auction. Before-swap хук реализует commit-reveal схему: MEV-боты должны сделать ставку за право первого свапа в блоке. Выручка от аукциона идёт в LP. Снижает Loss-Versus-Rebalancing для пассивных провайдеров ликвидности.
Стек и инструменты
Разработка — Foundry с v4-template от Uniswap. Тесты — fork на Ethereum Sepolia (v4 уже задеплоен на тестнете). HookTest базовый контракт предоставляет deployFreshManagerAndRouters — не нужно настраивать mock PoolManager вручную.
Для mining адреса хука — HookMiner из v4-periphery. В CI это выглядит как шаг перед тестами: майним salt, передаём в deployment script.
Верификация корректности флагов — отдельный тест:
assertEq(uint160(address(hook)) & HookFlags.ALL_FLAGS, expectedFlags);
Упавший тест сразу показывает, что адрес смайнен неправильно.
Аудит-специфика хуков
Стандартные Slither детекторы не знают про v4 callback-контракт. Пишем кастомные детекторы под конкретные паттерны:
- прямой ERC-20 transfer внутри callback (нарушение flash accounting)
- чтение
slot0напрямую вместо через PoolManager (устаревший паттерн из v3) - отсутствие проверки
msg.sender == address(poolManager)в callback-функциях
Последний пункт критичен: если хук не проверяет, кто вызывает beforeSwap, любой может вызвать его напрямую с произвольными параметрами. В зависимости от логики это может привести к манипуляции состоянием хука без реального свапа.
Процесс работы
Аналитика (2-3 дня). Формализуем логику хука: какие callback нужны, есть ли собственный storage, нужен ли доступ к oracle. Проверяем совместимость с существующей архитектурой пула.
Проектирование (2-3 дня). Схема взаимодействия хука с PoolManager, определение storage layout (минимизируем SLOAD в горячих путях), интерфейс для конфигурирования хука owner-ом.
Разработка (1-2 недели). Реализация callback-функций, тесты на fork Sepolia, fuzz-тесты на граничные значения входных параметров.
Аудит. Кастомные Slither детекторы + ручной review всех путей, где хук меняет BeforeSwapDelta. Документация инвариантов для внешнего аудитора.
Деплой. HookMiner в CI, deployment script через Foundry с верификацией флагов. Мониторинг через событийный лог PoolManager первые 72 часа после деплоя.
Ориентиры по срокам
Простой хук (single callback, только чтение) — 4-6 дней. Хук с динамическими комиссиями и собственным оракулом — 1-2 недели. Комплексный хук с аукционной механикой или собственной системой учёта LP-позиций — 3-4 недели.
Стоимость рассчитывается после анализа технического задания.







