Flow limits
Cap how much of a token can move into or out of a chain in each 6-hour window.
A flow limit caps the net amount of a token that can move into or out of a chain in each 6-hour window. It limits the damage if a chain, or the token's contract on it, is compromised: an attacker can move at most one limit's worth before someone reacts.
Each token has its own flow limit on each chain, kept by ITS on that chain: on EVM chains and Solana in the token's token manager (FlowLimit on EVM), on Sui and Stellar in ITS itself.
How it's applied
- Windows. Time is split into fixed 6-hour windows of the chain's clock. Amounts reset at the start of each window.
- Net flow. ITS adds up the amounts sent out of the chain and received into it in the current window. A transfer fails if the difference between the two would exceed the limit. Transfers in opposite directions cancel out.
- Where it fails. An outgoing transfer over the limit fails on the source chain. An incoming one fails when it's executed on the destination chain: the message stays approved, and can be executed again in a later window.
Amounts are in the token's smallest unit on that chain. By default, a token has no flow limit. How "no limit" is written differs by chain:
| Chain | No limit | A limit of 0 |
|---|---|---|
| EVM | 0, the default | Means no limit |
| Solana | None, the default | Blocks every transfer |
| Sui | none, the default | Blocks every transfer |
| Stellar | None, the default | Blocks every transfer |
Who manages it
On every chain, ITS's operator, an account Axelar controls, can set the flow limit of a token. Axelar's operations tooling uses this for emergencies: it freezes a token on a chain by setting a limit that blocks its transfers, and lifts the limit afterwards.
Who else can set it depends on the chain:
| Chain | Also set by | Can Axelar's operator be removed? |
|---|---|---|
| EVM | The token manager's operator and the flow limiters it adds | Yes, see below |
| Solana | The token manager's operator and its flow limiters | No |
| Sui | The token's operator | No |
| Stellar | Flow limiters that ITS's operator approves for the token | No |
The integration pages show the calls on each chain. The ITS Hub has no flow limits of its own.
On EVM chains
A flow limiter of the token manager calls:
function setFlowLimit(uint256 flowLimit_) external; // on the token managerTo read the limit and the current window's flows:
function flowLimit() external view returns (uint256);
function flowOutAmount() external view returns (uint256); // sent out in the current window
function flowInAmount() external view returns (uint256); // received in the current windowThe token manager's address is deployedTokenManager(tokenId) on ITS. A single transfer larger than the limit also fails.
ITS is a flow limiter of every token manager when it's deployed, and ITS's operator sets limits through it with setFlowLimits on ITS. Axelar freezes a token by setting the limit to 1, and lifts the freeze by setting it back to 0.
The token manager's operator is also a flow limiter. For an interchain token, the operator is the minter you deployed it with; for a custom token, the operator you passed when linking it. A canonical token has no operator, so only ITS can set its limits. The operator decides who keeps the role:
| You want | Do |
|---|---|
| Axelar to be able to freeze the token in an emergency | Nothing: ITS is a flow limiter from the start |
| To set limits yourself as well | Keep an operator, which is also a flow limiter (for an interchain token, deploy it with a minter) |
| To be the only one who can set limits | As operator, call removeFlowLimiter with ITS's address |
| No one to be able to change them | Remove ITS as above, remove yourself with removeFlowLimiter, then give up the operator role with transferOperatorship(address(0)) |
Set the limit you want before removing yourself: without a flow limiter, it can't be changed. Removing ITS also means Axelar can't freeze the token on that chain in an emergency.