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 getCurrentRateLimits task to fetch the current rate limiter states for a specific chain from a token pool. This task provides detailed information about both inbound and outbound rate limits, including:

  • Whether rate limiting is enabled
  • The maximum capacity (bucket size)
  • The refill rate (tokens per second)
  • Current token amount in the bucket
  • Last update timestamp

Below is an explanation of the parameters used:

ParameterDescriptionDefaultRequired
pooladdressThe address of the token pool to query.N/AYes
remotechainThe remote blockchain to check rate limits for (e.g., arbitrumSepolia for Fuji pool).N/AYes
networkThe blockchain on which to execute the query (e.g., avalancheFuji, arbitrumSepolia).N/AYes
  1. Get rate limiter settings for the token pool on Avalanche Fuji:

    npx hardhat getCurrentRateLimits \
      --pooladdress 0x1aFafBAd2528747cf464F62e9e57238FDE8f2BA9 \
      --remotechain arbitrumSepolia \
      --network avalancheFuji
    

    Example output:

    2024-12-02T22:20:25.611Z info:
    Rate Limiter States for Chain: arbitrumSepolia
    2024-12-02T22:20:25.613Z info: Pool Address: 0x1aFafBAd2528747cf464F62e9e57238FDE8f2BA9
    2024-12-02T22:20:25.613Z info: Chain Selector: 3478487238524512106
    2024-12-02T22:20:25.613Z info:
    Outbound Rate Limiter:
    2024-12-02T22:20:25.613Z info:   Enabled: true
    2024-12-02T22:20:25.613Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:20:25.613Z info:   Rate: 100000000000000000000
    2024-12-02T22:20:25.613Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:20:25.614Z info:   Last Updated: 1733178019
    2024-12-02T22:20:25.614Z info:
    Inbound Rate Limiter:
    2024-12-02T22:20:25.614Z info:   Enabled: true
    2024-12-02T22:20:25.614Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:20:25.614Z info:   Rate: 100000000000000000000
    2024-12-02T22:20:25.614Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:20:25.614Z info:   Last Updated: 1733178019
    
  2. Get rate limiter settings for the token pool on Arbitrum Sepolia:

    npx hardhat getCurrentRateLimits \
      --pooladdress 0x86F2521dB26AAEdDE5fe11cFA69EE5EfBeBc4E65 \
      --remotechain avalancheFuji \
      --network arbitrumSepolia
    

    Example output:

    2024-12-02T22:23:31.366Z info:
    Rate Limiter States for Chain: avalancheFuji
    2024-12-02T22:23:31.367Z info: Pool Address: 0x86F2521dB26AAEdDE5fe11cFA69EE5EfBeBc4E65
    2024-12-02T22:23:31.367Z info: Chain Selector: 14767482510784806043
    2024-12-02T22:23:31.367Z info:
    Outbound Rate Limiter:
    2024-12-02T22:23:31.367Z info:   Enabled: true
    2024-12-02T22:23:31.367Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Rate: 100000000000000000000
    2024-12-02T22:23:31.367Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Last Updated: 1733178210
    2024-12-02T22:23:31.367Z info:
    Inbound Rate Limiter:
    2024-12-02T22:23:31.367Z info:   Enabled: true
    2024-12-02T22:23:31.367Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Rate: 100000000000000000000
    2024-12-02T22:23:31.368Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:23:31.368Z info:   Last Updated: 1733178210
    

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 0x1aFafBAd2528747cf464F62e9e57238FDE8f2BA9 \
      --remotechain arbitrumSepolia \
      --ratelimiter both \
      --outboundratelimitenabled true \
      --outboundratelimitcapacity 1000000000000000000000 \
      --outboundratelimitrate 100000000000000000000 \
      --inboundratelimitenabled true \
      --inboundratelimitcapacity 1000000000000000000000 \
      --inboundratelimitrate 100000000000000000000 \
      --network avalancheFuji
    

    Expected output:

    2024-12-02T22:13:45.125Z info: Current Rate Limiters for token pool: 0x1aFafBAd2528747cf464F62e9e57238FDE8f2BA9
    2024-12-02T22:13:45.125Z info:   Outbound Rate Limiter:
    2024-12-02T22:13:45.125Z info:     Enabled: false
    2024-12-02T22:13:45.126Z info:     Capacity: 0
    2024-12-02T22:13:45.126Z info:     Rate: 0
    2024-12-02T22:13:45.126Z info:   Inbound Rate Limiter:
    2024-12-02T22:13:45.126Z info:     Enabled: false
    2024-12-02T22:13:45.126Z info:     Capacity: 0
    2024-12-02T22:13:45.126Z info:     Rate: 0
    2024-12-02T22:13:45.126Z info:
    2024-12-02T22:13:45.126Z info: ========== Updating Rate Limiters ==========
    2024-12-02T22:13:45.126Z info: New Outbound Rate Limiter:
    2024-12-02T22:13:45.126Z info:   Enabled: true
    2024-12-02T22:13:45.127Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:13:45.127Z info:   Rate: 100000000000000000000
    2024-12-02T22:13:45.127Z info: New Inbound Rate Limiter:
    2024-12-02T22:13:45.127Z info:   Enabled: true
    2024-12-02T22:13:45.127Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:13:45.127Z info:   Rate: 100000000000000000000
    2024-12-02T22:13:45.127Z info: Updating both rate limiters...
    2024-12-02T22:13:50.719Z info: Transaction hash: 0x7e5766b72467a7196aebb621403df1ff30de091ace82f028790fd6e3cfc9a64d
    2024-12-02T22:13:50.719Z 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 0x86F2521dB26AAEdDE5fe11cFA69EE5EfBeBc4E65 \
      --remotechain avalancheFuji \
      --ratelimiter both \
      --outboundratelimitenabled true \
      --outboundratelimitcapacity 1000000000000000000000 \
      --outboundratelimitrate 100000000000000000000 \
      --inboundratelimitenabled true \
      --inboundratelimitcapacity 1000000000000000000000 \
      --inboundratelimitrate 100000000000000000000 \
      --network arbitrumSepolia
    

    Expected output:

    2024-12-02T22:23:31.366Z info:
    Rate Limiter States for Chain: avalancheFuji
    2024-12-02T22:23:31.367Z info: Pool Address: 0x86F2521dB26AAEdDE5fe11cFA69EE5EfBeBc4E65
    2024-12-02T22:23:31.367Z info: Chain Selector: 14767482510784806043
    2024-12-02T22:23:31.367Z info:
    Outbound Rate Limiter:
    2024-12-02T22:23:31.367Z info:   Enabled: true
    2024-12-02T22:23:31.367Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Rate: 100000000000000000000
    2024-12-02T22:23:31.367Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Last Updated: 1733178210
    2024-12-02T22:23:31.367Z info:
    Inbound Rate Limiter:
    2024-12-02T22:23:31.367Z info:   Enabled: true
    2024-12-02T22:23:31.367Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Rate: 100000000000000000000
    2024-12-02T22:23:31.368Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:23:31.368Z info:   Last Updated: 1733178210
    

Verify the new rate limiter settings

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

  • Use the getCurrentRateLimits task to verify the updated settings:
  1. Verify the updated rate limiter settings for the token pool on Avalanche Fuji:

    npx hardhat getCurrentRateLimits \
      --pooladdress 0x1aFafBAd2528747cf464F62e9e57238FDE8f2BA9 \
      --remotechain arbitrumSepolia \
      --network avalancheFuji
    

    Example output:

    2024-12-02T22:20:25.611Z info:
    Rate Limiter States for Chain: arbitrumSepolia
    2024-12-02T22:20:25.613Z info: Pool Address: 0x1aFafBAd2528747cf464F62e9e57238FDE8f2BA9
    2024-12-02T22:20:25.613Z info: Chain Selector: 3478487238524512106
    2024-12-02T22:20:25.613Z info:
    Outbound Rate Limiter:
    2024-12-02T22:20:25.613Z info:   Enabled: true
    2024-12-02T22:20:25.613Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:20:25.613Z info:   Rate: 100000000000000000000
    2024-12-02T22:20:25.613Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:20:25.614Z info:   Last Updated: 1733178019
    2024-12-02T22:20:25.614Z info:
    Inbound Rate Limiter:
    2024-12-02T22:20:25.614Z info:   Enabled: true
    2024-12-02T22:20:25.614Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:20:25.614Z info:   Rate: 100000000000000000000
    2024-12-02T22:20:25.614Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:20:25.614Z info:   Last Updated: 1733178019
    
  2. Verify the updated rate limiter settings for the token pool on Arbitrum Sepolia:

    npx hardhat getCurrentRateLimits \
      --pooladdress 0x86F2521dB26AAEdDE5fe11cFA69EE5EfBeBc4E65 \
      --remotechain avalancheFuji \
      --network arbitrumSepolia
    

    Example output:

    2024-12-02T22:23:31.366Z info:
    Rate Limiter States for Chain: avalancheFuji
    2024-12-02T22:23:31.367Z info: Pool Address: 0x86F2521dB26AAEdDE5fe11cFA69EE5EfBeBc4E65
    2024-12-02T22:23:31.367Z info: Chain Selector: 14767482510784806043
    2024-12-02T22:23:31.367Z info:
    Outbound Rate Limiter:
    2024-12-02T22:23:31.367Z info:   Enabled: true
    2024-12-02T22:23:31.367Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Rate: 100000000000000000000
    2024-12-02T22:23:31.367Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Last Updated: 1733178210
    2024-12-02T22:23:31.367Z info:
    Inbound Rate Limiter:
    2024-12-02T22:23:31.367Z info:   Enabled: true
    2024-12-02T22:23:31.367Z info:   Capacity: 1000000000000000000000
    2024-12-02T22:23:31.367Z info:   Rate: 100000000000000000000
    2024-12-02T22:23:31.368Z info:   Tokens: 1000000000000000000000
    2024-12-02T22:23:31.368Z info:   Last Updated: 1733178210
    

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 1000000000000000000000 , let's transfer 1000000000000000000001 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 0x16F6b0f41b9217857551e29F38F99975a2fc9add --amount 1000000000000000000001 --destinationchain arbitrumSepolia --receiveraddress 0x9d087fC03ae39b088326b67fA3C788236645b717 --network avalancheFuji
    

    Expected output:

    2024-12-02T22:26:36.381Z info: Estimated fees: 17636974687679829
    2024-12-02T22:26:36.384Z info: Approving 1000000000000000000001 0x16F6b0f41b9217857551e29F38F99975a2fc9add to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
    2024-12-02T22:26:40.736Z info: Approving 17636974687679829 LINK to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
    2024-12-02T22:26:43.809Z info: Transferring 1000000000000000000001 of 0x16F6b0f41b9217857551e29F38F99975a2fc9add to 0x9d087fC03ae39b088326b67fA3C788236645b717 on chain arbitrumSepolia with 17636974687679829 of LINK as fees
    Simulation failed
    Decoded error from factory EVM2EVMOnRamp__factory: TokenMaxCapacityExceeded Result(3) [
      1000000000000000000000n,
      1000000000000000000001n,
       '0x16F6b0f41b9217857551e29F38F99975a2fc9add'
    ]
    

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

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

    1. First transfer (Successful):

      Command:

      npx hardhat transferTokens --tokenaddress 0x16F6b0f41b9217857551e29F38F99975a2fc9add --amount 1000000000000000000000 --destinationchain arbitrumSepolia --receiveraddress 0x9d087fC03ae39b088326b67fA3C788236645b717 --network avalancheFuji
      

      Expected output:

      2024-12-02T22:27:48.952Z info: Estimated fees: 17636974687679829
      2024-12-02T22:27:48.956Z info: Approving 1000000000000000000000 0x16F6b0f41b9217857551e29F38F99975a2fc9add to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-12-02T22:28:20.083Z info: Approving 17636974687679829 LINK to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-12-02T22:28:27.080Z info: Transferring 1000000000000000000000 of 0x16F6b0f41b9217857551e29F38F99975a2fc9add to 0x9d087fC03ae39b088326b67fA3C788236645b717 on chain arbitrumSepolia with 17636974687679829 of LINK as fees
      2024-12-02T22:28:50.258Z info: Transaction hash: 0x24e263a48f6fb39e9e2f7918acccc5ce5fdf93f37b238d7e032f45441611af67
      2024-12-02T22:28:50.286Z info: Message dispatched. Message id: 0xd9b3e7400a3002b71d4b63599e1ca98136f7110af8f7fadd6876f36bbb3bbac7
      2024-12-02T22:28:50.286Z info: ✅ Transferred 1000000000000000000000 of 0x16F6b0f41b9217857551e29F38F99975a2fc9add to 0x9d087fC03ae39b088326b67fA3C788236645b717 on chain arbitrumSepolia. Transaction hash: 0x24e263a48f6fb39e9e2f7918acccc5ce5fdf93f37b238d7e032f45441611af67 - CCIP message id: 0xd9b3e7400a3002b71d4b63599e1ca98136f7110af8f7fadd6876f36bbb3bbac7
      2024-12-02T22:28:50.286Z info: Check status of message on https://ccip.chain.link/msg/0xd9b3e7400a3002b71d4b63599e1ca98136f7110af8f7fadd6876f36bbb3bbac7
      
    2. Second transfer (Failed):

      Command:

      npx hardhat transferTokens --tokenaddress 0x16F6b0f41b9217857551e29F38F99975a2fc9add --amount 1000000000000000000000 --destinationchain arbitrumSepolia --receiveraddress 0x9d087fC03ae39b088326b67fA3C788236645b717 --network avalancheFuji
      

      Expected output:

      2024-12-02T22:29:11.768Z info: Estimated fees: 33361769518025675
      2024-12-02T22:29:11.771Z info: Approving 1000000000000000000000 0xcb19276d94d82bd0aceb9fd960df6c69e42ee1c6 to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-12-02T22:29:30.979Z info: Approving 33361769518025675 LINK to 0xF694E193200268f9a4868e4Aa017A0118C9a8177
      2024-12-02T22:29:43.238Z info: Transferring 1000000000000000000000 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,
        57000000000000000000n,
        '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.