Set Token Pool rate limits using Hardhat

This tutorial will guide you through the process of updating the rate limiter settings for outbound and inbound transfers in your deployed token pools using Hardhat. You will first review existing rate limiter settings and then update them.

Prerequisites

Before You Begin

  1. Make sure you have Node.js v18 or above installed. If not, install Node.js v18:
    Download Node.js 18 if you don't have it installed. Optionally, you can use the nvm package to switch between Node.js versions:

    nvm use 18
    

    Verify that the correct version of Node.js is installed:

    node -v
    

    Example output:

    $ node -v
    v18.7.0
    
  2. Clone the repository and navigate to the project directory:

    git clone https://github.com/smartcontractkit/smart-contract-examples.git
    cd smart-contract-examples/ccip/cct/hardhat
    
  3. Install dependencies for the project:

    npm install
    
  4. Compile the project:

    npm run compile
    
  5. Encrypt your environment variables for higher security:
    The project uses @chainlink/env-enc to encrypt your environment variables at rest. Follow the steps below to configure your environment securely:

    1. Set an encryption password for your environment variables:

      npx env-enc set-pw
      
    2. Set up a .env.enc file with the necessary variables for Avalanche Fuji and Arbitrum Sepolia testnets. Use the following command to add the variables:

      npx env-enc set
      

      Variables to configure:

      • AVALANCHE_FUJI_RPC_URL: A URL for the Avalanche Fuji testnet. You can get a personal endpoint from services like Alchemy or Infura.
      • ARBITRUM_SEPOLIA_RPC_URL: A URL for the Arbitrum Sepolia testnet. You can get a personal endpoint from services like Alchemy or Infura.
      • PRIVATE_KEY: The private key for your testnet wallet. If you use MetaMask, you can follow this guide to export your private key. Note: This key is required for signing transactions like token transfers.
  6. Fund your EOA with native gas tokens:
    Make sure your EOA has enough native gas tokens on Avalanche Fuji to cover transaction fees. You can use the Chainlink faucets to get testnet tokens.

Tutorial

Review current rate limiter settings

Use the getPoolConfig task to fetch the current settings:

npx hardhat getPoolConfig --pooladdress <POOL_ADDRESS> --network <NETWORK_NAME>
  1. Check the current configuration for Avalanche Fuji: Replace <POOL_ADDRESS> with your token pool address and <NETWORK_NAME> with avalancheFuji.

    Example:

    npx hardhat getPoolConfig --pooladdress 0xa6aca9013b111228433da2aea186cb267d74fc0f --network avalancheFuji
    

    Expected output:

    $ npx hardhat getPoolConfig --pooladdress 0xa6aca9013b111228433da2aea186cb267d74fc0f --network avalancheFuji
    2024-10-23T08:56:57.681Z info: Fetching configuration for pool at address: 0xa6aca9013b111228433da2aea186cb267d74fc0f
    2024-10-23T08:56:58.716Z info:
    Configuration for Remote Chain: arbitrumSepolia
    2024-10-23T08:56:58.716Z info:   Allowed: true
    2024-10-23T08:56:58.716Z info:   Remote Pool Address: 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510
    2024-10-23T08:56:58.716Z info:   Remote Token Address: 0xABc3B3cEC3978d831e5585fc6cBCc11B5fEDA2fc
    2024-10-23T08:56:58.716Z info:   Outbound Rate Limiter:
    2024-10-23T08:56:58.716Z info:     Enabled: false
    2024-10-23T08:56:58.717Z info:     Capacity: 0
    2024-10-23T08:56:58.717Z info:     Rate: 0
    2024-10-23T08:56:58.717Z info:   Inbound Rate Limiter:
    2024-10-23T08:56:58.717Z info:     Enabled: false
    2024-10-23T08:56:58.717Z info:     Capacity: 0
    2024-10-23T08:56:58.717Z info:     Rate: 0
    

    In this example, the rate limiter settings are currently disabled for both outbound and inbound transfers.

  2. Check the current configuration for Arbitrum Sepolia: Replace <POOL_ADDRESS> with your token pool address and <NETWORK_NAME> with arbitrumSepolia.

    Example:

    npx hardhat getPoolConfig --pooladdress 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510 --network arbitrumSepolia
    

    Expected output:

    $ npx hardhat getPoolConfig --pooladdress 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510 --network arbitrumSepolia
    2024-10-23T09:02:19.487Z info: Fetching configuration for pool at address: 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510
    2024-10-23T09:02:20.175Z info:
    Configuration for Remote Chain: avalancheFuji
    2024-10-23T09:02:20.176Z info:   Allowed: true
    2024-10-23T09:02:20.176Z info:   Remote Pool Address: 0xA6Aca9013b111228433DA2AeA186cb267D74fc0f
    2024-10-23T09:02:20.176Z info:   Remote Token Address: 0xcB19276D94d82bD0AceB9FD960dF6c69E42EE1c6
    2024-10-23T09:02:20.176Z info:   Outbound Rate Limiter:
    2024-10-23T09:02:20.176Z info:     Enabled: false
    2024-10-23T09:02:20.176Z info:     Capacity: 0
    2024-10-23T09:02:20.176Z info:     Rate: 0
    2024-10-23T09:02:20.176Z info:   Inbound Rate Limiter:
    2024-10-23T09:02:20.176Z info:     Enabled: false
    2024-10-23T09:02:20.177Z info:     Capacity: 0
    2024-10-23T09:02:20.177Z info:     Rate: 0
    

    In this example, the rate limiter settings are currently disabled for both outbound and inbound transfers.

Update rate limiter settings

Use the updateRateLimiters task to update the rate limiter configurations for an existing chain connection in your token pool. This task specifically interacts with the setChainRateLimiterConfig function of the TokenPool contract, allowing you to adjust the rate limits without altering other configurations like remote pool addresses.

The updateRateLimiters task allows you to:

  • Enable or disable rate limiting for outbound or inbound transfers or both.
  • Set the capacity and rate for the rate limiters, controlling the flow of tokens.
  • Target a specific remote chain, updating rate limits for that chain only.

Below is an explanation of the parameters used during the rate limiter update process:

ParameterDescriptionDefaultRequired
pooladdressThe address of the token pool being configured.N/AYes
remotechainThe remote blockchain to which this pool is linked. Examples include avalancheFuji, arbitrumSepolia, baseSepolia, and sepolia.N/AYes
ratelimiterSpecifies which rate limiters to update: inbound, outbound, or both.bothNo
outboundratelimitenabledA flag indicating whether to enable outbound rate limits for cross-chain transfers (true or false).falseNo
outboundratelimitcapacityThe maximum number of tokens allowed in the bucket for outbound transfers (in wei). Note: Applicable if outbound rate limits are enabled.0No
outboundratelimitrateThe number of tokens per second that the bucket is refilled for outbound transfers (in wei). Note: Applicable if outbound rate limits are enabled.0No
inboundratelimitenabledA flag indicating whether to enable inbound rate limits for cross-chain transfers (true or false).falseNo
inboundratelimitcapacityThe maximum number of tokens allowed in the bucket for inbound transfers (in wei). Note: Applicable if inbound rate limits are enabled.0No
inboundratelimitrateThe number of tokens per second that the bucket is refilled for inbound transfers (in wei). Note: Applicable if inbound rate limits are enabled.0No
networkThe blockchain network where the local token pool is deployed. Examples include avalancheFuji, arbitrumSepolia, baseSepolia, and sepolia.N/AYes

Command syntax:

npx hardhat updateRateLimiters \
  --pooladdress <POOL_ADDRESS> \
  --remotechain <REMOTE_CHAIN> \
  --ratelimiter <inbound/outbound/both> \
  --outboundratelimitenabled <true/false> \
  --outboundratelimitcapacity <OUTBOUND_CAPACITY> \
  --outboundratelimitrate <OUTBOUND_RATE> \
  --inboundratelimitenabled <true/false> \
  --inboundratelimitcapacity <INBOUND_CAPACITY> \
  --inboundratelimitrate <INBOUND_RATE> \
  --network <NETWORK_NAME>

Example command:

Suppose you want to enable inbound and outbound rate limits for your token pool on Avalanche Fuji to control the number of tokens received or sent from/to Arbitrum Sepolia. We will use an existing token pool that interacts with an ERC20 token with 18 decimals:

  • Token Pool on Avalance Fuji:

    • Outbound Rate Limiter:

      • Enabled: true
      • Capacity: 10000000000000000000 wei (equivalent to 10 tokens, based on 18 decimals)
      • Rate: 100000000000000000 wei (equivalent to 0.1 token per second, based on 18 decimals)
      • Note:
        • Capacity / Rate = 10 / 0.1 = 100 seconds
        • It takes 100 seconds to replenish the bucket from 0 to full capacity.
    • Inbound Rate Limiter:

      • Enabled: true
      • Capacity: 20000000000000000000 wei (equivalent to 20 tokens, based on 18 decimals)
      • Rate: 100000000000000000 wei (equivalent to 0.1 tokens per second, based on 18 decimals)
      • Note:
        • Capacity / Rate = 20 / 0.1 = 200 seconds
        • It takes 200 seconds to replenish the bucket from 0 to full capacity.
  • Token Pool on Arbitrum Sepolia: Rate limits are the same as the Avalanche Fuji pool, but the inbound and outbound settings are swapped.

    • Outbound Rate Limiter:
      • Enabled: true
      • Capacity: 20000000000000000000 wei
      • Rate: 100000000000000000 wei
    • Inbound Rate Limiter:
      • Enabled: true
      • Capacity: 10000000000000000000 wei
      • Rate: 100000000000000000 wei
  1. Update rate limiter settings for the token pool on Avalanche Fuji: Replace <POOL_ADDRESS> with your token pool address.

    npx hardhat updateRateLimiters \
      --pooladdress <POOL_ADDRESS> \
      --remotechain arbitrumSepolia \
      --ratelimiter both \
      --outboundratelimitenabled true \
      --outboundratelimitcapacity 10000000000000000000 \
      --outboundratelimitrate 100000000000000000 \
      --inboundratelimitenabled true \
      --inboundratelimitcapacity 20000000000000000000 \
      --inboundratelimitrate 100000000000000000 \
      --network avalancheFuji
    

    Expected output:

    2024-10-24T07:21:37.747Z info: Current Rate Limiters for token pool: 0xa6aca9013b111228433da2aea186cb267d74fc0f
    2024-10-24T07:21:37.748Z info:   Outbound Rate Limiter:
    2024-10-24T07:21:37.748Z info:     Enabled: false
    2024-10-24T07:21:37.748Z info:     Capacity: 0
    2024-10-24T07:21:37.748Z info:     Rate: 0
    2024-10-24T07:21:37.748Z info:   Inbound Rate Limiter:
    2024-10-24T07:21:37.748Z info:     Enabled: false
    2024-10-24T07:21:37.748Z info:     Capacity: 0
    2024-10-24T07:21:37.748Z info:     Rate: 0
    2024-10-24T07:21:37.748Z info:
    2024-10-24T07:21:37.748Z info: ========== Updating Rate Limiters ==========
    2024-10-24T07:21:37.748Z info: New Outbound Rate Limiter:
    2024-10-24T07:21:37.748Z info:   Enabled: true
    2024-10-24T07:21:37.748Z info:   Capacity: 10000000000000000000
    2024-10-24T07:21:37.748Z info:   Rate: 100000000000000000
    2024-10-24T07:21:37.748Z info: New Inbound Rate Limiter:
    2024-10-24T07:21:37.749Z info:   Enabled: true
    2024-10-24T07:21:37.749Z info:   Capacity: 20000000000000000000
    2024-10-24T07:21:37.749Z info:   Rate: 100000000000000000
    2024-10-24T07:21:37.749Z info: Updating both rate limiters...
    2024-10-24T07:21:50.777Z info: Transaction hash: 0xcb704dd8d7cdfe318dacce062f9ea254f57f0a9fd7bc40a0a5cd1bc5460131d8
    2024-10-24T07:21:50.777Z info: Rate limiters updated successfully
    
  2. Update rate limiter settings for the token pool on Arbitrum Sepolia: Replace <POOL_ADDRESS> with your token pool address.

    npx hardhat updateRateLimiters \
      --pooladdress <POOL_ADDRESS> \
      --remotechain avalancheFuji \
      --ratelimiter both \
      --outboundratelimitenabled true \
      --outboundratelimitcapacity 20000000000000000000 \
      --outboundratelimitrate 100000000000000000 \
      --inboundratelimitenabled true \
      --inboundratelimitcapacity 10000000000000000000 \
      --inboundratelimitrate 100000000000000000 \
      --network arbitrumSepolia
    

    Expected output:

    2024-10-24T07:22:49.215Z info: Current Rate Limiters for token pool: 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510
    2024-10-24T07:22:49.215Z info:   Outbound Rate Limiter:
    2024-10-24T07:22:49.215Z info:     Enabled: false
    2024-10-24T07:22:49.216Z info:     Capacity: 0
    2024-10-24T07:22:49.216Z info:     Rate: 0
    2024-10-24T07:22:49.216Z info:   Inbound Rate Limiter:
    2024-10-24T07:22:49.216Z info:     Enabled: false
    2024-10-24T07:22:49.216Z info:     Capacity: 0
    2024-10-24T07:22:49.216Z info:     Rate: 0
    2024-10-24T07:22:49.216Z info:
    2024-10-24T07:22:49.216Z info: ========== Updating Rate Limiters ==========
    2024-10-24T07:22:49.216Z info: New Outbound Rate Limiter:
    2024-10-24T07:22:49.216Z info:   Enabled: true
    2024-10-24T07:22:49.217Z info:   Capacity: 20000000000000000000
    2024-10-24T07:22:49.217Z info:   Rate: 100000000000000000
    2024-10-24T07:22:49.217Z info: New Inbound Rate Limiter:
    2024-10-24T07:22:49.217Z info:   Enabled: true
    2024-10-24T07:22:49.217Z info:   Capacity: 10000000000000000000
    2024-10-24T07:22:49.217Z info:   Rate: 100000000000000000
    2024-10-24T07:22:49.217Z info: Updating both rate limiters...
    2024-10-24T07:22:52.231Z info: Transaction hash: 0xc8b9cdf43577157732089560896ebb1260d2de3697df6d3d9940a94164f04a72
    2024-10-24T07:22:52.231Z info: Rate limiters updated successfully
    

Verify the new rate limiter settings

After applying the new rate limiter settings, verify that they have been updated correctly.

Use the getPoolConfig task again:

  1. Verify the updated rate limiter settings for the token pool on Avalanche Fuji:

    npx hardhat getPoolConfig --pooladdress <POOL_ADDRESS> --network avalancheFuji
    

    Expected output:

     $ npx hardhat getPoolConfig --pooladdress 0xa6aca9013b111228433da2aea186cb267d74fc0f --network avalancheFuji
     2024-10-24T07:23:49.330Z info: Fetching configuration for pool at address: 0xa6aca9013b111228433da2aea186cb267d74fc0f
     2024-10-24T07:23:50.237Z info:
     Configuration for Remote Chain: arbitrumSepolia
     2024-10-24T07:23:50.237Z info:   Allowed: true
     2024-10-24T07:23:50.237Z info:   Remote Pool Address: 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510
     2024-10-24T07:23:50.237Z info:   Remote Token Address: 0xABc3B3cEC3978d831e5585fc6cBCc11B5fEDA2fc
     2024-10-24T07:23:50.237Z info:   Outbound Rate Limiter:
     2024-10-24T07:23:50.237Z info:     Enabled: true
     2024-10-24T07:23:50.237Z info:     Capacity: 10000000000000000000
     2024-10-24T07:23:50.237Z info:     Rate: 100000000000000000
     2024-10-24T07:23:50.237Z info:   Inbound Rate Limiter:
     2024-10-24T07:23:50.237Z info:     Enabled: true
     2024-10-24T07:23:50.237Z info:     Capacity: 20000000000000000000
     2024-10-24T07:23:50.237Z info:     Rate: 100000000000000000
    
  2. Verify the updated rate limiter settings for the token pool on Arbitrum Sepolia:

    npx hardhat getPoolConfig --pooladdress <POOL_ADDRESS> --network arbitrumSepolia
    

    Expected output:

    $ npx hardhat getPoolConfig --pooladdress 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510 --network arbitrumSepolia
    2024-10-24T07:25:10.471Z info: Fetching configuration for pool at address: 0xCA0D481BFb1877b16b9e810C81390C7Ba757a510
    2024-10-24T07:25:11.026Z info:
    Configuration for Remote Chain: avalancheFuji
    2024-10-24T07:25:11.026Z info:   Allowed: true
    2024-10-24T07:25:11.026Z info:   Remote Pool Address: 0xA6Aca9013b111228433DA2AeA186cb267D74fc0f
    2024-10-24T07:25:11.026Z info:   Remote Token Address: 0xcB19276D94d82bD0AceB9FD960dF6c69E42EE1c6
    2024-10-24T07:25:11.026Z info:   Outbound Rate Limiter:
    2024-10-24T07:25:11.026Z info:     Enabled: true
    2024-10-24T07:25:11.027Z info:     Capacity: 20000000000000000000
    2024-10-24T07:25:11.027Z info:     Rate: 100000000000000000
    2024-10-24T07:25:11.027Z info:   Inbound Rate Limiter:
    2024-10-24T07:25:11.027Z info:     Enabled: true
    2024-10-24T07:25:11.027Z info:     Capacity: 10000000000000000000
    2024-10-24T07:25:11.027Z info:     Rate: 100000000000000000
    

Test the rate limiter settings

To verify the rate limiter settings, initiate a cross-chain transfer between the token pools on Avalanche Fuji and Arbitrum Sepolia. The rate limiter configuration will control the flow of tokens between these pools.

Note: Ensure that your externally owned account (EOA) has a sufficient balance of ERC20 tokens on Avalanche Fuji to complete the transfer.

In the example below, we use a token pool at address 0xa6aca9013b111228433da2aea186cb267d74fc0f on Avalanche Fuji, which has burn and mint privileges for the token at address 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6. We will transfer this token from Avalanche Fuji to Arbitrum Sepolia. For your own test, substitute these addresses with the token pool and token addresses that you have deployed.

  1. Test Capacity: Because the outbound capacity set is 10000000000000000000 , let's transfer 10000000000000000001 tokens from Avalanche Fuji to Arbitrum Sepolia. This transfer should fail because the capacity is less than the number of tokens being transferred.

    Command:

    npx hardhat transferTokens --tokenaddress <TOKEN_ADDRESS> --amount 10000000000000000001 --destinationchain arbitrumSepolia --receiveraddress <RECEIVER_ADDRESS> --network avalancheFuji
    

    Expected output:

    2024-10-24T08:57:51.767Z info: Estimated fees: 33458908536126430
    2024-10-24T08:57:51.770Z info: Approving 10000000000000000001 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
    2024-10-24T08:58:04.211Z info: Approving 33458908536126430 LINK to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
    2024-10-24T08:58:33.908Z info: Transferring 10000000000000000001 of 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0x9d087fc03ae39b088326b67fa3c788236645b717 on chain arbitrumSepolia with 33458908536126430 of LINK as fees
    Simulation failed
    Decoded error from factory EVM2EVMOnRamp__factory: TokenMaxCapacityExceeded Result(3) [
      10000000000000000000n,
      10000000000000000001n,
      '0xcB19276D94d82bD0AceB9FD960dF6c69E42EE1c6'
    ]
    

    Notice in the logs that the transfer failed because the capacity was exceeded: TokenMaxCapacityExceeded.

  2. Test Rate: Now, let's transfer 10000000000000000000 tokens from Avalanche Fuji to Arbitrum Sepolia, which will empty the bucket. After this transfer, we will attempt to transfer another 10000000000000000000 tokens. This transfer will fail because it takes 100 seconds to replenish the bucket.

    1. First transfer (Successful):

      Command:

      npx hardhat transferTokens --tokenaddress <TOKEN_ADDRESS> --amount 10000000000000000000 --destinationchain arbitrumSepolia --receiveraddress <RECEIVER_ADDRESS> --network avalancheFuji
      

      Expected output:

      2024-10-24T09:06:16.187Z info: Estimated fees: 33361769518025675
      2024-10-24T09:06:16.189Z info: Approving 10000000000000000000 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-10-24T09:06:24.019Z info: Approving 33361769518025675 LINK to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-10-24T09:06:33.681Z info: Transferring 10000000000000000000 of 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0x9d087fc03ae39b088326b67fa3c788236645b717 on chain arbitrumSepolia with 33361769518025675 of LINK as fees
      2024-10-24T09:06:39.801Z info: Transaction hash: 0x0f4f5f407ca1fea121aaf663ade911f39954537dee2283267bffcf926b1c9876
      2024-10-24T09:06:40.024Z info: ✅ Transferred 10000000000000000000 of 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0x9d087fc03ae39b088326b67fa3c788236645b717 on chain arbitrumSepolia. Transaction hash: 0x0f4f5f407ca1fea121aaf663ade911f39954537dee2283267bffcf926b1c9876 - CCIP message id: 0x96e72555bd6210e2fe9073eb6d471deccad10f317acdcc2d72bdca48df1132d7
      2024-10-24T09:06:40.024Z info: Check status of message on https://ccip.chain.link/msg/0x96e72555bd6210e2fe9073eb6d471deccad10f317acdcc2d72bdca48df1132d7
      
    2. Second transfer (Failed):

      Command:

      npx hardhat transferTokens --tokenaddress <TOKEN_ADDRESS> --amount 10000000000000000000 --destinationchain arbitrumSepolia --receiveraddress <RECEIVER_ADDRESS> --network avalancheFuji
      

      Expected output:

      2024-10-24T09:07:01.768Z info: Estimated fees: 33361769518025675
      2024-10-24T09:07:01.771Z info: Approving 10000000000000000000 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-10-24T09:07:20.979Z info: Approving 33361769518025675 LINK to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-10-24T09:07:33.238Z info: Transferring 10000000000000000000 of 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0x9d087fc03ae39b088326b67fa3c788236645b717 on chain arbitrumSepolia with 33361769518025675 of LINK as fees
      Simulation failed
      Decoded error from factory EVM2EVMOnRamp__factory: TokenRateLimitReached Result(3) [
        43n,
        5700000000000000000n,
        '0xcB19276D94d82bD0AceB9FD960dF6c69E42EE1c6'
      ]
      

      Notice in the logs that the transfer failed because the rate limit was exceeded: TokenRateLimitReached.

Get the latest Chainlink content straight to your inbox.