Хук и антиснайп

Хук Uniswap v4 — это контракт, который PoolManager вызывает в определённые моменты свопа. Текущая имплементация StockFunHook использует шесть разрешений: beforeInitialize, beforeAddLiquidity, beforeSwap, afterSwap и два разрешения на дельты, которые позволяют ему забирать свою долю. С 2026-10-02 его адрес несёт все 14 разрешений v4; колбэки, которые он не использует, просто пропускаются.

Адрес кодирует разрешения

v4 читает младшие биты адреса хука, чтобы понять, когда его вызывать. Поэтому адрес не выбирается: он майнится через CREATE2, пока не найдётся значение, биты которого совпадают с заявленными разрешениями.

С 2026-10-02 майнится адрес прокси, со всеми 14 выставленными битами разрешений. Апгрейд заменяет имплементацию за этим адресом, не меняя его, поэтому заново майнить его никогда не нужно, а будущая имплементация может использовать любой колбэк. Текущая пропускает колбэки, которые не использует: они возвращают свой селектор и, там, где ожидается дельта, ноль. Инициализация прокси проверяет, что его адрес несёт каждый бит.

Практическое следствие: намайненный адрес зависит от байт-кода прокси и аргументов его конструктора, которые содержат адрес имплементации. Новый деплой придётся майнить заново. По этой же причине foundry.toml задаёт bytecode_hash = "none" — без этого намайненный адрес больше не совпадает с задеплоенным контрактом.

Разрешение beforeAddLiquidity, добавленное 2026-10-01, изменило сами биты: адрес был намайнен заново, и любой деплой, сделанный до этой даты, устарел. Прокси от 2026-10-02 снова их изменил: любой деплой, сделанный до этой даты, тоже устарел.

Взимание комиссии

При покупке комиссия взимается в beforeSwap из входящего ETH, до того как произойдёт своп. Хук забирает свою долю из PoolManager и возвращает дельту, которая списывает её с покупателя. Роутер StockFun и лок ликвидности вносят ETH покупателя в PoolManager до свопа, поэтому их покупки никогда не задействуют ETH, который PoolManager уже держит.

Любой другой роутер v4 платит налог по той же ставке. Но у роутера, который вносит ETH покупателя после свопа, как это делает V4Router из v4-periphery с кодированием по умолчанию, налог берётся из ETH, который PoolManager уже держит, и его покупка не проходит, если налог больше этой суммы. Это может случиться, когда у PoolManager мало ETH, например в тестнете. Интегратору следует вносить ETH покупателя до свопа, как это делает официальный роутер. Продажи это не затрагивает. Это ограничение задокументировал пайплайн безопасности от 2026-10-01. Если вместо этого взимать налог в виде claims PoolManager, ограничение снялось бы для любого роутера, но 2 % казны перешли бы из выплачиваемых во время сделки в начисляемые и выплачиваемые позже: 2026-10-05 владелец решил оставить налог таким, каким он взимается сегодня.

При продаже она взимается в afterSwap из исходящего ETH, когда сумма уже известна.

В обоих случаях хук сразу делит комиссию: казна, создатель, команда, байбэк. Во время сделки уходит только строка казны, которая отправляется в волт рынка. Остальные три строки начисляются на хуке и выплачиваются вне сделок.

С 2026-10-05 волт, который не принимает строку казны, больше не срывает сделку: хук сохраняет сумму как долг перед этим волтом (treasuryOwed, событие TreasuryOwed), и все покупки и продажи продолжаются. Любой выплачивает этот долг волту через payTreasury, который не проходит и сохраняет долг, пока волт по-прежнему не принимает средства; кипер пытается в каждом цикле. Излишек антиснайпа подчиняется тому же правилу. До этого отправка падала с ошибкой: волт, который не мог принять средства, например после неудачного апгрейда, срывал каждую сделку на своём рынке, включая продажи.

Доля создателя работает по pull-модели: она накапливается на хуке, рынок за рынком, и создатель клеймит её, когда захочет, одной транзакцией на каждый рынок (claimCreatorFees). С 2026-10-05 создатель, который сам не может принять ETH, — контракт без способа его получить, — клеймит на другой адрес (claimCreatorFeesTo); это может сделать только создатель.

С 2026-10-01 доли команды и байбэка начисляются так же, по одному балансу на каждую, и выплачиваются через claimTeamFees() и claimBuybackFees(). Вызвать их может любой; они платят только кошелькам команды и байбэка, которые фабрика указывает на момент клейма. BuybackBurner сам забирает свой баланс в начале каждого сжигания. Кошелёк, который отказывается принимать ETH, лишь задерживает собственную выплату: его клейм не проходит, и баланс остаётся на хуке.

До этого эти две доли отправлялись во время сделки, с запасным вариантом через эскроу, если отправка не проходила. Аудит безопасности от 2026-09-29 показал, что кошелёк, который принимал отправку, а затем вызывал PoolManager, мог остановить торговлю во всех пулах. Эскроу больше нет.

То, что хук держит между сделками, кому-то причитается: балансы создателей, балансы команды и байбэка и долги перед волтами. ETH, отправленный ему кем-либо, кроме PoolManager, учитывается отдельно, как случайные поступления (strayEth). С 2026-10-05 владелец StockFun может выводить случайные поступления (rescue): не больше этой суммы ETH, и любой токен, поскольку хук никогда не держит токенов. Ничто из того, что хук должен, не может уйти этим путём. ETH, зачисленный принудительно, без вызова, не учитывается и ждёт апгрейда.

Ликвидность добавляет только лок

С 2026-10-01 beforeAddLiquidity отклоняет любое добавление ликвидности в пул StockFun, кроме добавления от лока ликвидности.

Позиция, размещённая вплотную к текущей цене, работает как лимитный ордер: своп трейдера пересекает её и конвертирует, и налог платит трейдер, тогда как владелец позиции добавляет и изымает её, так и не заплатив ни 5 %, ни, в первые десять блоков, антиснайп-налога. Аудит безопасности от 2026-09-29 это воспроизвёл. Пулы по умолчанию не взимают LP-комиссию, поэтому ни одному легитимному сценарию сторонняя позиция не нужна.

Собственные сборы комиссий лока, с дельтой ликвидности, равной нулю, проходят через путь изъятия ликвидности, который текущая имплементация пропускает: их это не затрагивает. Не затрагивает это и вывод ликвидности в режиме завершения, который забирает позиции через тот же путь: см. Запуск с двумя позициями.

Убывающий антиснайп

Пул v4 начинает работать в момент инициализации. Без защиты первые блоки после создания забрали бы боты. С 2026-09-28 в этих блоках действует повышенный налог, который снижается с каждым блоком, одинаково на покупках и продажах. С настройками по умолчанию:

Блок с момента запуска Налог
1 80 %
С 2 по 10 72, 64, 56, 48, 40, 32, 24, 16, 8 %
11+ Обычный режим, 5 %

Бот, который покупает в открывающем блоке, сразу платит 80 %: снайпинг убыточен.

Три момента заслуживают пояснения.

Покупке создателя при запуске не нужна идентификация. Единственный своп, который когда-либо выполняет LiquidityLock, — необязательная покупка создателя внутри его собственного колбэка создания. Поэтому «вызывающий — это лок» означает «мы внутри транзакции создания», и эта покупка платит обычные 5 %.

У излишка своё назначение. Обычные 5 % сохраняют привычную разбивку. Часть сверх них идёт в казну рынка, а значит, холдерам при следующем аирдропе. На рынке $STOCKFUN она идёт на баланс команды на хуке, который выплачивается через claimTeamFees().

А вот вайтлисту идентификация нужна, и в этом вся тонкость. Хук видит роутер, а не покупателя. Поэтому роутер StockFun передаёт адрес вызывающего в данных хука, и хук доверяет этому полю, только если вызывающий — официальный роутер, то есть тот, что записан в фабрике и который владелец может сменить в любой момент (принято 2026-09-28). tx.origin нигде в этом протоколе не используется.

Задокументированное ограничение: адрес из вайтлиста, который в первые десять блоков идёт через сторонний агрегатор, не получает исключения.

Вайтлист

Задаётся создателем рынка. Адреса из него в течение блоков антиснайпа платят обычные 5 %. До 2026-09-28 список вёл владелец, и он был общим для всех рынков — именно для того, чтобы он не мог стать инсайдерским преимуществом; теперь создатель может освободить от антиснайп-налога собственные кошельки. Утверждено 2026-09-28: список фиксируется в транзакции создания, публичен, неизменяем и по умолчанию ограничен 20 адресами.

Рынок $STOCKFUN

Убывающий налог действует и здесь. У $STOCKFUN нет внешнего создателя: его излишек идёт в комиссии команды, доступные для клейма, а его вайтлист задаёт владелец при запуске.

Настройки

С 2026-10-05 каждое число на этой странице — настройка владельца StockFun, которая меняется на хуке через setTaxSettings: налог, по умолчанию 5 %; его строки, 2 / 2 / 0,5 на запущенном рынке и 2 / 2,5 на $STOCKFUN, причём байбэк получает остаток; налог антиснайпа в открывающем блоке, 80 %; его снижение за блок, 8 пунктов; число блоков, в течение которых он действует, включая блок создания, 10; и наибольший вайтлист, 20 адресов.

Изменение применяется со следующего свопа, в каждом пуле, включая пул, который ещё находится в блоках антиснайпа: убывание рассчитывается по действующим настройкам, от блока запуска пула. При любых настройках антиснайп никогда не берёт меньше налога. Сеттер отклоняет ставку выше 100 % и схему, строки которой превышают налог. Лимит вайтлиста проверяется при создании рынка: уже заданный список остаётся как есть.

Апгрейды

С 2026-10-02 владелец StockFun может апгрейдить хук с немедленным вступлением в силу. Налог, его разбивка, антиснайп и вайтлисты, описанные на этой странице, относятся к текущей имплементации. Апгрейд сохраняет адрес хука и должен сохранить тот же PoolManager; фабрика, которую хук читает и которая решает, кто может его апгрейдить, зафиксирована в имплементации.