Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.hyperlane.xyz/llms.txt

Use this file to discover all available pages before exploring further.

The RateLimitedIsm is an ISM for warp routes that caps the token volume transferable to a destination chain within a configurable window. It pairs with a RateLimitedHook on the origin side, giving warp route deployers bidirectional control over transfer throughput.

How it works

Both contracts inherit from RateLimited, which implements a token bucket algorithm:
  • The bucket starts full at deployment (filledLevel = maxCapacity).
  • It refills continuously at refillRate = maxCapacity / DURATION tokens per second.
  • Each transfer consumes capacity equal to the token amount in the message.
  • If a transfer would exceed the current available capacity, it reverts with RateLimitExceeded.
  • After a full DURATION of inactivity the bucket is fully restored.
uint256 public constant DURATION = 1 days; // 86400 seconds
maxCapacity = refillRate × DURATION
currentLevel ≈ previousLevel + (elapsed × refillRate)
The refill window is DURATION, set to 1 day. The capacity — maxCapacity, expressed in the token’s base unit (e.g. 1_000_000e6 for 1M USDC) — is set at deployment and can be updated by the warp route owner at any time.

Contracts

ContractChain sideRole
RateLimitedHookOriginEnforces the limit at dispatch time (outbound)
RateLimitedIsmDestinationEnforces the limit at delivery time (inbound)
Either contract can be deployed independently or together. Deploying both provides defense in depth: transfers are blocked on the origin before they are relayed, and again on the destination before funds are released.

RateLimitedIsm

The verify() function:
  1. Requires the message recipient matches the configured recipient address (onlyRecipient).
  2. Requires the message has not already been validated — tracked via messageValidated[messageId] (replay protection).
  3. Confirms the message was delivered by the Mailbox (_isDelivered).
  4. Extracts the token amount and calls _validateAndConsumeFilledLevel.
function verify(
    bytes calldata,
    bytes calldata _message
) external onlyRecipient(_message) validateMessageOnce(_message) returns (bool) {
    require(_isDelivered(_message.id()), "InvalidDeliveredMessage");
    uint256 newAmount = _message.body().amount();
    _validateAndConsumeFilledLevel(newAmount);
    return true;
}

RateLimitedHook

The _postDispatch() function:
  1. Requires the message sender matches the configured sender address (onlySender).
  2. Requires the message has not already been processed (replay protection).
  3. Confirms this is the most recently dispatched message (_isLatestDispatched).
  4. Extracts the token amount and calls _validateAndConsumeFilledLevel.
function _postDispatch(
    bytes calldata,
    bytes calldata _message
) internal override onlySender(_message) validateMessageOnce(_message) {
    require(_isLatestDispatched(_message.id()), "InvalidDispatchedMessage");
    uint256 newAmount = _message.body().amount();
    _validateAndConsumeFilledLevel(newAmount);
}

Properties

Blast radius containment

Rate limits directly bound the maximum loss from a compromised bridge. If an attacker finds an exploit in the token contract or a validator key is leaked, they can drain at most maxCapacity tokens before the bucket empties and all transfers revert with RateLimitExceeded. This gives warp route owners time to detect the incident and pause or upgrade the route before a full treasury loss.

Replay protection

Both contracts maintain a messageValidated[messageId] mapping. A message that has already consumed capacity cannot be re-submitted, preventing double-spend attacks via message replay.

Recipient and sender binding

RateLimitedIsm is constructed with a fixed recipient address and rejects any message bound for a different contract. RateLimitedHook is constructed with a fixed sender address and rejects messages from unauthorized callers. This prevents the contracts from being used to validate or throttle transfers for unintended warp routes.

Owner-controlled capacity

setRefillRate(uint256 _capacity) is onlyOwner, allowing warp route owners to adjust the cap without redeploying:
// SDK enforces capacity >= DURATION (86400) and rounds down to nearest multiple
// e.g. 86401 → refillRate = 1 → effective maxCapacity = 86400 (1 unit lost)
ism.setRefillRate(newCapacity); // new refillRate = newCapacity / DURATION

Aggregation ISM compatibility

RateLimitedIsm can be nested inside an AggregationIsm to layer rate limiting on top of other security checks — for example, requiring both a MultisigIsm and a RateLimitedIsm to pass before a message is delivered. This requires relayer version agents-v2.2.0 or later. For defense in depth, pair RateLimitedIsm with a PausableIsm inside an AggregationIsm. The rate limit caps the damage during an active exploit while the owner detects the incident and pauses the route to fully close it.

SDK configuration

The TypeScript SDK and CLI support IsmType.RATE_LIMITED, enabling warp route configs to specify a RateLimitedIsm directly. The SDK type string is 'rateLimitedIsm'. See typescript/sdk/src/ism/types.ts. The config schema accepts:
FieldTypeRequiredNotes
type'rateLimitedIsm'Yes
maxCapacitystring (bigint)YesMust be ≥ 86400; rounded down to nearest multiple of 86400
recipientaddressNoDefaults to the warp route address
owneraddressNoDefaults to the deployer; receives onlyOwner rights (e.g. setRefillRate)
Example warp route ISM config:
interchainSecurityModule:
  type: rateLimitedIsm
  maxCapacity: "1000000000000"   # 1M USDC (6 decimals) per day

Deployment

// Destination chain — ISM
RateLimitedIsm ism = new RateLimitedIsm(
    mailbox,        // Mailbox address on destination
    1_000_000e6,    // maxCapacity in token base units
    warpRouteAddress
);

// Origin chain — Hook (optional, for bidirectional enforcement)
RateLimitedHook hook = new RateLimitedHook(
    mailbox,        // Mailbox address on origin
    1_000_000e6,    // maxCapacity in token base units
    warpRouteAddress
);