Skip to content

Limits

What are Limits

Limits are a bridge protection mechanism against large-scale attacks and exploits. They restrict daily transfer volume for each token separately.

Purpose:

  • Protection against drainage attacks (mass fund withdrawal on hack)
  • Limiting potential damage from exploits
  • Time for responding to anomalous activity

Where Applied:

  • ProxyMultiVaultAlienJetton_V3 contracts (alien tokens)
  • ProxyMultiVaultNativeJetton_V3 contracts (native tokens)

Limit Types

The system supports two types of daily limits for each token:

TypeDescriptionApplication
IncomingLimit on token volume entering the networkDeposits from other networks to TVM
OutgoingLimit on token volume leaving the networkWithdrawals from TVM to other networks

Features

  • Limits are set per specific token, not globally
  • Each limit is optional — may not be set (null)
  • If limit is not set — check is skipped, transfer proceeds without restrictions
  • Limits are independent of user address — this is total daily volume for token

Data Structure

DailyLimits

solidity
struct DailyLimits {
    optional(uint128) incomingLimit;      // Incoming limit (null = no limit)
    uint128 dailyIncomingVolume;          // Current daily incoming volume

    optional(uint128) outgoingLimit;      // Outgoing limit (null = no limit)
    uint128 dailyOutgoingVolume;          // Current daily outgoing volume

    uint32 dayStartTimestamp;             // Current "day" start timestamp
}

Limit Check Mechanism

Check Algorithm

Limit Check Algorithm

Day Definition

Day is determined by dividing block.timestamp by 86400 (seconds per day).

  • Day resets at 00:00 UTC
  • On day change, volumes are reset
  • dayStartTimestamp is updated

When Limit is Checked

DirectionCheck Moment
Incoming (deposit)After event relay confirmation
Outgoing (withdraw)When receiving tokens for burn/transfer

Check Formula

  • Incoming: dailyIncomingVolume + amount > incomingLimit → LimitReached
  • Outgoing: dailyOutgoingVolume + amount > outgoingLimit → LimitReached

Important

Check is > (strictly greater), not >=. If limit = 100 and current volume = 0, then transfer for 100 will pass (100 > 100 = false).

Scenarios When Limit is Reached

Outgoing — LimitReached

When outgoing limit is exceeded:

  1. Tokens are returned to sender
  2. OutgoingLimitReached(token) event is emitted
  3. Transfer is not created

Incoming — LimitReached

When incoming limit is exceeded:

  1. Event contract transitions to LimitReached status (code 5)
  2. limitApprover address is set
  3. LimitReached(approver) event is emitted
  4. Transfer is delayed until approver decision

Actions on LimitReached (incoming)

LimitApprover can perform one of three actions:

ActionFunctionResult
ApproveapproveLimit()Transfer executes, tokens minted to recipient
Cancelcancel()Tokens returned to source network
RejectrejectLimit()Event rejected, funds remain in source network

Retry Mechanism

User or initializer can call retry() for another transfer attempt (e.g., after limit reset on new day).

Limit Recovery

Limit automatically resets at 00:00 UTC the next day. Manual reset is not provided — only limit value change via setTokenDailyLimits().

Limit Management

Setting Limits

solidity
setTokenDailyLimits(_token, _incomingLimit, _outgoingLimit)

Parameters:

  • _token — token address
  • _incomingLimit — incoming limit (null = no limit)
  • _outgoingLimit — outgoing limit (null = no limit)

Access rights: Contract owner only

Setting LimitApprover

solidity
setLimitApprover(approverAddress)

Access rights: Contract owner only

Reading Limits

  • getDailyLimits(token) — returns DailyLimits structure for token
  • getLimitApprover() — returns current approver address

Default Limits

By default, limits are not set (null). This means unlimited transfers until administrator explicitly sets limits.

API/Contract Reference

Proxy Contracts (Alien and Native)

FunctionAccessDescription
setTokenDailyLimits()ownerSet/change token limits
getDailyLimits()publicGet current limits and volumes
setLimitApprover()ownerSet approver address
getLimitApprover()publicGet approver address

Event Contracts

FunctionAccessDescription
approveLimit()limitApproverApprove transfer on LimitReached
cancel()limitApproverCancel and return funds
rejectLimit()limitApproverReject transfer
retry()recipient/initializerRetry transfer attempt

Events

EventWhen Emitted
OutgoingLimitReached(address token)When outgoing limit is exceeded
LimitReached(address approver)When incoming limit is exceeded

Errors and Edge Cases

Error Codes

CodeConstantDescription
2335SENDER_IS_NOT_LIMIT_APPROVERCaller is not limit approver
2324WRONG_STATUSInvalid status for operation

Edge Cases

SituationBehavior
Limit = 0Any transfer > 0 will exceed limit
Limit = nullNo restrictions
First transfer of dayVolume resets, then amount is added
Transfer exactly equals limitWill pass (check is >, not >=)
limitApprover = zero addressapproveLimit() will fail

Important Notes

  1. Partial execution is NOT supported — transfer either passes completely or is rejected
  2. Limits are checked AFTER relay confirmation for incoming transfers
  3. Limits are checked BEFORE event creation for outgoing transfers
  4. On outgoing LimitReached, tokens are returned instantly, no manual intervention required

ChainConnect Bridge Documentation