Перейти к основному содержимому

Известные проблемы и риски

Найдено при разборе кода на 07.10.2026 (ветка preprod-start-v2). Раздел ликвидности пока в тестовом раскатывании, поэтому большинство пунктов ещё не проявились у пользователей. Их нужно закрыть до открытия раздела.

Критичные​

Цена земли не проверяется​

createGroundBuySignature подписывает token и amount из тела запроса без сверки с ценой (apps/backend/src/liquidity/liquidity.service.ts:1124-1153). Контракт тоже цену не знает — там TODO про управление ценой (MagnetLiquidityGround.sol:131).

Последствие. Любой пользователь может получить подпись на землю за 1 wei или за свой собственный токен. Сейчас фронт и так продаёт землю за 0, но как только появится цена, обход будет тривиальным.

Что делать. Хранить цену и разрешённый токен по уровням в конфиге бэкенда (или в контракте) и подписывать только их.

Оплата за землю остаётся на контракте навсегда​

После начисления долей партнёрам остаток платежа лежит на MagnetLiquidityGround, а функции вывода в контракте нет — ни для админа, ни для распределения в компанию.

Последствие. С ненулевой ценой деньги за земли окажутся заблокированы до апгрейда контракта (он UUPS, так что это поправимо).

Что делать. Добавить в контракт получателей «в компанию» по аналогии с buyDistributionTargets или админский вывод.

Партнёрку с вышек нельзя забрать​

MagnetLiquidityShared.claimPartnerRewards требует подпись ClaimPartnerPermit от signatureVerifier, но эндпоинта, который её выдаёт, нет ни в одном бэкенде. UI клейма партнёрки на фронте тоже нет.

Последствие. Начисленные с покупок вышек доли копятся без возможности вывода.

Что делать. Эндпоинт подписи с maxLines по правилам квалификации и UI клейма для обоих контрактов.

Высокие​

Масштаб партнёрских процентов​

Бэкенд передаёт [5, 5, 5, 5], контракт делит на 10 000: каждая линия получает 0,05 %, а не 5 %. Тесты контракта считают в базисных пунктах (500 = 5 %). Подробнее — в партнёрке.

Что делать. Решить, какой процент задуман, и вынести базовые проценты в конфиг.

Вторая земля на уровнях 2–15​

Для groundIndex ≥ 1 дерево начинается только тогда, когда у аплайна уже есть земля с таким индексом. Для первой такой земли бэкенд отдаёт фолбэк groundId = 1 (apps/p2-backend/src/tree.service.ts:2164-2171), а это корень первого уровня — контракт отклоняет покупку с InvalidUpline (MagnetLiquidityGround.sol:158-161). Индексатор при этом готов создать «виртуальный» корень, но уникальность (groundId, groundIndex) допускает его только для одного уровня.

Последствие. На уровнях 2–15 у пользователя может быть только одна земля.

Что делать. Минтить корневые земли для каждого индекса или передавать корень уровня (groundId = level) и создавать корни для индексов в графе.

Вышка может не попасть в граф без ошибки​

CREATE_TOWER_QUERY создаёт вышку только при upline.places > 0 и если нашлась своя земля покупателя (apps/p2-backend/src/jobs/liquidityTowerEvents.job.ts:14-38). Иначе запрос не возвращает строк, а крон коммитит транзакцию и отмечает операцию завершённой.

Последствие. NFT вышки в сети есть, в графе её нет, места не списаны. Защита — только кулдаун подписей (SIGNATURE_COOLDOWN).

Что делать. Проверять результат запроса и бросать ошибку или писать алерт.

Средние​

Одно событие останавливает индексацию​

Если обработчик события бросил ошибку, EventsService не сдвигает курсор, и каждый следующий запуск падает на том же событии (packages/reusable-magnet-backend/src/events/events.service.ts). Пример — Neo4j ground creation failed, если родитель не нашёлся.

Последствие. Все следующие покупки земель (или вышек) не попадают в граф, бэкенд выдаёт подписи по устаревшим данным.

Что делать. Отдельная очередь «проблемных» событий и алерт.

Клейм партнёрки с земель без ограничения линий​

MagnetLiquidityGround.claimPartnerRewards(tokens, maxLines) не требует подписи: maxLines выбирает сам пользователь, 0 — все 15 линий. ТЗ (tz/active/liquidity-nft.md) требует, чтобы число доступных линий задавала подпись бэкенда, как в MagnetLiquidityShared.

Трансфер земли не отражается в учёте​

Токен земли передаётся без ограничений, но владелец в графе (ownerUid) и получатель партнёрки (uid в partnerRewardsBalance) не меняются. Новый держатель не может строить вышки на этой земле и не получает партнёрку с неё.

Что делать. Либо запретить трансфер, как у вышек, либо индексировать Transfer и переносить владельца.

Места не освобождаются​

Полный вывод ликвидности сжигает NFT вышки (MagnetLiquiditySharedOps.sol → withdraw, _burn при нулевой доле), но граф об этом не знает: вышка остаётся, места на обеих землях заняты. Трансфер вышки тоже не меняет Tower.ownerUid.

Лок родителя тормозит покупки​

Ключ liquidity:ground:buy-parent-lock:<parentId> живёт 140 с и после выдачи подписи не снимается (apps/backend/src/liquidity/liquidity.service.ts:1019-1073). Все покупки под одной землёй идут не чаще одной в 140 с. Под корневыми землями uid 1 окажется большинство первых покупок, так что это узкое место на старте.

Что делать. Снимать ключ, когда операция завершилась (по событию), или убрать лок, если он не нужен: земли под землёй места не тратят.

Окно поиска событий покупки — 50 блоков​

getBuyNftEventLookbackBlocks возвращает 50, код расчёта окна в 25 минут под return недостижим (liquidity.service.ts:890-905). Если транзакция покупки вышки попала в блок старше 50 последних, а индексатор отстаёт, сверка её не увидит и пометит операцию expired, после чего кулдаун пропустит новую подпись на то же место.

В ТЗ — партнёрка при клейме дохода​

ТЗ предполагает распределение партнёрки и при клейме дохода вышки. В коде при клейме работают только claimDistributionTargets, партнёрская цепочка передаётся пустой (MagnetLiquiditySharedOps.sol:621-637).

Низкие​

  • Кешбэк на землю ничего не возвращает. Покупатель платит полную цену, «отданная» партнёром часть остаётся на контракте (кешбэк).
  • Кешбэк меньше 20 % не действует. При базе 5 округление вверх даёт тот же процент.
  • Номера мест вышки завышены, когда на земле есть и свои, и чужие вышки: count() по произведению двух OPTIONAL MATCH (liquidityTowerEvents.job.ts:139-163). На логику мест не влияет.
  • Цепочка Program 1 сортируется по uid, а не по глубине (tree.service.ts:1817). Работает, пока аплайн всегда старше даунлайна.
  • configureCashback в контракте не используется, параметр signature не проверяется (MagnetLiquidityGround.sol:97-112).
  • GET /liquidity/grounds сортирует вышки по несуществующему towerIndex и возвращает только свои вышки на земле, без вышек даунлайна.
  • База groundmatrix не описана в ProdDB и, судя по описанию бэкапов, не бэкапится. Восстановить граф из событий можно только переиндексацией с блока деплоя контрактов.
  • Фронт. Повтор подписи на 409 без ограничения попыток; проверка отмены во время ожидания недостижима; дедлайн покупки вышки считается по времени блока Polygon; остались отладочные console.log и parseError.