Skip to content

Limits

What are Limits

The limit system in MultiVault protects against instant contract drainage in case of relay node compromise or vulnerability exploitation. Limits are applied to two types of operations:

  1. Deposit Limits — restrict the maximum amount of tokens that can be stored on MultiVault
  2. Withdrawal Limits — restrict the withdrawal amount per single transaction and cumulatively over 24 hours

Important

Limits exist only on the EVM network side.

Why are Limits Needed?

  • Relay node compromise — limits slow down fund withdrawal and provide time to detect an attack
  • Smart contract exploits — limits block large transactions
  • Liquidity protection — prevent instant pool drainage
  • Fraud detection window — exceeding limits creates a pending withdrawal requiring approval

Race condition on approve

Governance may approve a pending withdrawal, but the MultiVault balance may turn out to be insufficient — the status will change to Approved, but tokens will not be transferred. To complete the withdrawal, forceWithdraw() or Fill via LP will be required (see Liquidity Request).

Deposit Limits

Restrict the maximum token balance on MultiVault.

ParameterDescription
depositLimitMaximum token balance on MultiVault
Applies toAlien tokens only
When exceededDeposit is rejected (revert)

A deposit is rejected if the sum of the current MultiVault balance and the deposit amount exceeds the established limit. If the limit is not set (equals zero), the check is skipped.

Why only for Alien

Native tokens on deposit (EVM→TVM transfer) are burned, not stored on MultiVault. Therefore, the MultiVault balance for native tokens is always ~0.

Withdrawal Limits

Restrict withdrawal amounts from MultiVault.

ParameterDescription
undeclaredPer-transaction limit
dailyPer-period limit (24 hours)
enabledFlag to enable limits for the token
Applies toBoth Alien and Native tokens
When exceededPending Withdrawal is created

Invariant: daily >= undeclared

Withdrawal proceeds instantly if both conditions are met simultaneously. If limits are disabled for the token, the check is skipped.

Condition 1: Per-transaction (undeclared)

The current transaction amount must be strictly less than the per-transaction limit (undeclared).

Important: A transaction for exactly the limit amount requires approval.

Condition 2: Per-period (daily)

The sum of the current transaction amount plus already withdrawn during the period minus governance-approved amounts must be strictly less than the daily limit.

  • Already withdrawn (total) — cumulative withdrawal for the current 24-hour period
  • Approved (considered) — amounts approved by governance (subtracted to avoid blocking subsequent legitimate withdrawals)

Calculation Example

ParameterValue
Undeclared limit10,000 USDT
Daily limit50,000 USDT
Already withdrawn in period (total)30,000 USDT
Governance approved (considered)20,000 USDT
Withdrawal request15,000 USDT

Check 1 (per-transaction):

15,000 < 10,000 → ❌ FAIL

Check 2 (per-period):

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

Result: Although check 2 passed, check 1 failed → Pending Withdrawal is created with Required status.

24-Hour Period Mechanism

Period ID Calculation

Period ID is calculated by dividing the timestamp by the period duration (86400 seconds = 24 hours). All transactions within the same day receive the same period ID.

Examples

TimestampDate/time (UTC)Period ID
17040672002024-01-01 00:00:0019723
17041535992024-01-01 23:59:5919723
17041536002024-01-02 00:00:0019724

Automatic Reset

When transitioning to a new period, counters (total, considered) are automatically reset to zero (new entry in mapping).

Important: Uses eventTimestamp from the TVM event, not block.timestamp. This prevents manipulation through transaction execution delay.

Behavior When Limits Are Exceeded

When a withdrawal fails the limit check, a Pending Withdrawal with Required status is created — the same object as when liquidity is insufficient (see Liquidity Request), but with a different status and set of available actions.

Available Actions

A Pending Withdrawal with Required status awaits a governance decision:

ActionWho canDescription
Approvegovernance / withdrawGuardianApprove the withdrawal — if liquidity is available, tokens are sent immediately
Rejectgovernance / withdrawGuardianReject the withdrawal — recipient can return tokens via Cancel
Force WithdrawAnyoneAfter Approve — push through the withdrawal if it didn't execute automatically

TIP

Fill, Bounty, and Cancel actions are available for NotRequired status — see Liquidity Request.

How it works in the contract

Both methods (saveWithdrawNative, saveWithdrawAlien) first create a Pending with NotRequired status. Then, if limits are not passed, the status is immediately updated to Required. For Native tokens, the only reason for Pending is limits, so the status is always Required. For Alien — it depends on what exactly failed: balance, limits, or both.

Pending Creation Diagram



Details: Approve or Reject

Who can call: Only governance or withdrawGuardian

Requirements:

  • Current status must be Required
  • Can only set to Approved or Rejected

Logic on Approve:

  • If MultiVault balance is sufficient OR it's a Native token → automatic withdrawal
  • Otherwise, just changes status to Approved

Callback:NOT called

The setPendingWithdrawalApprove function checks that the current status is Required and that Approved or Rejected is being set. When setting Approved, if the MultiVault balance is sufficient or it's a Native token, withdrawal is automatically executed. In any case, the amount is added to considered for the current period.


Details: Force Withdraw (after Approve)

Who can call: Any address

Requirements:

  • Status Approved (or NotRequired)
  • amount > 0

Logic:

  • amount is transferred to the recipient in full
  • Bounty is not credited to anyone (goes to the recipient)

Callback:Called

The forceWithdraw function accepts an array of pending withdrawals and for each: resets the pending amount to zero, transfers the full amount to the recipient (without deducting bounty), and calls the callback.

Data Structures

Storage Mappings

MappingKeyDescription
tokens_token addressToken information, including depositLimit
withdrawalLimits_token addressundeclared, daily, enabled
withdrawalPeriods_token → period IDtotal (cumulative withdrawal), considered (governance-approved)
pendingWithdrawals_recipient → IDPending withdrawal parameters
pendingWithdrawalsPerUserrecipientPending counter (used as ID)
pendingWithdrawalsTotaltoken addressTotal pending by token

ApproveStatus

ValueStatusDescription
0NotRequiredApproval not required (insufficient funds on MultiVault)
1RequiredApproval required (limits exceeded)
2ApprovedApproved by governance or withdrawGuardian
3RejectedRejected

Limit Management

Setting Limits

All functions require the onlyGovernance modifier.

Deposit Limit

The setDepositLimit function sets the maximum token balance on MultiVault.

Withdrawal Limits

The following management functions are available:

  • setDailyWithdrawalLimits — sets the daily limit (checks that it's not less than undeclared)
  • setUndeclaredWithdrawalLimits — sets the per-transaction limit (checks that it's not more than daily)
  • enableWithdrawalLimits — enables limits for the token
  • disableWithdrawalLimits — disables limits for the token

Events

EventParametersWhen Emitted
UpdateDailyWithdrawalLimitstoken, limitDaily limit changed
UpdateUndeclaredWithdrawalLimitstoken, limitUndeclared limit changed
UpdateWithdrawalLimitStatustoken, statusLimits enabled/disabled
PendingWithdrawalCreatedrecipient, id, token, amount, payloadIdPending created when limits exceeded
PendingWithdrawalUpdateApproveStatusrecipient, id, approveStatusApprove or Reject
PendingWithdrawalWithdrawrecipient, id, amountAuto-withdrawal on approve

TIP

Fill, Cancel, and Bounty events are described in the Liquidity Request section.

Access Rights

ActionRequired Role
Set deposit limitgovernance
Set withdrawal limitsgovernance
Enable/disable limitsgovernance
Approve/Reject pendinggovernance OR withdrawGuardian

Error Codes

ErrorCause
"Deposit: limits violated"Deposit exceeds depositLimit for the token
"Settings: daily limit < undeclared"Attempt to set daily < undeclared or undeclared > daily
"Pending: wrong current approve status"Attempt to Approve/Reject a pending not in Required status
"Pending: wrong approve status"Invalid new status specified (not Approved or Rejected)
"Pending: zero amount"Pending already executed (amount = 0)
"Pending: params mismatch"Batch Approve: ID and status array lengths don't match

ChainConnect Bridge Documentation