MagnetLiquidityRent
Контракт держит позиции ликвидности Uniswap V3 и выдаёт пользователям временный доступ к их доходу. Продукт-агностичен: VIP — первый потребитель, следующий продукт добавляется планом и своим хуком-грантором, сам контракт при этом не меняется.
Файлы: apps/contracts-v2/contracts/MagnetLiquidity/MagnetLiquidityRent.sol, хранилище — storage/RentStorage.sol, интерфейс — interfaces/IMagnetLiquidityRent.sol.
Сущности
| Сущность | Что описывает |
|---|---|
| Группа позиций | Набор позиций одного пула с общими настройками: токены, тир, payoutToken |
| План аренды | Правила доступа: режим завершения, длительности, окно дозабора, потолок выплаты, порог доходности, нужна ли конвертация |
| Аренда | Связка «пользователь ↔ позиция ↔ план» с таймерами и счётчиком выплаченного |
Жизненный цикл аренды
- Выдача.
grantAccess(для гранторов) илиactivateWithPermit(пользователь с подписью оператора) занимает свободную позицию группы. Доход, накопленный позицией до этого момента, уходит в казну — новый арендатор начинает с нуля. - Клейм.
claimсобирает комиссии позиции, при необходимости конвертирует их вpayoutTokenи переводит пользователю. Всё, что выше потолка плана, уходит в казну. - Закрытие. По истечении доступа
closeExpiredRentalsфиксирует остаток в снапшоте, освобождает позицию и открывает окно дозабора. Своп при закрытии не делается: транзакция чужая, задать проскальзывание некому. - Дозабор. Пока идёт окно, пользователь забирает снапшот тем же
claim. - Сгорание. После окна
sweepExpiredSnapshotsпереводит остаток в казну.
Режимы завершения
FIXED_TIME— доступ живётdurationSeconds.PROFIT_TARGET— доступ живёт, пока пользователю не выплаченоreferenceAmount × targetPercentBps / 10000, но не дольшеmaxDurationSeconds. В порог засчитывается только фактически выплаченное: срезанный потолком остаток к цели не приближает.
Пороги считаются в payoutToken. Оракула в проекте нет, поэтому «вложенная сумма» задаётся админом вручную в админке — это осознанный компромисс, зафиксированный в ТЗ.
Роли
DEFAULT_ADMIN_ROLE— группы, позиции, планы, казна, верификатор подписи, пауза, апгрейд.OPERATOR_ROLE—sweepExpiredSnapshots,forceCloseRental; используется кроном бэкенда.GRANTER_ROLE— продуктовые хуки, которым разрешено открывать аренду.
Пауза останавливает выдачу новых аренд, но не блокирует клейм: заработанное должно оставаться доступным.
Связь с VIP
VipPaymentHook стал кассой продукта: цена, персональный лимит покупок, вызов grantAccess при оплате. Состояние дос тупа он больше не хранит, а hasActiveVip / getActiveVipAccounts / getVipConfig работают как view-прокси в аренду — поэтому бэкенд и админка не переписывались.
Бесплатная активация проходит ончейн: бэкенд выдаёт подпись RentPermit, пользователь сам отправляет activateWithPermit. Использование гранта резервируется в момент выдачи подписи и возвращается заданием, если транзакция так и не пришла.
Обслуживание
Крон бэкенда (apps/backend/src/liquidity-rent/jobs/rentClosure.job.ts) раз в минуту закрывает истёкшие аренды и зачищает сгоревшие снапшоты. Если крон простаивал, выдача следующей аренды сама закроет до трёх просроченных — окно дозабора при этом отсчитывается от момента закрытия, а не от вызова крона.
Ссылки
- ТЗ:
apps/docs/docs/tz/active/liquidity-rent.md - Дизайн:
docs/superpowers/specs/2026-07-28-liquidity-rent-design.md - Тесты:
apps/contracts-v2/test/MagnetLiquidityRent.test.ts