Аварийный режим

С 2026-08-29 владелец StockFun может переместить активы казны или любого контракта, который держит средства протокола, на любой адрес. С 2026-10-05 перемещение происходит немедленно: без предварительного объявления, без задержки, и никакая настройка не может её добавить. Это самое значимое по последствиям изменение в проекте, и оно изменило то, что продукту позволено говорить.

Что он позволяет

emergencyTransfer(asset, amount, to), вызываемый на контракте, который держит актив, сразу перемещает указанную сумму любого актива — ETH, USDC, USDG, токенизированных акций, токенов рынков — на адрес to. Эта функция есть у пяти контрактов: TreasuryVault, BridgeHub и, с 2026-10-04, контракт аирдропа, AirdropDistributor, на Ethereum; RemoteHub и зеркальные волты на Robinhood Chain. Вызвать её может только их администратор аварийного режима: владелец фабрики на Ethereum и, на Robinhood Chain, тот же адрес, каким его передал последний батч моста. Владелец может применить её к любому из этих контрактов в любой момент.

Каждое перемещение получает порядковый номер и генерирует публичное событие, EmergencyExecuted, с активом, суммой и получателем. Заранее его ничто не объявляет.

До 2026-10-05 владелец планировал перемещение: публичное событие объявляло его, владелец мог отменить его в течение 48 часов, а после этого исполнить его мог кто угодно. Этого графика больше нет, и задержка не является параметром: аварийный режим срабатывает, как только владелец его применяет, и никогда не ждёт. Вместе с графиком ушло исключение, предусмотренное для средств аирдропа, застрявших в зеркальном волте, которое так и не было реализовано в коде: немедленное перемещение их уже покрывает.

Вместе с этим: пауза конвертаций и отправок через мост — немедленная, без перемещения средств — и замена адаптера моста для будущих отправок, changeAdapter, тоже немедленная с 2026-10-05. На контракте аирдропа пауза останавливает отправки, открытие циклов (openCycle) и размещение отложенных акций (assignUnassigned); она ничего не перемещает и никогда не останавливает клейм.

С 2026-10-01 удалённый хаб на паузе всё равно применяет смены кипера и администратора аварийного режима, которые несёт каждый батч, поэтому смена владельца всегда доходит до Robinhood Chain; деньги, которые он тем временем получает, ждут там, записанные по каждому рынку, до sweep после снятия с паузы.

Аудит безопасности от 2026-09-29 обнаружил, что замена адаптера не может сработать от начала до конца: удалённый хаб принимает батчи только от исходного адаптера, поэтому каждый батч, отправленный после замены, ждал бы на удалённом хабе, пока его не вернёт аварийный режим (M-2). 2026-10-05 владелец решил оставить всё как есть: адаптер меняют апгрейдом на месте, по тому же адресу, а чтобы принять новый адрес, удалённый хаб сначала пришлось бы апгрейдить.

На волте, начиная с пайплайна безопасности от 2026-10-01, аварийное перемещение, которое опускает деньги ниже резервов акций, обнуляет их все: то, что осталось, и каждое последующее поступление заново распределяются по весам корзины. Аварийное перемещение, которое забирает только незарезервированные деньги или другой актив, их сохраняет. Деньги, выведенные и затем отправленные обратно в волт, распределяются как любое поступление, а не возвращаются той акции, для которой были зарезервированы.

Второй раунд аудита, проведённый 2026-10-01, обнаружил, что на удалённом хабе аварийное перемещение, которое забирает деньги, уже записанные для рынка, оставляет запись на месте, и она подлежит выплате из денег других рынков (R2H-1). С 2026-10-05 за перемещением следует урегулирование расчётов, описанное ниже, и этот случай покрыт его инструментами вместе с процедурой: на каноническом канале владелец StockFun ставит удалённый хаб на паузу перед перемещением. С четвёртого цикла аудита того дня перемещение можно также отнести на один рынок, что урегулирует его расчёты в том же вызове.

После перемещения: урегулирование расчётов

С 2026-10-05, после циклов аудита того дня. Само перемещение не изменилось: оно немедленное и без условий. Но два контракта держат активы, которые обеспечивают то, что причитается нескольким рынкам: контракт аирдропа и удалённый хаб. После перемещения из любого из них расчёты урегулируются, и ничего никогда не выплачивается за счёт другого рынка. Либо активы возвращаются, либо владелец StockFun списывает потерю с рынка, который её понёс.

Оба контракта учитывают то, что обеспечивает их расчёты, и никогда — свой баланс. Баланс включает и токены, которые уже пришли, но ещё не зачислены: доставку акций, последний шаг которой на Ethereum ещё не выполнен, или, на канале USDG, деньги батча, последний шаг которого на Robinhood Chain ещё не выполнен. Эти токены пока ничего не обеспечивают и могут принадлежать другому рынку. До второго цикла аудита от 2026-10-05 контракты читали баланс, поэтому такие токены могли снова открыть клеймы опустошённого цикла и выплатить их акциями другого рынка.

  • Аварийное перемещение забирает сначала из того, что обеспечивает расчёты. Токены невозможно отличить друг от друга, а если считать зачисленные токены ушедшими первыми, ни один рынок никогда не платит за другой. То, что оно забирает сверх этого, пришло из ещё не зачисленных токенов: контракт записывает эту сумму (taken, cashTaken), и следующие доставки этого актива погашают её, прежде чем что-либо обеспечивать.
  • Активы возвращаются через restore. Вызвать её может кто угодно; она забирает токены у вызывающего. Простой перевод в контракт ничего не обеспечивает. С третьего цикла аудита от 2026-10-05 restore сначала возвращает то, что перемещение забрало сверх обеспечения расчётов (taken, cashTaken), и обеспечивает расчёты остатком: токены доставки, которая ещё ждёт, никогда не идут на выплату опустошённому рынку.
  • Случайно попавшие токены. Токены, которые попали в контракт аирдропа или в удалённый хаб на канале USDG по ошибке и так и не были зачислены, тоже вычитаются из того, что обеспечивает расчёты, если их забирает аварийное перемещение. Аварийное перемещение забирает их только для того, чтобы вернуть обратно; иначе их сумму придётся списать с цикла или рынка, на который она приходится, или устранить апгрейдом.

  • Контракт аирдропа ведёт по каждой акции учёт того, что он должен, и того, чем это обеспечено, и выплачивает акцию, только пока обеспечение покрывает то, что он должен. После перемещения, которое забрало часть акции, клеймы этой акции на каждом рынке, который её держит, ждут, пока акция не вернётся через restore или пока владелец не спишет потерю с цикла, который её понёс (writeDownCycle), или с отложенных акций рынка (writeDownUnassigned). Пока никто не заклеймил эту акцию из цикла, владелец может списать любую её часть, и каждый холдер цикла теряет одну и ту же долю; как только часть холдеров получила выплату, владелец может списать только весь остаток, который теряют холдеры, ещё не получившие выплату, или вернуть акцию. Всё, что поступает в этот цикл потом, делится пропорционально между всеми его холдерами, как если бы списанной суммы там никогда не было. Ни один другой цикл за потерю не платит. С четвёртого цикла аудита клейм переносит только ожидающую акцию (ClaimDeferred) и выплачивает остальные в том же вызове; до этого он не проходил целиком. У контракта аирдропа нет перемещения, относимого на один цикл: его лимит размера не оставил для этого места. Процедура, которая никогда не допускает недостачи в его расчётах, сначала списывает цикл или отложенные акции, а затем перемещает акцию; перемещение, сделанное первым, сохраняет общее ожидание, описанное выше.

  • Удалённый хаб на канале USDG считает деньги, которые обеспечивают то, что он должен, и отказывается выплачивать (sweep), пока их не хватает. После перемещения, которое забрало больше этого, следующие батчи записываются, а не доставляются, пока не восполнят недостачу. Владелец возвращает деньги (restore) или списывает сумму, которая потеряна или которую перемещение вручную доставило в зеркальный волт рынка (writeOffPending). С четвёртого цикла аудита владелец может также отнести перемещение на один рынок: emergencyTransferFromPending(marketId, amount, to) перемещает то, что хаб должен этому рынку, и списывает это в том же вызове, так что расчёты никогда не уходят в недостачу и ни один другой рынок не ждёт.
  • Удалённый хаб на каноническом канале должен по записям своей очереди и такого счёта не ведёт: защита — это процедура. Владелец ставит хаб на паузу перед перемещением, перемещает деньги, удаляет запись, которой они принадлежали (writeOffRecord), затем снимает хаб с паузы, чтобы очередь никогда не выплатила эту запись второй раз деньгами другого рынка. С четвёртого цикла аудита для записи это делает один вызов: emergencyTransferRecord(index, to) перемещает всю сумму записи и удаляет её. С пятого этот вызов предназначен для записи, депозит которой уже пришёл: деньги очереди общие, поэтому он отклоняет сумму, превышающую деньги, не удерживаемые для отклонённых долей (RecordNotCovered); запись, депозит которой потерян, удаляется через writeOffRecord. Доля, которую не принял зеркальный волт и которая хранится отдельно для своего рынка (undeliverable), перемещается и списывается через emergencyTransferFromPending.

Каждое списание генерирует публичное событие (WrittenDown, PendingWrittenOff), как и каждый возврат (Restored) и каждый перенесённый клейм (ClaimDeferred).

Спасение: что модуль держит по ошибке

С 2026-10-05, согласно правилу основателя, по которому у всего, что может держать средства, есть рычаг (см. Архитектура), у контрактов без аварийного режима есть собственная функция спасения — для того, что было отправлено им по ошибке или осталось после неудачной операции. Вызывает её только владелец StockFun: владелец фабрики на Ethereum, администратор удалённого хаба на Robinhood Chain — тот же адрес, который апгрейдит модули. Каждое спасение генерирует публичное событие.

  • У модулей, которые ничего ни для кого не хранят между транзакциями, — фабрики, Lens, оракулов, регистратора владения, роутеров акций, официального своп-роутера и адаптеров моста — есть rescue(asset, amount, to), для ETH (нулевой адрес) или любого токена
  • Хук спасает только случайные поступления: не больше ETH, отправленного ему кем-либо, кроме PoolManager (strayEth), и любой токен, поскольку токенов он никогда не держит. Балансы создателей, команды и байбэка и долги перед волтами никогда не уходят этим путём. ETH, зачисленный принудительно, без вызова, не учитывается и ждёт апгрейда
  • Лок ликвидности, который нельзя апгрейдить, спасает любой токен и ETH сверх долей, которые он удерживает для волтов и создателей (totalOwed), — эти доли никогда не уходят этим путём; с пятого цикла аудита также claims v4, зачисленные ему в PoolManager (rescueClaims), и NFT, отправленный ему простым переводом (rescueNft). Общего вызова у него нет: через PoolManager можно было бы добраться до вывода ликвидности режима завершения без его 30 дней. Его позиции остаются доступными только через режим завершения
  • BuybackBurner спасает токен в любой момент, а свой ETH — только когда никакое сжигание уже не могло бы его потратить: до того, как назначен рынок протокола, или после того, как режим завершения вывел ликвидность пула $STOCKFUN. Пока этот пул заблокирован, его ETH уходит только через сжигания
  • Токены, которые нельзя апгрейдить, спасают то, что было отправлено на их собственный адрес: собственные токены — с сообщением регистратору владения, как при любом переводе, — любой другой токен, принудительно зачисленный ETH и, с пятого цикла аудита, claims v4 и NFT (rescue, rescueClaims, rescueNft). Баланс ни одного холдера этим путём сдвинуть нельзя
  • Контракты имплементации за прокси никогда не используются напрямую. На имплементациях фабрики и удалённого хаба, которые хранят своего администратора в хранилище прокси, рычагом для того, что отправлено на их собственный адрес, служит адрес, который их задеплоил; каждая другая имплементация читает своего администратора через свой источник прав — владельца протокола

Волты, два хаба и контракт аирдропа вместо этого сохраняют аварийный режим, поскольку ведут собственные расчёты. Два деплоера на Ethereum и деплоер зеркальных волтов не хранят состояния, не принимают ETH и не имеют владельца: рычаг им не нужен.

Чего он не позволяет

Аварийный режим не затрагивает реестр фидов. Он перемещает активы, но не меняет границы исполнения, которые являются отдельными настройками владельца. Он не может заставить волт купить что угодно по любой цене; он может сразу забрать то, что находится в волте.

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

Однако это не единственный путь владельца к волту. С 2026-10-02 владелец StockFun может также апгрейдить волт или оракул, который тот читает, с немедленным вступлением в силу. У заблокированной ликвидности есть собственный выход — режим завершения, объявляемый за 30 дней: единственная фиксированная задержка протокола. См. Модель доверия.

Зачем он существует

Ревью кросс-чейн сценариев отказа задало простой вопрос: что происходит, когда маршрут не срабатывает, сообщение моста теряется или в контракте есть баг?

Без пути восстановления ответ такой: «средства потеряны, навсегда». Протокол выбрал контролируемый путь восстановления, который держит владелец и который фиксируется ончейн, а не элегантность системы, которая ничего не может починить. С 2026-10-05 этот путь срабатывает, как только им воспользуются.

С 2026-10-01 это также путь, которым деньги рынка, чью корзину отклонила Robinhood Chain, покидают зеркальный волт этого рынка, который остаётся неинициализированным: см. Канал Robinhood.

Во что он обходится

Владелец может переместить активы казны на любой адрес, немедленно и без предварительного объявления. Это записано в модели доверия проекта.

Прежде всего, утверждённая формулировка заменяет все прежние обещания.

Активы находятся в волте рынка и подчиняются его правилам. Команда StockFun может переместить активы казны в экстренной ситуации, после публичной 48-часовой задержки.

С 2026-10-05 задержки из этой фразы больше не существует: перемещение происходит немедленно. Фразу предстоит пересмотреть, а её новую формулировку — утвердить. С 2026-10-02 она также не охватывает апгрейды волтов, которые вступают в силу сразу.

Продукт больше не может говорить non-custodial, trustless, неизменяемая казна или никто не может тронуть казну. Эти формулировки запрещены правилами бренда; автоматический аудит текстов их не проверяет — это задача ревью.

Аварийный режим никогда не описывается как защита или гарантия от потерь. Это полномочие команды, действующее немедленно. Не более того.

Что видит пользователь

До 2026-10-05 баннер должен был объявлять запланированное восстановление на каждой затронутой поверхности, с датой исполнения. Теперь перемещение происходит немедленно, поэтому объявлять заранее нечего: оно становится видно постфактум, в событии EmergencyExecuted контракта, из которого ушли средства.