Кипер
Офчейн-воркер, который раз в 24 часа запускает конвертацию каждой казны, а затем её аирдроп. Он ничего не решает. По умолчанию он делает проход каждые пять минут, а с 2026-10-06 конвертирует ETH волта раз за окно аирдропа, на своём первом проходе после закрытия окна: см. «Ритм конвертации» ниже. Его шаг аирдропа написан с 2026-10-05: см. ниже.
Что он делает
- Раз в 24 часа, после закрытия окна, по умолчанию в 13:00 UTC, до открытия торгов в США круглый год, находит волты, которые держат как минимум порог конвертации, по умолчанию 0,1 ETH, на своём первом проходе после закрытия; волт ниже порога тогда ждёт следующего окна, даже если сделки поднимут его выше порога позже в тот же день. Покупки цикла выполняются в течение следующей сессии, а его отправки, по умолчанию, — после окончания этой сессии. Деньги, которые волт ещё держит с более раннего цикла, USDC или USDG, идут дальше независимо от порога: с шестого цикла аудита, 2026-10-06, USDC волта — один раз после каждой его конвертации ETH и не чаще раза за окно в остальных случаях, USDG зеркального волта — на каждом проходе (см. «Ритм конвертации»)
- Предлагает маршруты свопа и рассчитывает минимальные суммы по свежей котировке; с 2026-10-05 волты сверяют то, что фактически приходит, с этими минимумами. С 2026-10-06 каждый волт в Ethereum котируется на собственном роутере — том, с которым он был создан
- Определяет размер каждого шага, чтобы площадка могла исполнить его в пределах границы волта (см. ниже)
- Вызывает конвертацию, батч моста, затем покупку в удалённой сети; с 2026-10-05 только он
может отправить батч моста (
bridgeReady). На каноническом канале он запрашивает комиссию батча по удвоенной последней базовой комиссии (quoteBridgeAt), а излишек ему возвращается. С девятого цикла аудита он переходит на котировку более старого хаба, только когда сам хаб отвечает, что у него её нет, и никогда — когда нода молчит: молчание задерживает батч на один проход — с оповещением на каноническом канале, комиссия которого следует за базовой комиссией, и лишь с предупреждением на канале USDG. С десятого цикла аудита, 2026-10-06, батч на канале USDG несёт не больше рынков, чем потолок адаптера, по умолчанию 17, который кипер читает при каждом батче: первыми идут рынки, которые ждут дольше всех, а остальные держат свои USDC в своих волтах до его следующего прохода, так что ни один не ждёт бесконечно (см. Канал Robinhood). До того каждый готовый рынок уходил в один батч, фиксированный газ compose которого большой батч мог исчерпать - Заранее деплоит зеркальный волт каждого нового рынка на Robinhood Chain до его первого
батча (
predeploy), что с 2026-10-05 могут делать только он и владелец StockFun. Удалённый хаб узнаёт кипера из батча, поэтому до первого батча волт первого рынка заранее деплоит владелец StockFun. Кипер заранее деплоит волт, только если его ключ на Robinhood Chain — тот, который удалённый хаб знает как кипера, или ключ администратора хаба. Рынок, волт которого кипер не может задеплоить заранее (до первого батча, после смены кипера, о которой удалённый хаб ещё не узнал, или когда предварительный деплой не проходит), исключается из батча с оповещением, а остальные рынки проходят через мост. Рынок, зеркальный волт которого кипер не может прочитать в этот день, ждёт следующего цикла, поскольку рынок, отправленный вслепую, мог бы сорвать весь батч - Отслеживает доставки: перевод, который не пришёл, тикет для повторной отправки, ещё не
задеплоенный зеркальный волт. Перевод, который он не смог измерить на Robinhood Chain в
момент отправки, подтверждается только собственными событиями удалённого хаба и никогда —
балансом, прочитанным позже, а на каноническом канале с 2026-10-06 так подтверждается
любой перевод: удалённый хаб выплачивает свои записи из общих денег, поэтому баланс
может вырасти за счёт денег другого батча. Эти события он читает по нескольку фрагментов
за раз, начиная с того места, где остановился поиск каждого перевода; с седьмого цикла
аудита, 2026-10-06, — только до блока на несколько блоков ниже последнего блока сети,
поэтому событие в самых новых блоках читается в одном из следующих циклов, а не теряется
из-за ноды, отстающей на блок или два. С восьмого цикла аудита он следит за батчем
моста, только когда блок, в котором тот находится, ушёл на столько же блоков вглубь
(
KEEPER_LOG_LAG_BLOCKS), по его квитанции, прочитанной тогда заново: идентификаторы батча на Robinhood Chain берутся из этого блока, и реорганизация самых новых блоков Ethereum, которая перемещала батч, оставляла кипера следить за идентификаторами, которые никогда не появятся. С десятого цикла аудита его оповещение о переводе, не зачисленном вовремя, сообщает на канале USDG, где находится сообщение батча на эндпоинте Robinhood Chain: ещё не доставлено туда; compose выполнен, и зачисление последует; или сохранено, и тогда его USDG лежит на удалённом хабе и нигде не записан, пока кто-нибудь снова не выполнит последний шаг вручную с большим газом, причём оповещение даёт хеш сохранённого сообщения. До того оно давало только то, что записывает удалённый хаб, чей ноль читался как «ничего не пришло», когда последний шаг, которому не хватило газа, оставил деньги на хабе - Сразу пересылает в свой вебхук оповещений каждое аварийное перемещение, которое видит,
на каждом волте, хабе моста, контракте аирдропа, удалённом хабе и каждом зеркальном
волте. С десятого цикла аудита он указывает отслеживаемые контракты группами, по одному
запросу логов на группу, каждый из которых указывает не более
KEEPER_LOG_MAX_SELECTORSадресов и значений событий (по умолчанию 1 000), и считает участок блоков пройденным, только когда прочитана каждая его группа. До того один запрос указывал их все: как только реестр рынков перерос то, что принимает эндпоинт (2 000 адресов и значений событий у нод Robinhood Chain, девять адресов у публичного эндпоинта Sepolia), каждый запрос отклонялся, и ни одно аварийное перемещение больше не пересылалось - С десятого цикла аудита читает то, что каждому проходу нужно о каждом рынке (ETH волта, его USDC и его окно, долги, последний аирдроп), через Multicall3, по сто рынков за вызов, на том же блоке, который использовало каждое чтение по отдельности; чтение, которое не вернулось, выполняется отдельно, как раньше, и никогда не принимается за ноль. До того каждый когда-либо созданный рынок стоил своих чтений по одному на каждом проходе, активный или нет, и 2 000 неактивных рынков растягивали проход на весь интервал между двумя проходами
- На каноническом канале, с шестого цикла аудита, следит за обоими тикетами каждого батча,
пока не узнает, что каждый из них исполнен, независимо от зачисления его перевода, в
каждом цикле, в сессию или нет, и повторно отправляет ещё живой тикет. О тикете, об
исполнении которого не известно через шесть часов после отправки его батча, по умолчанию
(
KEEPER_TICKET_ALERT_HOURS), подаётся одно оповещение; так же — о тикете, исчезнувшем в свой срок или позже без замеченного исполнения, или о тикете, создание которого не прошло. До того кипер переставал следить за тикетами батча, как только его рынки были зачислены, из-за чего депозит, пропустивший своё автоматическое исполнение, мог истечь. С седьмого цикла аудита он читает каждый тикет в том порядке, в каком это делает Arbitrum SDK: сначала квитанцию его создания, затем его автоматическое исполнение, затем то, существует ли он ещё на блоке не раньше его создания; с восьмого цикла аудита — на блоке на несколько блоков ниже последнего (KEEPER_REMOTE_LOG_LAG_BLOCKS) или на блоке его создания, если тот позже, — на блоке, который есть у каждой ноды эндпоинта. Депозит, созданный между двумя его чтениями или увиденный двумя нодами с разницей в блок, никогда не принимается за исполненный, пока он ещё жив - Подаёт оповещение после повторяющихся сбоев на волте, считая его шаг на Ethereum и его шаг на Robinhood Chain по отдельности, чтобы о волте, который каждый день даёт сбой на одной стороне, сообщалось. С седьмого цикла аудита он ждёт свой вебхук оповещений не более десяти секунд и читает его ответ: оповещение, которое вебхук не принял, сохраняется, не более ста, в его файле состояния и отправляется снова в следующих циклах, поэтому оно приходит поздно, а не никогда. Сообщение несёт поля, которые читают Slack и Discord. С восьмого цикла аудита хранится одна запись на каждое отдельное оповещение: оповещение, поданное снова, пока оно ждёт (волт, дающий сбой в каждом цикле), учитывается, а не хранится дважды. Сообщение говорит, когда оповещение было подано впервые и в последний раз и сколько раз, а оповещение, доставленное с опозданием, начинается с того, что сообщает об этом. С девятого цикла аудита сохранённые оповещения уходят в том порядке, в каком они подавались в последний раз: оповещение, поданное снова, перемещается в конец, поэтому последнее, что сказано о предмете, — его последнее состояние (мониторинг, который отказывает, восстанавливается и снова отказывает, пока вебхук недоступен, последним доставляется как «отказывает», тогда как раньше всё заканчивалось на «восстановлен»). Сверх сотни первым отбрасывается только оповещение, подаваемое в каждом цикле, пока длится его причина (волт, который не может провести конвертацию, батч моста, который не проходит), поэтому разовое оповещение, например об аварийном перемещении, и последнее слово каждого эпизода никогда не вытесняются. С десятого цикла аудита текст ошибки, в его логе, его оповещениях и его файле состояния, содержит от адреса RPC только схему и хост, никогда — остальное, куда провайдеры помещают ключ
- С девятого цикла аудита в течение минуты после того, как одна из его собственных транзакций смайнена, читает то, что эта транзакция изменила, на её блоке, а не на последнем, и там же оценивает газ того, что отправляет следом. Тестнетовый запуск показал, зачем это нужно: батч моста, отправленный сразу после конвертации того же прохода, симулировался на ноде эндпоинта с балансировкой нагрузки, отстававшей от конвертации на один блок, не увидел ничего для отправки через мост и вызвал ложное оповещение. Нода, отстающая от этого блока, теперь отвечает ошибкой, и батч ждёт один проход с предупреждением. Каждая транзакция кипера также уходит с оценкой газа, умноженной на 1,25, поскольку апгрейд Ethereum Glamsterdam сделал вызовы внутри его транзакций тяжелее
- С девятого цикла аудита время, прочитанное из сети, которое не может вместить ни одна дата (начало фида, конец окна, срок тикета), читается как «an unknown time», а не срывает чтение, которое на него наткнулось
- С седьмого цикла аудита перед своим первым циклом проверяет, что каждый RPC обслуживает сеть, указанную в его конфигурации, и иначе отказывается запускаться. Неверная сеть, обнаруженная позже, останавливает работу в этой сети с оповещением; сеть, идентификатор которой нельзя прочитать, ждёт, а работа в другой сети продолжается. С восьмого цикла аудита должны быть заданы оба идентификатора сети (см. ниже), а файл состояния, записанный для другой сети, остаётся нетронутым, пока кипер не прочитает, какую сеть обслуживает его RPC (см. «Перезапуски»)
- С восьмого цикла аудита читает две защиты ценовых фидов оракула зеркальных волтов на Robinhood Chain: пока оракул придерживает каждую цену из-за секвенсора, удалённые покупки ждут, с одним оповещением, а акция в корпоративном действии ждёт отдельно (см. «Когда оракул придерживает цены»)
- Выплачивает через sweep деньги, ожидающие на удалённом хабе, на обоих каналах: записи, токены которых уже пришли, и то, что дошло до хаба, пока он был на паузе. На канале USDG он воздерживается от sweep, пока удалённый хаб ожидает от владельца StockFun урегулирования расчётов после аварийного перемещения, о чём сообщает один раз, и возобновляет его, когда расчёты урегулированы. С 2026-10-05 он также выполняет sweep доли, которую зеркальный волт не принял, как только этот волт снова принимает деньги; на каноническом канале он отправляет такой sweep, только если тот что-то выплатит
- Запускает
BuybackBurner, учитывая баланс байбэка, начисленный на хуке, который бёрнер забирает перед покупкой - Раз за окно, по умолчанию после сессии в США, отправляет акции каждого рынка в контракт аирдропа, никогда не задавая само распределение: см. ниже
- С 2026-10-05 в каждом цикле, независимо от сессии, выплачивает то, что хук и лок
ликвидности удерживают для получателя, который это не принял: то, что хук должен волту
рынка (
payTreasury), и доли сбора комиссий, которые лок удерживает для волта или создателя (payOwed). Каждая выплата сначала симулируется и отправляется, только если что-то выплачивает. Оба вызова открыты для любого - С 2026-10-05 собирает LP-комиссии пула, созданного с такой комиссией (
collectFees, открыт для любого), не чаще раза в день на пул. Пулы StockFun по умолчанию не взимают LP-комиссию, поэтому по умолчанию собирать нечего
Ритм конвертации
Основатель решил 2026-09-27, что ETH волта конвертируется раз за ежедневный цикл, непосредственно перед аирдропом, и только если волт держит порог при этой проверке. Кипер придерживается этого с 2026-10-06; до того он конвертировал ETH волта на каждом проходе сессии, как только волт держал порог.
- Окно. Цикл — это окно аирдропа: оно закрывается, когда так говорит контракт аирдропа
(24 часа, закрывающиеся по умолчанию в 13:00 UTC), включая рынок
$STOCKFUN; пока фабрика не указывает контракт аирдропа — по этому расписанию по умолчанию - Одна проверка за окно. Кипер по-прежнему по умолчанию делает проход каждые пять минут, и каждый проход заканчивается отчётом цикла. Порог проверяется на первом проходе после закрытия окна, который доходит до волта. Если баланс на пороге или выше, ETH конвертируется, один раз: собственная запись волта о его последней конвертации ETH, прочитанная из блокчейна, сообщает об этом следующим проходам, поэтому перезапуск ничего не меняет. Ниже порога ETH ждёт следующего окна, даже если сделки поднимут его выше порога позже в тот же день; кипер хранит эту проверку в своём файле состояния. С 2026-10-06 он хранит её как конец окна, для которого была сделана проверка, а не как время на собственных часах, которые, немного отставая или спеша около закрытия, могли разойтись с окном блокчейна; проверка, записанная более старым кипером, не считается, поэтому в окне обновления такой волт проверяется заново. С шестого цикла аудита проверка читает баланс волта после закрытия окна: проход, который шёл через момент закрытия, записывал баланс, прочитанный до него, и волт, вовремя пересёкший порог, пропускал это окно
- Конвертация, которая не может уйти при проверке (молчащий фид ETH/USD, площадка, которая не может исполнить её в пределах границы волта, не прошедшая транзакция, окно, которое кипер не может прочитать), повторяется на следующих проходах того же окна. Окно, которое он не может прочитать, считается сбоем шага ETH, о котором подаётся оповещение после повторяющихся сбоев, а USDC волта всё равно движется
- USDC, с шестого цикла аудита. USDC, который уже лежит в волте, идёт дальше независимо от порога, в собственном ритме: его покупка в Ethereum или его передача в батч моста уходит один раз после каждой конвертации ETH волта и не чаще раза за окно в остальных случаях (подарок, возврат, то, что оставил не прошедший или уменьшенный вдвое шаг). Учитывается только прошедший шаг; не прошедший повторяется на следующих проходах. Так решение основателя от 2026-09-27 распространяется на USDC: до того несколько единиц USDC, отправленных на волт перед каждым проходом, заставляли кипера на каждом проходе отправлять покупку или целый батч моста с его комиссией
- Что уходит на каждом проходе: покупки на Robinhood Chain. Баланс зеркального волта не отличает доставку от подарка, а лимиты каждой покупки намеренно распределяют крупную доставку на несколько проходов. ETH, который оставляет уменьшенная вдвое конвертация (площадка не смогла исполнить весь баланс в пределах границы), ждёт следующего окна
- Локальные и тестнетовые запуски выставляют
KEEPER_CONVERT_ONCE_PER_WINDOWв false и тогда конвертируют, покупают и отправляют через мост на каждом проходе; во всех остальных случаях она остаётся включённой — это её значение по умолчанию
В остальной части этой страницы «в каждом цикле» означает на каждом проходе кипера, как в отчёте цикла; окном является только ежедневный цикл аирдропа.
Каждый сбой отдельно
С 2026-10-05 контракты позволяют одному звену, одной акции, одному рынку или одной доставке не пройти, не останавливая остальные, и сообщают о том, что не прошло, событием. Кипер читает эти события и подаёт оповещение о том, что не проходит устойчиво; его собственная работа разделена так же.
- Звено покупки. Звено каждой акции планируется отдельно: звено, которое нельзя
спланировать, пропускается, а остальные звенья идут. Звено, о сбое которого сообщает волт
(
LegFailed), сохраняет свои деньги зарезервированными для своей акции; кипер никогда не считает его сконвертированным и подаёт оповещение после трёх сбоев подряд этой акции в этом волте (KEEPER_ALERT_AFTER_FAILURES). Когда у волта нельзя спланировать ни одного звена, волт не проходит целиком, как и раньше - Рынок батча моста. Рынок, который хаб моста исключает из батча (
MarketSkipped), сохраняет свои USDC в своём волте до одного из следующих батчей. Если он исключён из трёх батчей подряд (KEEPER_BRIDGE_ALERT_AFTER_SKIPS), о нём один раз подаётся оповещение, с декодированной причиной и способом её устранения. С десятого цикла аудита рынок, который потолок адаптера оставляет вне батча, ждёт следующего прохода, о чём говорится в логе, и идёт первым в том батче; о батче, который адаптер отклоняет (сверх своего потолка или у адаптера, получившего апгрейд без своих настроек батча), подаётся оповещение с названной причиной и собственными рынками батча - Отклонённая доставка. О доле, которую денежный токен отклонил для зеркального волта
(
DeliveryRefused), подаётся одно оповещение, со способом устранения, на всё время, пока удалённый хаб ещё что-то должен этому рынку из этой доли - Нечитаемый волт. Волт, собственные view-функции которого не отвечают, например после
сломанного апгрейда, исключается из этого цикла; остальные волты конвертируют. О нём один
раз подаётся оповещение после трёх циклов подряд (
KEEPER_ALERT_AFTER_FAILURES) - Долг, который остаётся. Пока волт, или хук в случае доли создателя, всё ещё не принимает причитающийся ему ETH, кипер один раз за эпизод подаёт оповещение о том, что пул ожидает владельца StockFun (исправляющего апгрейда), и записывает в лог окончание эпизода, когда долг выплачен — кипером или кем-либо ещё
То, что может урегулировать только владелец StockFun, — это «ожидает администратора»: одно оповещение за эпизод, никогда не засчитываемое как сбой, а остальное тем временем продолжается.
В отчёте цикла появились выплаченные и всё ещё причитающиеся долги, звенья, которые не
прошли, пулы, LP-комиссии которых были собраны, а для шага аирдропа — открытые циклы,
выполненные поиски для отложенных акций, отправившие волты и доставки, ещё находящиеся в
пути. С 2026-10-06 отправка в окно без допустимого владения, которую контракт аирдропа
откладывает, учитывается отдельно (airdropHeldAside), а не вместе с отправившими
волтами; на каноническом канале отчёт учитывает отправленные повторы тикетов (redeemed)
и тикеты, за которыми ещё ведётся слежение (ticketsWatched). С седьмого цикла аудита он
также учитывает рынки, отправка в аирдроп которых ждёт, пока не окупит свою стоимость
(airdropBelowCost), и оповещения, сохранённые для вебхука (alertsUndelivered), и
сообщает, когда идентификатор сети ещё не проверен (deferred: chain-unverified).
Когда оракул придерживает цены
С 2026-10-06 оракул зеркальных волтов может придержать цену (см. Канал Robinhood), а с восьмого цикла аудита кипер читает его, прежде чем что-либо котировать:
- Секвенсор. Раз за цикл он спрашивает оракул, что говорит его проверка секвенсора. Пока секвенсор не работает, восстановился не дольше льготного периода назад или его фид аптайма нельзя прочитать, ни одну акцию нельзя оценить: покупка волта ждёт, ничего не котируется и ничто не считается сбоем. Эпизод открывает одно оповещение, сохранённое в файле состояния, чтобы перезапуск не подал его снова, и ещё одно сообщает, когда покупки возобновляются. Проверка выключена на Robinhood Chain, пока Chainlink не опубликует для неё фид аптайма, поэтому сегодня ничего из этого произойти не может
- Корпоративное действие. Акция, токен которой ставит свой оракул на паузу, заранее исключается из покупки, с записью в логе, а её USDG сохраняется для неё; она никогда не засчитывается в оповещение о сбоях, а остальные акции корзины покупаются. Звено, о сбое которого по этой причине сообщает волт между планом кипера и его покупкой, ожидаемо и тоже не засчитывается
- Причина называется. Там, где границу звена нельзя прочитать, потому что защита
придерживает его цену, кипер записывает в лог эту причину, в любой из двух сетей, а не
волт, который не может оценить звено. Защита, которую он не может прочитать (оракул из
версии до защит), ничего не придерживает: решает собственная граница каждого звена, как
раньше. С девятого цикла аудита декодируются и остальные ошибки оракула: устаревший фид
читается в логе и в причине непрошедшего звена как
StalePrice, тогда как раньше там стоял недекодированный код
Шаг аирдропа
Сторона контракта написана с 2026-10-04, а шаг кипера — с 2026-10-05. В каждом цикле, до
проверки сессии, кипер сначала отслеживает уже отправленные доставки. Затем, пока фабрика
указывает контракт аирдропа, он берёт каждый рынок по очереди, включая рынок $STOCKFUN:
- Раз за окно. Волт закончил с окном, когда его последняя отправка (
lastAirdropAt) приходится на конец окна, в которое отправка попала бы сейчас (currentCycleEnd), или позже. Оба значения читаются из блокчейна, поэтому перезапуск ничего не меняет. По умолчанию кипер отправляет, только когда дневная сессия в США закончилась, чтобы покупки дня ушли одной отправкой, в окно, закончившееся в тот день, и только когда волт держит акции; с седьмого цикла аудита — только акции, которые стоят того, во что обходится их отправка (см. ниже). С десятого цикла аудита, при этом значении по умолчанию, волт, прочитанный после закрытия сессии, которому нечего отправлять, читается снова только при открытии следующей сессии или когда сдвигается окно, на каналах, покупки которых следуют за нью-йоркской сессией (не на канале Ondo): то, что он держит, приходит от его покупок, совершаемых в сессии. Но не пока его собственная покупка ещё ждёт своей квитанции, которая может прийти вечером: такой волт читается на каждом проходе, и то, что приносит покупка, уходит в окно, закончившееся в тот день - Сначала отложенные акции, даже если нечего отправлять. Если у рынка есть отложенные
акции —
assignUnassigned, не более пяти вызовов за цикл, пока они не будут размещены, что открывает цикл окна, которое их принимает, или не останется завершившихся окон для проверки. С 2026-10-06 кипер выполняет этот поиск независимо от того, есть ли у волта что отправить, и независимо от паузы волта: до того он выполнялся только перед отправкой, поэтому рынок, на котором однажды прошла сделка и который затем затих, держал свой первый аирдроп, отложенный, вне досягаемости своих холдеров до новой сделки или пока кто-нибудь не выполнял поиск вручную. Незаконченный поиск задерживает до следующего цикла отправку в окно без допустимого владения. С шестого цикла аудита, пока волту нечего отправлять, поиск, который ничего не разместил бы (ни одно окно ещё не может принять акции), отправляется только тогда, когда поиск рынка отстаёт от последнего окна наmaxWindowsPerAssignокон, по умолчанию 30: один поиск в месяц вместо одного в день навсегда. Поиск, который размещает акции, уходит всегда. С седьмого цикла аудита «нечего отправлять» означает, что нет ничего, что стоило бы отправлять, по тому же правилу, что и для отправки: на канале через мост пыль, которую OFT не может перенести и которую каждая отправка оставляет в зеркальном волте, не считается, и лимит действует и там - Цикл, только перед отправкой.
openCycle, чтобы каждая доставка лишь добавлялась к нему; никогда — для окна, в которое ничего не отправляется, а с седьмого цикла аудита — только для отправки, которая окупает свою стоимость, с учётом открытия. Окно без допустимого владения — не ошибка: тогда акции становятся отложенными - Отправка. На локальном канале —
sendToAirdropнаTreasuryVaultрынка. На канале через мост —sendToAirdropна зеркальном волте, с оплатой котируемой комиссии LayerZero (quoteSendToAirdrop) плюс запас, по умолчанию 10 %; волт возвращает излишек. С девятого цикла аудита отправка и каждая её котировка несут газ, который её доставки получают на Ethereum и который выбирает кипер (см. ниже). Перед этой отправкой кипер проверяет, что удалённый хаб отправляет в контракт аирдропа фабрики, что у каждой акции есть адаптер и что этот адаптер отвечает, и что OFT акции на Ethereum зарегистрирован в контракте аирдропа. С седьмого цикла аудита отправка перечисляет только акции, которые окупают свою стоимость, а её комиссия котируется заново для них - Доставки. На канале через мост доставка каждой акции отслеживается, пока эндпоинт LayerZero на Ethereum не выполнит её последний шаг, который зачисляет поступление в контракт аирдропа. С девятого цикла аудита доставку, застрявшую на этом эндпоинте, кипер сам выполняет повторно (см. ниже). Доставка, не зачисленная в течение 60 минут по умолчанию, вызывает одно оповещение с командами, которые выполняют её шаги вручную; выполнить их может любой
Каждая акция идёт отдельно: акция, баланс которой нельзя прочитать (заморозка эмитентом),
акция без адаптера или с незарегистрированным OFT, акция, адаптер которой не отвечает
(peers(): по его адресу нет контракта или это не приложение LayerZero; с 2026-10-06, до
того это срывало всю отправку рынка), с восьмого цикла аудита — акция, комиссию LayerZero
которой её адаптер не может котировать (см. ниже), и акция, которую, по сообщению волта,
он не смог отправить (AirdropSendFailed), исключаются, с одним оповещением, а остальные
уходят. Каждый рынок тоже идёт отдельно: о рынке, который продолжает давать сбой, подаётся
одно оповещение после трёх сбоев подряд, а следующий рынок продолжает. С 2026-10-06 у
поиска отложенных акций, который продолжает давать сбой, есть собственное оповещение,
которое указывает способ устранения: любой может вызвать assignUnassigned для этого
рынка.
«Ожидает администратора» здесь охватывает контракт аирдропа на паузе, акцию, которой контракту аирдропа не хватает после аварийного перемещения (эта акция ждёт в волтах, остальные уходят), удалённый хаб, который отправляет в другой контракт аирдропа, а не в контракт фабрики, а с девятого цикла аудита — и удалённый хаб, границы газа доставки которого не заданы или потолок которого ниже того, что нужно доставке.
Что стоит отправлять
С седьмого цикла аудита, 2026-10-06, кипер отправляет только то, что стоит того, во что обходится его отправка. До того уходила любая акция выше нуля: подарок чуть выше пыли на волт рынка, которым никто не торгует, заставлял кипера каждый день открывать цикл этого окна и оплачивать сообщение LayerZero или отправку на Ethereum.
- Что несёт отправка. Акцию, баланс которой можно прочитать и который больше нуля; на канале через мост у неё также должны быть адаптер и собственная котировка больше нуля. Котировка волта одинаково равна нулю и для пыли, которую OFT не может перенести, и для акции, адаптер которой не может котировать её отправку, поэтому с восьмого цикла аудита кипер спрашивает сам адаптер акции — на той отправке, которую построил бы волт: пыль никогда не отправляется и о ней никогда не подаётся оповещение; акция, котировка которой не проходит и собственная отправка которой не прошла бы, исключается с одним оповещением, объясняющим причину; а акция, о которой кипер не может спросить, уходит как раньше, и решает её отправка. До того такую акцию принимали за пыль и молча исключали из каждой отправки
- Её стоимость. Каждая акция оценивается собственным оракулом её волта, по последнему ответу её ценового фида, устаревшему или нет, поскольку это оценка, а никогда не граница; затраты, оплачиваемые в ETH, переводятся в доллары по фиду ETH/USD того же оракула
- Её собственные затраты. Каждая акция должна стоить не меньше собственных затрат: своей комиссии LayerZero на канале через мост, где каждая акция — отдельное сообщение, или своей доли газа отправки на локальном канале. Подарок, который не стоит собственного сообщения, никогда не уходит вместе с настоящей отправкой
- Вся отправка. Прошедшие акции вместе должны стоить не меньше всей отправки плюс
открытия окна (
openCycle), если его цикл ещё не открыт. На локальном канале, с восьмого цикла аудита, открытие учитывается один раз: собственная оценка отправки, сделанная, пока цикл закрыт, уже его включает, поэтому добавляется только базовая стоимость транзакции открытия; учтённое дважды, оно на дни задерживало отправку, стоившую немного больше своих затрат. Иначе не уходит ничего, и цикл не открывается - Что ждёт. То, что не уходит, остаётся в волте и уходит в одном из следующих окон вместе с тем, что накопится. Рынок, отправка которого ждёт, учитывается в отчёте цикла и записывается в лог раз за окно
- Что нельзя прочитать. Цена, комиссия или стоимость газа, которую кипер не может прочитать, пропускает отправку, как до этого правила: непрошедшее чтение никогда не задерживает настоящую отправку
KEEPER_AIRDROP_MIN_VALUE_BPS задаёт множитель: по умолчанию 10 000, однократная
стоимость, поэтому отправка, которая стоит не меньше своих затрат, уходит, каким бы ни был
её размер сверх них; большее значение задерживает меньшие отправки, пока они не будут
стоить этого кратного, а 0 отправляет всё, что можно перенести. То же правило решает, есть
ли у волта что отправить, для поиска отложенных акций выше. Аудит принял его как
рекомендованное по умолчанию; оно решает только, когда уходит акция, но никогда — сколько,
что или куда.
Газ доставки на Ethereum
2026-10-06 Sepolia активировала апгрейд Ethereum Glamsterdam, из-за которого новый слот хранилища стоит примерно в пять раз больше газа, чем раньше, и каждой доставке аирдропа в первом цикле тестнетового запуска LayerZero не хватило газа у исполнителя LayerZero: отправка оплачивала газ только последнего шага доставки, а газом её первого шага было то, что принудительно задаёт владелец адаптера LayerZero этой акции (в мейннете — эмитент). С девятого цикла аудита, по решению основателя того же дня:
- Кипер симулирует каждую доставку на Ethereum перед отправкой: контракт токена акции, который её получает, и контракт аирдропа, который её зачисляет, в том виде, в каком их застанет доставка. Он запрашивает потребность самой тяжёлой акции, умноженную на 1,25, за вычетом газа, который адаптер LayerZero этой акции уже принудительно задаёт для первого шага
- Когда симуляцию нельзя выполнить, он берёт то, что использовали на Ethereum последние доставки рынка, а затем значения удалённого хаба по умолчанию
- Удалённый хаб его ограничивает. Владелец StockFun задаёт значение по умолчанию, нижний предел и потолок для каждого из двух шагов (см. Канал Robinhood); всё, что запрашивает кипер, удерживается между ними. Запрос, который потолок урезает ниже потребности доставки, — это «ожидает администратора»: отправка всё равно уходит, а застрявшая доставка выполняется повторно (следующий раздел)
- Для холдеров ничего не меняется. Кипер решает только, сколько газа дать доставке, но никогда — сколько отправляется, куда или кому
- Выбирается заново, только когда нужно. Пара, выбранная по симуляции каждой акции, сохраняется на окно, пока акции, состояние цикла окна и то, может ли доставка прийти после конца следующего окна, остаются прежними; пару, которую симуляция дать не смогла, выбирают заново на каждом проходе. С десятого цикла аудита отправка, которая несёт только пыль, которую мост не может перенести, ничего не читает об окружении доставки, а пара, просимулированная, когда цикл окна уже открыт, — а это состояние никогда не возвращается назад, — используется снова без повторного чтения этого состояния
Доставка, застрявшая на Ethereum
Доставка, которой не хватило газа, ничего не теряет: она остаётся на эндпоинте LayerZero на Ethereum — её первый шаг проверен и не выполнен или её последний шаг сохранён и не выполнен, — пока кто-нибудь не выполнит её повторно с большим газом. С девятого цикла аудита кипер делает это сам:
- В каждом цикле он читает шаг каждой доставки на эндпоинте. Как только очередь
исполнителя прошла (его сбой, которому верят, только если он исходит от собственного
исполнителя LayerZero, или десять минут), он повторно выполняет застрявший шаг со своего
ключа в Ethereum, с симулированной потребностью, умноженной на 1,25, и никогда не больше
KEEPER_AIRDROP_REEXECUTION_MAX_GAS, по умолчанию 4 000 000 - О шаге, который он не может отправить (его симуляция не проходит, его потребность выше ограничения, на его ключе не хватает ETH), подаётся одно оповещение с командой, чтобы выполнить его вручную, и он пробуется снова в каждом цикле
- Шаг, который снова не проходит в сети и по-прежнему остаётся застрявшим, — это второй
сбой: о нём подаётся оповещение с командой, и кипер больше никогда его не отправляет. Он
решает так только по чтению шага, которое дало ответ, и только когда блок непрошедшего
запуска ушёл на несколько блоков вглубь (
KEEPER_LOG_LAG_BLOCKS), поэтому ни нода, отстающая на блок, ни реорганизация этого блока не могут заставить его сдаться - Эти запуски оплачивает ключ кипера в Ethereum: при Glamsterdam последнему шагу, который открывает цикл, нужно около миллиона газа
Кипер выбирает, когда, и газ доставки в пределах, заданных владельцем, и ничего больше. Отправка, поступившая после следующего времени закрытия, измеряется по окну следующего дня.
Шагом управляют семь настроек: KEEPER_AIRDROP (по умолчанию включена),
KEEPER_AIRDROP_AFTER_SESSION (по умолчанию включена; выключена в тестнете, который
игнорирует сессию), KEEPER_AIRDROP_FEE_MARGIN_BPS (1 000),
KEEPER_AIRDROP_TRANSIT_MINUTES (60), с седьмого цикла аудита
KEEPER_AIRDROP_MIN_VALUE_BPS (10 000), а с девятого —
KEEPER_AIRDROP_REEXECUTION_MAX_GAS (4 000 000) и KEEPER_AIRDROP_LZ_EXECUTOR (пусто:
исполнитель LayerZero в сети кипера, единственный вызывающий, оповещениям о сбое от
которого кипер верит).
Перезапуски: файл состояния
С 2026-10-05 кипер записывает то, что он отслеживает от одного цикла к другому, в файл
состояния (KEEPER_STATE_DIR): доставки аирдропа и переводы моста в пути, транзакции,
ожидающие квитанции, свои эпизоды оповещений и серии сбоев; с шестого цикла аудита также
тикеты канонического канала, за которыми он следит, место, где остановился поиск каждого
перевода, и время, когда USDC каждого волта уходил в последний раз; с седьмого —
оповещения, которые его вебхук ещё не принял. Файл, записанный более старым кипером,
загружается как есть. Он записывает файл после каждого цикла и каждой отправки и читает
его один раз при запуске. Перезапуск ничего из этого не забывает: о доставке, последний
шаг которой не проходит после перезапуска, всё равно подаётся оповещение, транзакция,
отправленная до него, никогда не отправляется повторно, а об эпизоде, о котором уже было
оповещение, не оповещают дважды. Файл, записанный для другой фабрики, откладывается в
сторону, и кипер начинает с пустого состояния, с предупреждением. С восьмого цикла аудита
файл, записанный для другой сети, сначала остаётся как есть — его не читают и не
перезаписывают, — пока кипер не прочитает, какую сеть обслуживает его RPC: если тот
обслуживает сконфигурированную сеть, файл принадлежал другой сети и тогда откладывается в
сторону; если нет, ошибка — в сконфигурированном идентификаторе, кипер отказывается
запускаться, а когда идентификатор исправлен, кипер забирает свой файл обратно, включая
депозиты, за которыми следил. До того опечатка в идентификаторе стоила файла ещё до того,
как кипер отказывался запускаться. С того же цикла сохранённые оповещения хранятся по
одному на каждое отдельное оповещение; файл, записанный более старым кипером, загружается
как есть. С девятого цикла аудита файл также хранит пакет каждой доставки аирдропа и её
запуски кипером, газ, который использовали последние доставки рынка, и последний повтор
каждого канонического тикета кипером; файл, записанный более старым кипером, по-прежнему
загружается как есть.
С 2026-10-06:
- Каждая отправка попадает на диск до ожидания. Каждая транзакция уходит с nonce, который блокчейн выдаёт адресу кипера непосредственно перед ней, и записывается в файл состояния вместе с хешем и nonce, как только возвращается хеш, до того, как начинается ожидание квитанции: кипер, остановленный во время ожидания, при перезапуске находит её по хешу, а не отправляет повторно
- Потерянная транзакция отпускается. Транзакция, которую ни одна нода больше не знает
спустя четырёхкратное значение
KEEPER_RECEIPT_ALERT_MSпосле отправки (по умолчанию два часа), отбрасывается с одним оповещением, чтобы больше не задерживать отправки своего рода; её nonce остаётся свободным, и его занимает следующая транзакция кипера. Оповещение о транзакции, которая ещё не смайнена, говорит, как её заменить - Запись целиком. Диск, слишком заполненный, чтобы принять файл, — это ошибка, о которой подаётся оповещение, и последний исправный файл остаётся; до того его могла заменить усечённая копия
- Один кипер на папку. Кипер берёт блокировку в своей папке состояния (
keeper.lock): второй кипер на той же папке отказывается запускаться и называет первого. Блокировка, кипер которой больше не работает, перехватывается; блокировка, оставленная кипером на другом хосте, — нет, и оператор удаляет её, когда тот кипер больше не работает. Пробный прогон блокировку не берёт
Размер каждого шага
С 2026-10-01 волт конвертирует сумму, которую указывает кипер, а не весь свой баланс. Кипер определяет её так:
| Шаг | Сумма |
|---|---|
| ETH → USDC | Весь баланс, уменьшаемый вдвое, пока котировка не укладывается в границу волта, но не ниже порога конвертации; с 2026-10-06 остаток ждёт следующего окна |
| Акция в пулах Uniswap на Ethereum | Резерв акции, уменьшаемый вдвое не более четырёх раз; звено, которое всё равно не укладывается, ждёт следующего цикла, а его резерв остаётся нетронутым |
| Акция на опциональном канале Ondo | Резерв акции, ограниченный номинальным лимитом сессии. С 2026-10-06 звено, которое Ondo оценивает ниже границы волта, никогда не отправляется: бесплатная котировка читается до любой аттестации, и как только аттестация возвращается ниже границы, для этой акции до следующего окна больше ни одна не запрашивается |
| Акция на Robinhood Chain | Резерв акции, ограниченный по отдельности фиксированным потолком и, когда пул этой акции можно измерить, долей глубины этого пула |
То, что не потрачено, ждёт в волте следующего цикла (для ETH — следующего окна); деньги акции остаются зарезервированными для этой акции.
Начиная с пайплайна безопасности от 2026-10-01, каждая покупка удерживает n−1 единиц на последней акции корзины, где n — число акций, на любом канале, который покупает акции. Волты отдают остаток от округления весов последней акции, поэтому несколько единиц денег, поступивших между чтением, которое делает кипер, и его транзакцией, могут уменьшить только этот резерв, не более чем на n−2 единиц; при расходе прочитанной суммы весь вызов не прошёл бы, и вызвать это мог бы любой пылевым переводом. Удержанное остаётся зарезервированным для следующего вызова. Всё остальное, что вызывает волты, должно соблюдать тот же запас. Исправление в контрактах, которое меняет задокументированное правило аллокации, ожидает решения владельца. С 2026-10-06 волт, который держит не больше этих нескольких единиц USDC (не более четырёх, поскольку корзина содержит не более пяти акций), ради них больше не посещается: они ждут следующего USDC, который принесёт его ETH.
Размер сжигания $STOCKFUN определяется так же: часть, влияние которой на цену превышает
потолок кипера, уменьшается вдвое, но не ниже порога кипера, а остальное сжигается в
следующих циклах. Начиная с пайплайна безопасности от 2026-10-01, часть, которую пул
$STOCKFUN не может исполнить целиком, тоже уменьшается вдвое; раньше сжигание в этом
цикле не проходило. При любом другом сбое повторной попытки нет.
Чего он не может
Выбрать актив вне корзины. Перенести зарезервированные деньги одной акции на другую акцию. Ослабить границу оракула. Отправить актив куда-либо, кроме того места, куда его отправляет контракт. Вывести что-либо. Выбрать, кто получит аирдроп или сколько в нём будет, или назначить долю себе.
Доступ к запуску закреплён за ним, чтобы не допустить сэндвич-атак, а не потому, что киперу доверяют: каждый его вызов исполняется в пределах границ волтов.
Цикл
календарь NYSE} C -->|нет| W[Ждать] C -->|да| P{Контракт на паузе?} P -->|да| W P -->|нет| A{Первая проверка волта
в этом окне: ≥ 0.1 ETH?} A -->|нет, ETH ждёт
следующего окна| N[Следующий рынок] A -->|да| E[ETH → USDC, граница 50 bps] E --> B[USDC → USDG → мост] B --> R{Дошло до удалённой сети?} R -->|нет| M[Мониторинг, повтор тикета] R -->|да| K[Покупка акций, граница 200 bps] K --> AD[Отправка в контракт аирдропа] AD --> N
Час, порог и границы на диаграмме — это настройки по умолчанию. Кипер делает проход каждые пять минут; ETH волта проверяется раз за окно, на первом проходе кипера после закрытия, а USDC, который волт уже держит, идёт дальше один раз после каждой конвертации и не чаще раза за окно в остальных случаях. Отправка в контракт аирдропа выполняется раз за окно, по умолчанию после сессии; долги, LP-комиссии, доставки и, с шестого цикла аудита, тикеты канонического канала обрабатываются в каждом цикле, независимо от сессии.
Его конфигурация
Всё задаётся через переменные окружения: RPC для обеих сетей, приватный ключ кипера, адреса
контрактов, интервал и предохранители — пробный прогон, однократный запуск, требование
открытого рынка. С 2026-10-05 также: настройки шага аирдропа, см. выше; сбор LP-комиссий,
KEEPER_COLLECT_FEES (по умолчанию включён), KEEPER_COLLECT_FEES_HOURS (24) и
KEEPER_COLLECT_FEES_MIN_WEI (0); оповещение после повторных пропусков,
KEEPER_BRIDGE_ALERT_AFTER_SKIPS (3); и папка файла состояния, KEEPER_STATE_DIR. С
2026-10-06 — KEEPER_CONVERT_ONCE_PER_WINDOW (по умолчанию включена): ETH волта
конвертируется раз за окно, а с шестого цикла аудита его USDC уходит один раз после каждой
конвертации и не чаще раза за окно в остальных случаях; локальный или тестнетовый запуск
её отключает и конвертирует, покупает и отправляет через мост на каждом проходе. С шестого
цикла аудита также KEEPER_TICKET_ALERT_HOURS (6): на каноническом канале — число часов,
после которых подаётся оповещение о тикете, об исполнении которого не известно.
KEEPER_STOCK_ROUTER, роутер, который фабрика указывает для новых волтов, лишь
определяет, сессия какого рынка допускает цикл; каждый волт котируется на собственном
роутере.
С седьмого цикла аудита идентификаторы сетей, KEEPER_CHAIN_ID и
KEEPER_REMOTE_CHAIN_ID, сверяются с RPC перед первым циклом: 1 с 4663 в мейннете,
11155111 с 46630 в тестнете. С восьмого цикла аудита оба должны быть заданы:
KEEPER_CHAIN_ID всегда, KEEPER_REMOTE_CHAIN_ID при мосте, и без них кипер отказывается
запускаться (до того, если их не задать, KEEPER_CHAIN_ID считался равным 11155111, а
KEEPER_REMOTE_CHAIN_ID — 4663). С десятого цикла аудита KEEPER_LOG_MAX_SELECTORS (по
умолчанию 1 000, не меньше 5) ограничивает число адресов и значений событий, которые
указывает один запрос логов, так, как их считают ноды Robinhood Chain; за публичным
эндпоинтом Sepolia, который отклоняет десять адресов и больше, её задают равной 10. Две
настройки задают, на сколько блоков ниже последнего останавливаются его поиски по логам:
KEEPER_LOG_LAG_BLOCKS (2, на Ethereum) и
KEEPER_REMOTE_LOG_LAG_BLOCKS (12, на Robinhood Chain); с восьмого цикла аудита первая
также задаёт, насколько глубоко должен уйти блок батча моста, прежде чем кипер начнёт за
ним следить, а вторая — на сколько блоков ниже последнего читается тикет. Защитам оракула
настройка не нужна: кипер читает их на оракуле каждого зеркального волта. Целочисленная
настройка, оставленная пустой, принимает своё значение по умолчанию, а значение вне
допустимого диапазона останавливает кипера при запуске.
Ключ кипера — горячий ключ, пополняемый только для оплаты газа и отличный от ключа деплоера.
Его ограничения
- На канале USDG доставка, которую денежный токен отклонил до запуска кипера или пока он был остановлен, видна только как предупреждение при sweep; на каноническом канале такая доля обнаруживается в собственном состоянии удалённого хаба
- До девятого цикла аудита кипер не выполнял сам последний шаг непрошедшей доставки
(
lzCompose); с тех пор он повторно выполняет застрявший шаг, в пределах ограничения, а второй сбой оставляется человеку, с командой - Мониторинг аварийных событий не сохраняет свою позицию при перезапуске: события, сгенерированные, пока кипер был остановлен, не сканируются
- Кипер, остановленный в те несколько миллисекунд, что проходят между отправкой и её записью в файл состояния, может при перезапуске отправить эту транзакцию ещё раз; собственные проверки контрактов приводят к тому, что большинство таких повторов не проходит, ценой их газа
- Поиск прибытия перевода никогда не читает блок дважды: реорганизация, которая переносит прибытие в уже прочитанный блок, оставляет этот перевод оповещению о транзите. С седьмого цикла аудита каждый поиск по логам останавливается на несколько блоков ниже последнего, что задерживает зачисление и его оповещение о транзите на эти несколько блоков
- С восьмого цикла аудита за батчем моста следят, когда его блок ушёл на несколько блоков вглубь, на цикл позже, чем раньше, и следующий батч его ждёт; реорганизация глубже этого не покрывается, как и для поисков по логам. Тикет, исполненный в последние несколько блоков, там всё ещё читается как живой, пока последний блок не уйдёт на столько же блоков дальше его исполнения: в следующем цикле в загруженной сети, через несколько циклов в тихой. С девятого цикла аудита тикет, который удалила собственная повторная отправка кипера, тем временем не отправляется повторно ещё раз и не вызывает оповещения, в том числе после перезапуска; тикет, повторно отправленный кем-то другим в этих блоках, пробуется снова, не проходит симуляцию и всё ещё может вызвать оповещение как живой по истечении часов оповещения
- Минута, в течение которой кипер читает на блоке своей последней транзакции, не покрывает ноду, отстающую от остальных больше чем на минуту
- На локальном канале открытие окна учитывается один раз, без того, что повторяет вторая транзакция (около 5 % стоимости): отправка может уйти, стоя до этой доли меньше, чем она обходится
- С десятого цикла аудита акция, переданная волту после закрытия сессии помимо любой покупки, уходит с отправкой следующей сессии, в более позднее окно: кипер снова читает волт, которому нечего отправлять, только на следующей сессии. Покупка, квитанцию которой кипер прочитал на последнем проходе сессии, также может быть прочитана пустой на первом проходе после закрытия сессии с ноды, отстающей от неё, для чего нужен интервал между проходами короче отставания этой ноды (около пяти секунд в Sepolia), а никогда не пять минут по умолчанию
Префлайт
Перед любым реальным запуском команда префлайта, не отправив ни одной транзакции, проверяет, что сети отвечают, что по адресам есть код, что пиры LayerZero у USDG OFT совпадают, что USDG не поставлен на паузу своим эмитентом, Paxos, и что веса корзин такие, какими должны быть.
С восьмого цикла аудита она также проверяет защиты оракула. Её манифест должен указывать,
как задана проверка секвенсора, в любом из двух вариантов: без фида аптайма и без
льготного периода (проверка выключена, как сегодня) или фид с льготным периодом. Она
читает этот фид, когда тот есть, и паузу оракула токена каждой акции: токен в
корпоративном действии — это предупреждение, которое не проваливает проверку; токен без
этого сигнала её проваливает. После деплоя она проверяет, что задеплоенный оракул
соответствует манифесту, что состояние его секвенсора пропускает цены и что проверка паузы
включена для каждой акции. Команда кипера inspect выводит те же защиты: проверку
секвенсора и её состояние, а также проверку паузы каждой акции и то, ставит ли её токен
паузу сейчас.
С девятого цикла аудита она также работает на деплое тестнетового запуска LayerZero (Sepolia и тестнет Robinhood Chain), по двум файлам этого запуска, с RPC, названными для этой пары, поэтому RPC мейннета никогда не спрашивают о тестнете; любая другая пара сетей, включая смешанную, отклоняется. В тестнете она не читает роутер Uniswap v3, которого в этой сети нет, и сверяет фид ETH/USD с интервалом актуальности (heartbeat), заданным оракулу этого запуска. На обеих парах она проверяет, что удалённый роутер указывает тот роутер v3, который указан в манифесте. Запуск на тестнетовом деплое: 68 проверок, все проходят.
Без RPC она отказывается запускаться, вместо того чтобы сообщить о ложном успехе.