Skip to main content
Version: Endpoint V2

Rate Limiter

The RateLimiter.sol is used to control the number of cross-chain messages that can be sent within a certain time window, ensuring that the OApp is not spammed by too many transactions at once. It's particularly useful for:

  • Preventing Denial of Service Attacks: By setting thresholds on the number of messages that can be processed within a given timeframe, the RateLimiter acts as a safeguard against DoS attacks, where malicious actors might attempt to overload an OApp with a flood of transactions. This protection ensures that the OApp remains accessible and functional for legitimate users, even under attempted attacks.

  • Regulatory Compliance: Some applications may need to enforce limits to comply with legal or regulatory requirements.

The RateLimiter is only useful under specific application use cases. It will not be necessary for most OApps and can even be counterproductive for more generic applications:

  • Low Traffic Applications: If your application doesn't expect high volumes of traffic, implementing a rate limiter might be unnecessary overhead.

  • Critical Systems Requiring Immediate Transactions: For systems where transactions need to be processed immediately without delay, rate limiting could hinder performance.

Installation

To begin working with LayerZero contracts, you can install the OApp npm package to an existing project:

npm install @layerzerolabs/lz-evm-oapp-v2

Usage

Import the RateLimiter.sol contract into your OApp contract file and inherit the contract:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { OApp } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
import { RateLimiter } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/utils/RateLimiter.sol";

contract MyOApp is OApp, RateLimiter {
// ...contract
}

Initializing Rate Limits

In the constructor of your contract, initialize the rate limits using _setRateLimits with an array of RateLimitConfig structs.

Example:

constructor(
RateLimitConfig[] memory _rateLimitConfigs,
address _lzEndpoint,
address _delegate
) OApp(_lzEndpoint, _delegate) {
_setRateLimits(_rateLimitConfigs);
}
// ...Rest of contract code

RateLimitConfig Struct:

struct RateLimitConfig {
uint32 dstEid; // destination endpoint ID
uint256 limit; // arbitrary limit of messages/tokens to transfer
uint256 window; // window of time before limit resets
}

Setting Rate Limits

Provide functions to set or update rate limits dynamically. This can include a function to adjust individual or multiple rate limits and a mechanism to authorize who can make these changes (typically restricted to the contract owner or a specific role).

/**
* @dev Sets the rate limits based on RateLimitConfig array. Only callable by the owner or the rate limiter.
* @param _rateLimitConfigs An array of RateLimitConfig structures defining the rate limits.
*/
function setRateLimits(
RateLimitConfig[] calldata _rateLimitConfigs
) external {
if (msg.sender != rateLimiter && msg.sender != owner()) revert OnlyRateLimiter();
_setRateLimits(_rateLimitConfigs);
}

Checking Rate Limits During Send Calls

Before processing transactions, use _checkAndUpdateRateLimit to ensure the transaction doesn't exceed the set limits. This function should be called in any transactional functions, such as message passing or token transfers.

Message Passing

function send(
uint32 _dstEid,
string memory _message,
bytes calldata _options
) external payable {
_checkAndUpdateRateLimit(_dstEid, 1); // updating the rate limit per message sent
bytes memory _payload = abi.encode(_message); // Encodes message as bytes.
_lzSend(
_dstEid, // Destination chain's endpoint ID.
_payload, // Encoded message payload being sent.
_options, // Message execution options (e.g., gas to use on destination).
MessagingFee(msg.value, 0), // Fee struct containing native gas and ZRO token.
payable(msg.sender) // The refund address in case the send call reverts.
);
}

Token Transfers

/**
* @dev Checks and updates the rate limit before initiating a token transfer.
* @param _amountLD The amount of tokens to be transferred.
* @param _minAmountLD The minimum amount of tokens expected to be received.
* @param _dstEid The destination endpoint identifier.
* @return amountSentLD The actual amount of tokens sent.
* @return amountReceivedLD The actual amount of tokens received.
*/
function _debit(
uint256 _amountLD,
uint256 _minAmountLD,
uint32 _dstEid
) internal virtual override returns (uint256 amountSentLD, uint256 amountReceivedLD) {
_checkAndUpdateRateLimit(_dstEid, _amountLD);
return super._debit(_amountLD, _minAmountLD, _dstEid);
}