Skip to content

Лимиты (Limits)

Что такое лимиты

Система лимитов в MultiVault защищает от мгновенного опустошения контракта при компрометации relay-нод или эксплойте уязвимости. Лимиты применяются к двум типам операций:

  1. Лимиты на депозит — ограничивают максимальное количество токенов, которое может храниться на MultiVault
  2. Лимиты на вывод — ограничивают сумму вывода за одну транзакцию и суммарно за 24 часа

Важно

Лимиты существуют только на стороне EVM сети.

Зачем нужны лимиты?

  • Компрометация relay-нод — лимиты замедлят вывод средств и дадут время на обнаружение атаки
  • Эксплойты смарт-контрактов — лимиты блокируют крупные транзакции
  • Защита ликвидности — предотвращают моментальное опустошение пулов
  • Fraud detection window — превышение лимитов создаёт pending withdrawal, требующий одобрения

Race condition при approve

Governance может одобрить pending, но баланс MultiVault окажется недостаточен — статус изменится на Approved, но токены не будут переведены. Для завершения потребуется forceWithdraw() или Fill через LP (см. Liquidity Request).

Лимиты на депозит (Deposit Limits)

Ограничивают максимальный баланс токена на MultiVault.

ПараметрОписание
depositLimitМаксимальный баланс токена на MultiVault
ПрименяетсяТолько для Alien токенов
При превышенииДепозит отклоняется (revert)

Депозит отклоняется, если сумма текущего баланса MultiVault и суммы депозита превысит установленный лимит. Если лимит не установлен (равен нулю), проверка пропускается.

Почему только для Alien

Native токены при депозите (трансфере EVM→TVM) сжигаются (burn), а не хранятся на MultiVault. Поэтому баланс MultiVault для native токенов всегда ~0.

Лимиты на вывод (Withdrawal Limits)

Ограничивают суммы вывода токенов из MultiVault.

ПараметрОписание
undeclaredЛимит на одну транзакцию (per-transaction limit)
dailyЛимит на сумму за 24 часа (per-period limit)
enabledФлаг включения лимитов для токена
ПрименяетсяДля Alien и Native токенов
При превышенииСоздаётся Pending Withdrawal

Инвариант: daily >= undeclared

Вывод проходит мгновенно, если выполняются оба условия одновременно. Если лимиты отключены для токена, проверка пропускается.

Условие 1: Per-transaction (undeclared)

Сумма текущей транзакции должна быть строго меньше лимита на одну транзакцию (undeclared).

Важно: Транзакция ровно на сумму лимита требует одобрения.

Условие 2: Per-period (daily)

Сумма текущей транзакции плюс уже выведенное за период минус одобренное governance должна быть строго меньше суточного лимита (daily).

  • Уже выведено (total) — суммарный вывод за текущий 24-часовой период
  • Одобрено (considered) — суммы, одобренные governance (вычитаются, чтобы не блокировать последующие легитимные выводы)

Пример расчёта

ПараметрЗначение
Undeclared limit10,000 USDT
Daily limit50,000 USDT
Уже выведено за период (total)30,000 USDT
Одобрено governance (considered)20,000 USDT
Запрос на вывод15,000 USDT

Проверка 1 (per-transaction):

15,000 < 10,000 → ❌ FAIL

Проверка 2 (per-period):

15,000 + 30,000 - 20,000 = 25,000 < 50,000 → ✅ PASS

Результат: Хотя проверка 2 прошла, проверка 1 провалилась → создаётся Pending Withdrawal со статусом Required.

Механизм 24-часовых периодов

Вычисление ID периода

ID периода вычисляется делением timestamp на длительность периода (86400 секунд = 24 часа). Все транзакции в рамках одних суток получают одинаковый ID периода.

Примеры

TimestampДата/время (UTC)Period ID
17040672002024-01-01 00:00:0019723
17041535992024-01-01 23:59:5919723
17041536002024-01-02 00:00:0019724

Автоматический сброс

При переходе в новый период счётчики (total, considered) автоматически обнуляются (новая запись в маппинге).

Важно: Используется eventTimestamp из TVM события, а не block.timestamp. Это предотвращает манипуляции через задержку исполнения транзакции.

Поведение при превышении лимитов

Когда вывод не проходит проверку лимитов, создаётся Pending Withdrawal со статусом Required — тот же объект, что и при нехватке ликвидности (см. Liquidity Request), но с другим статусом и набором действий.

Доступные действия

Pending Withdrawal со статусом Required ждёт решения governance:

ДействиеКто можетОписание
Approvegovernance / withdrawGuardianОдобрить вывод — если ликвидность есть, токены отправляются сразу
Rejectgovernance / withdrawGuardianОтклонить вывод — получатель может вернуть токены через Cancel
Force WithdrawЛюбойПосле Approve — протолкнуть вывод, если автоматически не прошёл

TIP

Действия Fill, Bounty, Cancel доступны для статуса NotRequired — см. Liquidity Request.

Как это работает в контракте

Оба метода (saveWithdrawNative, saveWithdrawAlien) сначала создают Pending со статусом NotRequired. Затем, если лимиты не пройдены, статус сразу обновляется на Required. Для Native токенов единственная причина Pending — лимиты, поэтому статус всегда Required. Для Alien — зависит от того, что именно не прошло: баланс, лимиты или оба.

Диаграмма создания Pending



Детали: Approve или Reject

Кто может вызвать: Только governance или withdrawGuardian

Требования:

  • Текущий статус должен быть Required
  • Можно установить только Approved или Rejected

Логика при Approve:

  • Если баланс MultiVault достаточен ИЛИ это Native токен → автоматический вывод
  • Иначе просто меняется статус на Approved

Callback:НЕ вызывается

Функция setPendingWithdrawalApprove проверяет, что текущий статус Required и что устанавливается Approved или Rejected. При установке Approved, если баланс MultiVault достаточен или это Native токен, автоматически выполняется вывод. В любом случае сумма добавляется к considered для текущего периода.


Детали: Force Withdraw (после Approve)

Кто может вызвать: Любой адрес

Требования:

  • Статус Approved (или NotRequired)
  • amount > 0

Логика:

  • amount переводится получателю в полном объёме
  • Bounty никому не зачисляется (идёт получателю)

Callback:Вызывается

Функция forceWithdraw принимает массив pending withdrawals и для каждого: обнуляет сумму pending, переводит получателю полную сумму (без вычета bounty) и вызывает callback.

Структура данных

Storage маппинги

МаппингКлючОписание
tokens_token addressИнформация о токене, включая depositLimit
withdrawalLimits_token addressundeclared, daily, enabled
withdrawalPeriods_token → period IDtotal (суммарный вывод), considered (одобренное governance)
pendingWithdrawals_recipient → IDПараметры отложенного вывода
pendingWithdrawalsPerUserrecipientСчётчик pending (используется как ID)
pendingWithdrawalsTotaltoken addressСуммарный pending по токену

ApproveStatus

ЗначениеСтатусОписание
0NotRequiredОдобрение не требуется (недостаток средств в MultiVault)
1RequiredТребуется одобрение (превышение лимитов)
2ApprovedОдобрено governance или withdrawGuardian
3RejectedОтклонено

Управление лимитами

Установка лимитов

Все функции требуют модификатор onlyGovernance.

Deposit Limit

Функция setDepositLimit устанавливает максимальный баланс токена на MultiVault.

Withdrawal Limits

Доступны следующие функции управления:

  • setDailyWithdrawalLimits — устанавливает суточный лимит (проверяет, что он не меньше undeclared)
  • setUndeclaredWithdrawalLimits — устанавливает лимит на транзакцию (проверяет, что он не больше daily)
  • enableWithdrawalLimits — включает лимиты для токена
  • disableWithdrawalLimits — отключает лимиты для токена

События

СобытиеПараметрыКогда эмитится
UpdateDailyWithdrawalLimitstoken, limitИзменён daily limit
UpdateUndeclaredWithdrawalLimitstoken, limitИзменён undeclared limit
UpdateWithdrawalLimitStatustoken, statusВключены/отключены лимиты
PendingWithdrawalCreatedrecipient, id, token, amount, payloadIdСоздан pending при превышении лимитов
PendingWithdrawalUpdateApproveStatusrecipient, id, approveStatusApprove или Reject
PendingWithdrawalWithdrawrecipient, id, amountАвтовывод при approve

TIP

События Fill, Cancel, Bounty описаны в разделе Liquidity Request.

Права доступа

ДействиеТребуемая роль
Установить deposit limitgovernance
Установить withdrawal limitsgovernance
Включить/отключить лимитыgovernance
Approve/Reject pendinggovernance ИЛИ withdrawGuardian

Коды ошибок

ОшибкаПричина
"Deposit: limits violated"Депозит превышает depositLimit для токена
"Settings: daily limit < undeclared"Попытка установить daily < undeclared или undeclared > daily
"Pending: wrong current approve status"Попытка Approve/Reject для pending, который не в статусе Required
"Pending: wrong approve status"Указан недопустимый новый статус (не Approved и не Rejected)
"Pending: zero amount"Pending уже исполнен (amount = 0)
"Pending: params mismatch"Массовый Approve: длины массивов ID и статусов не совпадают

ChainConnect Bridge Documentation