Архитектура
Протокол работает в двух сетях. На Ethereum находятся лаунчпад, пулы и волты; на Robinhood Chain — токенизированные акции.
Контракты
Эта таблица описывает протокол в соответствии с принятыми решениями. Часть, относящаяся к
запуску, есть в коде с 2026-09-28, а распределение аирдропа — с 2026-10-04, без деплоя;
адаптеров LayerZero для акций в репозитории нет. Роутеры и адаптеры, кроме официального
своп-роутера, в таблицу не включены: UniswapV4StockRouter на Ethereum,
RobinhoodStockRouter на Robinhood Chain и UsdgOftAdapter. См. Статус.
С 2026-10-02 каждый контракт из таблицы можно апгрейдить, кроме токенов, лока ликвидности и
деплоеров: см. ниже. Проценты на диаграмме — это настройки налога по умолчанию.
| Контракт | Роль | Количество |
|---|---|---|
StockFunFactory |
Создаёт рынки, взимает комиссию за создание, ведёт реестр и хранит настройки владельца для новых рынков и для волтов; источник прав на апгрейд для каждого модуля в Ethereum | Один |
MarketDeployer · VaultDeployer |
Обходят лимит EIP-170, неся в себе код создания токена и код создания прокси волта; не хранят состояния, заменяются через фабрику, а не апгрейдятся | По одному |
StockFunToken |
Простой ERC-20 без апгрейда, без минта и без сжигания; без собственной логики, кроме одного сеттера, setRecorder, и функций спасения, для владельца протокола; сообщает о каждом изменении баланса регистратору владения |
Один на рынок |
HoldingRecorder |
Записывает для каждого токена владение каждого адреса во времени, а также саплай вне PoolManager Uniswap; перевод не проходит, если не проходит его запись, — единственное намеренное исключение из правила изоляции ниже |
Один |
TreasuryVault |
Держит ETH, USDC, затем акции; конвертация, каждое звено покупки отдельно, передача своих акций в аирдроп на локальном канале (sendToAirdrop), каждой акции отдельно, и немедленное аварийное восстановление |
Один прокси на рынок |
LiquidityLock |
Создаёт пул и вносит обе позиции, которые остаются заблокированными, и хранит LP-комиссию и шаг тиков каждого пула; удерживает отклонённую долю сбора комиссий для её получателя; без апгрейда; единственный выход — режим завершения, через 30 дней после его объявления | Один |
StockFunHook |
Взимает комиссию с каждого свопа, применяет антиснайп, хранит настройки налога и вайтлист каждого пула, отклоняет ликвидность от кого-либо, кроме лока, держит балансы создателей, команды и байбэка до клейма, а также то, что причитается волту, не принявшему свою долю | Один |
StockFunSwapRouter |
Официальный роутер для сделок, единственный, через который адрес из вайтлиста освобождается от антиснайпа | Один, владелец может его заменить |
StockFunLens |
Только чтение; агрегирует состояние рынка для dapp, читая каждый волт отдельно; с 2026-10-06 его страница также содержит канал волта, его состояние паузы и USDC, которые он отправил через мост, состояние блокировки пула, регистратор владения токена и то, что хук и лок должны волту | Один |
TreasuryOracle |
Фиды Chainlink, записываемые один раз; настройками являются их интервалы актуальности (heartbeat), а с 2026-10-06 — две его защиты на Robinhood Chain: проверка секвенсора, выключенная, пока Chainlink не опубликует фид аптайма для этой сети, и пауза оракула каждой акции, которая придерживает цену этой акции во время корпоративного действия | Один на сеть |
BuybackBurner |
Покупает $STOCKFUN и отправляет его на сжигание. Пока пул $STOCKFUN заблокирован, другого пути нет |
Один |
BridgeHub · RemoteHub · RemoteTreasuryVault |
Кросс-чейн канал; удалённый хаб — источник прав на апгрейд на Robinhood Chain и назначает маршрут аирдропа и адаптер каждой акции; зеркальный волт отправляет свои акции в аирдроп (sendToAirdrop) |
Один, один, один прокси на рынок |
StockFunProtocolToken |
$STOCKFUN, который, как и токен рынка, нельзя апгрейдить; весь его саплай идёт в его заблокированную позицию |
Один |
AirdropDistributor |
Распределяет на Ethereum акции, купленные каждой казной, между холдерами её токена, ежедневными циклами; зачисляет только поступления от собственных волтов рынка; каждый холдер клеймит сам, и клейм выплачивает каждую акцию, какую может; привязан к фабрике, которая его назначает | Один |
Прокси и апгрейды
С 2026-10-02 каждый модуль — это прокси ERC-1967, стоящий перед имплементацией, с апгрейдом через UUPS. Прокси хранит адрес и состояние; имплементация хранит код, и апгрейд её заменяет. Имплементацию нельзя инициализировать никогда: каждый прокси инициализируется внутри собственного конструктора.
Каждый модуль спрашивает у одного контракта, кто может его апгрейдить, — у своего источника
прав на апгрейд, зафиксированного в его имплементации: у фабрики на Ethereum, которая
отвечает адресом своего владельца, и у удалённого хаба на Robinhood Chain, который отвечает
адресом своего администратора аварийного режима, то есть владельца в Ethereum, каким его
передал последний батч моста. Поэтому передача владения фабрикой разом переносит право на
апгрейд каждого модуля в Ethereum, а право на апгрейд модулей Robinhood Chain — со
следующим батчем. Апгрейд отклоняется, если новая имплементация указывает другой источник
прав; имплементации хука и регистратора владения также должны сохранять тот же
PoolManager.
Нельзя апгрейдить: токены, лок ликвидности и, на Robinhood Chain, деплоер зеркальных
волтов, из адреса которого выводится адрес каждого зеркального волта. MarketDeployer и
VaultDeployer не хранят состояния: фабрика заменяет их (setDeployers), а не апгрейдит.
Хранилище только дополняется и никогда не переупорядочивается: раскладка каждого модуля
записана в contracts/storage-layouts/, а contracts/script/check-storage-layouts.sh
сверяет её перед каждым апгрейдом. С 2026-10-05 он сверяет каждую структуру на всех
уровнях вложенности, включая размеры, и отклоняет любое изменение структуры, которая
является элементом массива в хранилище: расти, в своём конце, может только структура,
являющаяся значением маппинга, или последняя переменная состояния.
Контракт имплементации никогда не используется напрямую, но и для того, что по ошибке отправлено на его собственный адрес, есть рычаг. Большинство имплементаций читают свой источник прав из immutable-переменной, поэтому такое выводит владелец протокола; имплементации фабрики и удалённого хаба хранят своего администратора в хранилище прокси, поэтому на их имплементациях рычагом служит адрес, который их задеплоил.
Кто может выполнять апгрейд и как быстро: см. Модель доверия.
Контракт аирдропа
С 2026-10-04 AirdropDistributor распределяет акции каждой казны на Ethereum. Это
апгрейдируемый модуль, как и остальные: прокси, источник прав на апгрейд которого —
фабрика. Фабрика назначает его (setAirdropDistributor), а волты читают его в реальном
времени; заменённый дистрибьютор сохраняет каждый свой цикл доступным для клейма там же. Он
читает владение из регистратора владения, а его правила описаны на странице
Аирдроп.
К нему ведут два пути, и больше ничто не зачисляет средства в цикл:
- Канал через мост.
sendToAirdropзеркального волта отправляет каждую указанную акцию через адаптер LayerZero этой акции, который назначает удалённый хаб (setStockAdapter), в дистрибьютор, который назначает удалённый хаб (setAirdrop), с идентификатором рынка в качестве полезной нагрузки. На Ethereum OFT акции минтит обёрнутую акцию в дистрибьютор, и эндпоинт LayerZero вызывает его. Он зачисляет доставку только от OFT акции, который зарегистрировал владелец, с Robinhood Chain, отправленную зеркальным волтом, который хаб моста выводит для этого рынка. - Локальный канал.
sendToAirdropволта на Ethereum одобряет точные суммы, дистрибьютор забирает их, только из собственного волта рынка, и зачисляет то, что получил; одобрения снова закрываются. Волт, подключённый к хабу моста, отклоняет этот вызов.
Кипер запускает оба пути и решает только, когда; на канале через мост, с 2026-10-06, — также газ, который каждая доставка получает на Ethereum и который удалённый хаб удерживает в пределах, заданных там владельцем StockFun.
Структурные свойства
Хук — синглтон. Один контракт обслуживает все пулы, что избавляет от майнинга адреса для каждого рынка — адрес хука v4 кодирует его разрешения в младших битах, а поиск такого адреса требует вычислительных ресурсов. С 2026-10-02 это прокси, адрес которого несёт все 14 разрешений v4: апгрейд сохраняет этот адрес, а будущая имплементация может использовать любой колбэк.
Ликвидность — не NFT. Она находится непосредственно в PoolManager, с привязкой к адресу
контракта блокировки. Нет позиции, которую можно передать, нет approve, который можно
отозвать, нет tokenId, который можно потерять. Единственные операции с ликвидностью,
которые может выполнить контракт, — modifyLiquidity с дельтой ровно ноль, чтобы собрать
комиссии, и, через режим завершения, изъятие всех позиций пула через 30 дней после
объявления о завершении.
Ликвидность добавляет только лок. С 2026-10-01 хук отклоняет любую другую позицию в пуле StockFun, поэтому каждая сделка — это своп против заблокированных позиций, и она платит налог.
У волтов нет вывода. Ни одна функция не позволяет кому-либо отправить активы волта на адрес по своему выбору. Два выхода — это аирдроп, единственный адресат которого — контракт аирдропа, назначенный протоколом, который выплачивает холдерам токена пропорционально, по правилу, которое никто не выбирает, и аварийный режим, с помощью которого владелец StockFun сразу перемещает актив на любой адрес. Таковы правила текущей имплементации: владелец StockFun может апгрейдить волт с немедленным вступлением в силу.
Границы — это настройки, фиды — нет. Ценовые границы волтов, по умолчанию 50 и 200 базисных пунктов, — настройки владельца StockFun, которые каждый волт читает в реальном времени; кипер может их только ужесточить. Реестр фидов записывает каждый фид один раз; настройками являются интервалы актуальности (heartbeat) фидов, а с 2026-10-06 — и две его защиты, которые могут только придержать цену, но никогда её не изменить: проверка секвенсора (выключенная на Robinhood Chain, пока Chainlink не опубликует для неё фид аптайма) и пауза оракула каждой акции (включённая для каждой акции Robinhood Chain; см. Канал Robinhood). Аварийный режим не затрагивает ни то, ни другое: он перемещает активы, но не меняет правила исполнения. Реестр, как и волты, владелец StockFun может апгрейдить.
Изоляция и рычаги
2026-10-05 основатель установил правило проектирования: когда что-то приводит к сбою одной функции, остальные функции не должны за это расплачиваться; всё должно продолжать работать, и у всего должен быть рычаг, чтобы вернуть потерянные средства и принять исправление. Четвёртый цикл аудита того дня внёс это правило в код, и с тех пор циклы аудита считают дефектом любое нарушение любой из двух его частей.
Изоляция. Сбой в одной функции, на одном рынке, в одной акции, цикле, записи или у одного получателя никогда не блокирует остальные: элемент пропускается, сохраняется как долг или переносится, с событием, а остальное продолжается.
- Батч моста исключает рынок, волт которого не может выдать свои деньги
(
MarketSkipped), а остальные пересекают мост. Доставка, которую денежный токен отклоняет для одного зеркального волта, остаётся на удалённом хабе, причитающейся этому рынку (DeliveryRefused), а остальные рынки получают выплату - Покупка исполняет звено каждой акции отдельно (
LegFailed), а отправка в аирдроп — каждую акцию отдельно (AirdropSendFailed) - Клейм выплачивает каждую акцию, какую может, и переносит остальные (
ClaimDeferred) - Волт, который не принимает ETH, больше не останавливает торговлю на своём рынке: хук
сохраняет то, что не смог выплатить, как долг перед этим волтом (
treasuryOwed), а лок удерживает отклонённую долю сбора комиссий для её получателя (vaultOwed,creatorOwed). Любой выплачивает их, как только получатель снова принимает ETH (payTreasury,payOwed), и кипер делает это в каждом цикле - Lens читает каждый волт отдельным вызовом, поэтому волт, который не может ответить, не
мешает читать остальные (
vaultReadable). С 2026-10-06 этот вызов также возвращает канал волта, его состояние паузы и USDC, которые он отправил через мост, а страница — то, что хук и лок должны волту: сервис данных приложения не читает по рынку ничего другого, поэтому волт, который сжигает весь свой газ, срывает только собственные цифры. С седьмого цикла аудита он также читает каждый апгрейдируемый модуль (хук, оракул, контракт аирдропа, оба хаба канала) и токен протокола, каждый в собственной группе, поэтому один модуль, который сжигает весь свой газ после неудачного апгрейда, срывает только собственные цифры
Рычаги. У каждого контракта, который может держать ETH или токены, даже мимоходом или по ошибке, есть рычаг, чтобы вывести застрявшее, и каждый модуль может принять исправление: апгрейд или сеттер, который заменяет модуль.
- У модулей, которые между транзакциями ничего ни для кого не хранят, — фабрики, Lens,
оракулов, регистратора владения, роутеров акций, официального своп-роутера и адаптеров
моста — есть
rescue(asset, amount, to)владельца протокола - У модулей, которые ведут собственные расчёты, — волтов, двух хабов и контракта аирдропа — есть аварийный режим
rescueхука забирает только то, что было отправлено ему по ошибке, и никогда — то, что он должен. Функция спасения лока никогда не забирает удерживаемые им доли и позиции, до которых добирается только режим завершения. Функция спасенияBuybackBurnerзабирает его ETH, только когда никакое сжигание уже не могло бы его потратить. Токены, которые нельзя апгрейдить, могут вывести то, что было отправлено на их собственный адрес- Без рычага, намеренно: два деплоера на Ethereum и деплоер зеркальных волтов, которые не хранят состояния, не принимают ETH и не имеют владельца
Подробности — в разделе Аварийный режим.
Одно намеренное исключение. Сообщение токена своему регистратору владения остаётся
блокирующим: если сообщение не проходит, перевод не проходит. Если бы сообщению было
позволено не пройти, холдер мог бы оставить его без газа в собственном переводе, чтобы
запись пропустила это перемещение, а его доля аирдропа выросла. Рычаг немедленный, по
одной транзакции на каждый: setRecorder(0) на токене, который останавливает его запись,
или апгрейд регистратора на месте. См. Аирдроп.
Офчейн-пакеты
| Пакет | Роль |
|---|---|
shared/ |
Сгенерированные ABI, константы протокола, форматирование. Единый источник для всего остального |
backend/ |
Сервис цен. Изолирует единственную внешнюю зависимость dapp |
keeper/ |
Конвертация, батчи моста, маршрутизация в удалённой сети, мониторинг доставки и, с 2026-10-05, шаг аирдропа, выплата того, что хук и лок удерживают для получателя, и сбор LP-комиссий |
cairn-app/ |
Приложение, фронтенд на Next.js, рядом с projet/ с 2026-10-02, когда оно заменило dapp на React + Vite: см. Dapp |
cairn-worker/ |
Граничный слой данных приложения: читает протокол один раз для всех открытых страниц и передаёт изменения |