Set Token Pool Rate Limits Using Foundry

Guide Versions

This guide is available in multiple versions. Choose the one that matches your needs.

In this tutorial you will:

  1. Review current standard rate limiter configurations for your deployed token pool.
  2. Update outbound and inbound standard rate limiter configurations.
  3. Verify the updated configurations.
  4. Test standard rate limiter enforcement with a cross-chain token transfer.
  5. Optionally configure and test rate limits for faster than finality transfers.

Before You Begin

1 Set up your development environment
  1. Install Node.js and npm:

    • Make sure you have Node.js v22.10.0 or above installed. If not, install Node.js v22.10.0 using their documentation.
    • npm is bundled with Node.js. If you can't run npm, reinstall/update Node.js from the official installer.
  2. Install/Update Chainlink CCIP-CLI. You can also find the GitHub repository here.

Terminal
npm install -g @chainlink/ccip-cli

Verify the installation by running the following command:

Terminal
ccip-cli --version
  1. Install Foundry: If you haven't already, follow the instructions in the Foundry documentation to install Foundry.
    Verify the installation by running the following command:
Terminal
forge --version
  1. Clone the repository and navigate to the project directory:
CCIP 2.0 template

Clone the CCIP 2.0 template for a smoother CCT setup.

Terminal
git clone https://github.com/smartcontractkit/docs-cct-foundry.git
cd docs-cct-foundry
  1. Create an encrypted Foundry keystore, if you haven't already:
Terminal
cast wallet import your_keystore_name --interactive
Foundry keystore list command output
  1. Create a .env file by copying the .env.example file, and fill in the required values:
Terminal
cp .env.example .env
.env
# Keystore name (created via `cast wallet import`)
KEYSTORE_NAME=your_keystore_name

# RPC URLs (add the ones you need)
ETHEREUM_SEPOLIA_RPC_URL=your_eth_sepolia_rpc
ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL=your_arbitrum_sepolia_rpc

# Etherscan API key (required only if you pass --verify to deployment scripts)
ETHERSCAN_API_KEY=your_etherscan_api_key
Complete List of Supported Chains

View the complete list of supported chains in the HelperConfig.s.sol file.

  1. To make sure your terminal has access to these variables, run the following command:
Terminal
source .env
  1. Build the project:
Terminal
npm install && forge build

Tutorial


1 Review Current Rate Limiter Configurations

Use the GetCurrentRateLimits.s.sol script to read the current rate limiter configuration state for a specific lane from your token pool. The script reports whether rate limiting is enabled, the maximum capacity (bucket size), the refill rate (tokens per second), and the current token count in each bucket.

GetCurrentRateLimits.s.sol

View the rate limiter query script on GitHub.

Env varRequiredDescription
DEST_CHAINYesRemote chain whose lane is being queried (e.g. ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1)
FAST_FINALITYNoSet to true to read the faster than finality bucket instead of the standard finality bucket (v2 pools only, default: false)
  1. Check the Ethereum Sepolia pool (lane to Arbitrum Sepolia):
Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  forge script \
  script/configure/rate-limiter/GetCurrentRateLimits.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL

Your output should look something like this:

Terminal
========================================
📊 Get Current Rate Limits
========================================
Chain:        Ethereum Sepolia
Remote Chain: Arbitrum Sepolia
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Action:       View rate limits
Bucket:       Standard finality
========================================

Pool Version: v2

Current Rate Limiter State:
Outbound Enabled:  false
Outbound Capacity: 0
Outbound Rate:     0
Outbound Tokens:   0
Inbound  Enabled:  false
Inbound  Capacity: 0
Inbound  Rate:     0
Inbound  Tokens:   0

========================================
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Token Pool:   https://sepolia.etherscan.io/address/0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
========================================
  1. Check the Arbitrum Sepolia pool (lane to Ethereum Sepolia):
Terminal
DEST_CHAIN=ETHEREUM_SEPOLIA \
  forge script \
  script/configure/rate-limiter/GetCurrentRateLimits.s.sol \
  --rpc-url $ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL

Your output should look something like this:

Terminal
========================================
📊 Get Current Rate Limits
========================================
Chain:        Arbitrum Sepolia
Remote Chain: Ethereum Sepolia
Token Pool:   0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
Action:       View rate limits
Bucket:       Standard finality
========================================

Pool Version: v2

Current Rate Limiter State:
Outbound Enabled:  false
Outbound Capacity: 0
Outbound Rate:     0
Outbound Tokens:   0
Inbound  Enabled:  false
Inbound  Capacity: 0
Inbound  Rate:     0
Inbound  Tokens:   0

========================================
Token Pool:   0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
Token Pool:   https://sepolia.arbiscan.io/address/0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
========================================
2 Update Rate Limiter Configurations

Use the UpdateRateLimiters.s.sol script to update rate limiter configurations for an existing lane in your token pool. The direction to update is inferred automatically from whichever OUTBOUND_* / INBOUND_* environment variables you set.

Only the directions you specify are updated; any direction you don't specify is left unchanged. Outbound and inbound are relative to the pool you're configuring:

  1. outbound is tokens leaving this pool toward DEST_CHAIN;
  2. inbound is tokens arriving from DEST_CHAIN into this pool.
UpdateRateLimiters.s.sol

View the rate limiter update script on GitHub.

Env varRequiredDescription
DEST_CHAINYesRemote chain whose lane is being updated
OUTBOUND_RATE_LIMIT_CAPACITYTo update outboundToken bucket capacity for outbound transfers
OUTBOUND_RATE_LIMIT_RATETo update outboundToken bucket refill rate (tokens/second) for outbound transfers
OUTBOUND_RATE_LIMIT_ENABLEDNoOverride isEnabled explicitly (true/false; defaults to true when CAPACITY or RATE are set)
INBOUND_RATE_LIMIT_CAPACITYTo update inboundToken bucket capacity for inbound transfers
INBOUND_RATE_LIMIT_RATETo update inboundToken bucket refill rate (tokens/second) for inbound transfers
INBOUND_RATE_LIMIT_ENABLEDNoOverride isEnabled explicitly (true/false; defaults to true when CAPACITY or RATE are set)
FAST_FINALITYNotrue to update the faster than finality bucket instead of the standard finality bucket (v2 only, default: false)

In this example, we configure an 18-decimal token with outbound capacity of 10 tokens (refilling at 0.1 tokens/sec) and inbound capacity of 11 tokens (refilling at 0.11 tokens/sec, a 10% buffer):

  1. Update the Ethereum Sepolia pool (lane to Arbitrum Sepolia):
Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  OUTBOUND_RATE_LIMIT_CAPACITY=10000000000000000000 \
  OUTBOUND_RATE_LIMIT_RATE=100000000000000000 \
  INBOUND_RATE_LIMIT_CAPACITY=11000000000000000000 \
  INBOUND_RATE_LIMIT_RATE=110000000000000000 \
  forge script \
  script/configure/rate-limiter/UpdateRateLimiters.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL \
  --account $KEYSTORE_NAME \
  --broadcast

Your output should look something like this:

Terminal
========================================
⚡️ Update Rate Limiters
========================================
Chain:        Ethereum Sepolia
Remote Chain: Arbitrum Sepolia
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Action:       Update rate limits
Direction:    Outbound + Inbound
Bucket:       Standard finality
========================================

Pool Version: v2 (setRateLimitConfig)

Current Rate Limiter State:
Outbound Enabled:  false
Outbound Capacity: 0
Outbound Rate:     0
Outbound Tokens:   0
Inbound  Enabled:  false
Inbound  Capacity: 0
Inbound  Rate:     0
Inbound  Tokens:   0

New Configuration:
Outbound Enabled:  true
Outbound Capacity: 10000000000000000000
Outbound Rate:     100000000000000000
Inbound Enabled:   true
Inbound Capacity:  11000000000000000000
Inbound Rate:      110000000000000000

========================================
✅ Rate limiter update complete on Ethereum Sepolia!
========================================
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Token Pool:   https://sepolia.etherscan.io/address/0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
========================================
  1. Update the Arbitrum Sepolia pool (lane to Ethereum Sepolia):
Terminal
DEST_CHAIN=ETHEREUM_SEPOLIA \
  OUTBOUND_RATE_LIMIT_CAPACITY=10000000000000000000 \
  OUTBOUND_RATE_LIMIT_RATE=100000000000000000 \
  INBOUND_RATE_LIMIT_CAPACITY=11000000000000000000 \
  INBOUND_RATE_LIMIT_RATE=110000000000000000 \
  forge script \
  script/configure/rate-limiter/UpdateRateLimiters.s.sol \
  --rpc-url $ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL \
  --account $KEYSTORE_NAME \
  --broadcast

Your output should look something like this:

Terminal
========================================
⚡️ Update Rate Limiters
========================================
Chain:        Arbitrum Sepolia
Remote Chain: Ethereum Sepolia
Token Pool:   0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
Action:       Update rate limits
Direction:    Outbound + Inbound
Bucket:       Standard finality
========================================

Pool Version: v2 (setRateLimitConfig)

Current Rate Limiter State:
Outbound Enabled:  false
Outbound Capacity: 0
Outbound Rate:     0
Outbound Tokens:   0
Inbound  Enabled:  false
Inbound  Capacity: 0
Inbound  Rate:     0
Inbound  Tokens:   0

New Configuration:
Outbound Enabled:  true
Outbound Capacity: 10000000000000000000
Outbound Rate:     100000000000000000
Inbound Enabled:   true
Inbound Capacity:  11000000000000000000
Inbound Rate:      110000000000000000

========================================
✅ Rate limiter update complete on Arbitrum Sepolia!
========================================
Token Pool:   0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
Token Pool:   https://sepolia.arbiscan.io/address/0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
========================================
  1. To Disable rate limiters for a lane, set OUTBOUND_RATE_LIMIT_ENABLED=false and INBOUND_RATE_LIMIT_ENABLED=false:
Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  OUTBOUND_RATE_LIMIT_ENABLED=false \
  INBOUND_RATE_LIMIT_ENABLED=false \
  forge script \
  script/configure/rate-limiter/UpdateRateLimiters.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL \
  --account $KEYSTORE_NAME \
  --broadcast
3 Verify the Updated Rate Limiter Configurations

Re-run the GetCurrentRateLimits.s.sol script on both chains to confirm the update was applied correctly.

  1. Verify the Ethereum Sepolia pool:
Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  forge script \
  script/configure/rate-limiter/GetCurrentRateLimits.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL

Your output should look something like this:

Terminal
========================================
📊 Get Current Rate Limits
========================================
Chain:        Ethereum Sepolia
Remote Chain: Arbitrum Sepolia
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Action:       View rate limits
Bucket:       Standard finality
========================================

Pool Version: v2

Current Rate Limiter State:
Outbound Enabled:  true
Outbound Capacity: 10000000000000000000
Outbound Rate:     100000000000000000
Outbound Tokens:   10000000000000000000
Inbound  Enabled:  true
Inbound  Capacity: 11000000000000000000
Inbound  Rate:     110000000000000000
Inbound  Tokens:   11000000000000000000

========================================
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Token Pool:   https://sepolia.etherscan.io/address/0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
========================================
  1. Verify the Arbitrum Sepolia pool:
Terminal
DEST_CHAIN=ETHEREUM_SEPOLIA \
  forge script \
  script/configure/rate-limiter/GetCurrentRateLimits.s.sol \
  --rpc-url $ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL

Your output should look something like this:

Terminal
========================================
📊 Get Current Rate Limits
========================================
Chain:        Arbitrum Sepolia
Remote Chain: Ethereum Sepolia
Token Pool:   0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
Action:       View rate limits
Bucket:       Standard finality
========================================

Pool Version: v2

Current Rate Limiter State:
Outbound Enabled:  true
Outbound Capacity: 10000000000000000000
Outbound Rate:     100000000000000000
Outbound Tokens:   10000000000000000000
Inbound  Enabled:  true
Inbound  Capacity: 11000000000000000000
Inbound  Rate:     110000000000000000
Inbound  Tokens:   11000000000000000000

========================================
Token Pool:   0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
Token Pool:   https://sepolia.arbiscan.io/address/0x7D2f4A6c8E1b3D5a9F0c2E4b6A8d1C3e5F7a9B2d
========================================
4 Test the Rate Limiter Configurations

Initiate a cross-chain transfer to verify that the rate limiter enforces the configured limits.

Before testing, export the router and token addresses for the source chain:

Terminal
export ETHEREUM_SEPOLIA_ROUTER=0x...
export ETHEREUM_SEPOLIA_TOKEN=0x...

Note:

  1. Add --fee-token LINK to pay CCIP fees in LINK. If you omit this flag, fees are paid in the native gas token.
  2. Use --receiver <address> for a custom recipient. If omitted on EVM-to-EVM, the receiver defaults to the sender.

Test 1: Exceed capacity

Transfer one wei more than the outbound capacity (10 tokens + 1 wei). This should fail because the requested amount exceeds the bucket size.

Terminal
ccip-cli send \
  --source ethereum-testnet-sepolia \
  --router $ETHEREUM_SEPOLIA_ROUTER \
  --dest ethereum-testnet-sepolia-arbitrum-1 \
  --transfer-tokens $ETHEREUM_SEPOLIA_TOKEN=10.000000000000000001 \
  --wallet foundry:$KEYSTORE_NAME \
  --rpc "$ETHEREUM_SEPOLIA_RPC_URL" \
  --rpc "$ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL"

Your output should look something like this:

Terminal
Fee: 130129888907619n = 0.000130129888907619 ETH
✔ Enter password for Foundry keystore 'PRIVATE_KEY'
error: Failed to call "ccipSend"
Error = execution reverted: TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress)
Revert.error = TokenMaxCapacityExceeded(uint256,uint256,address)
Revert.capacity = 10000000000000000000n
Revert.requested = 10000000000000000001n
Revert.tokenAddress = 0x9602399103Ff5F87587Ac5A28E1551A0bA0c6C0D

The transfer should fail with TokenMaxCapacityExceeded because the requested amount is larger than the bucket capacity.

Test 2: Drain the bucket, then hit the rate

  1. First transfer (succeeds: drains the outbound bucket):
Terminal
ccip-cli send \
  --source ethereum-testnet-sepolia \
  --router $ETHEREUM_SEPOLIA_ROUTER \
  --dest ethereum-testnet-sepolia-arbitrum-1 \
  --transfer-tokens $ETHEREUM_SEPOLIA_TOKEN=10 \
  --wallet foundry:$KEYSTORE_NAME \
  --rpc "$ETHEREUM_SEPOLIA_RPC_URL" \
  --rpc "$ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL"

Your output should look something like this:

Terminal
Fee: 130129888907619n = 0.000130129888907619 ETH
✔ Enter password for Foundry keystore 'PRIVATE_KEY'
🚀 Sending message to 0xE23Fc63F47F08F58B9d7448d4CCE0bCDcc96d7F3 @ ethereum-testnet-sepolia-arbitrum-1 , tx => 0x3dc40bea29f3e3fc93ff8fce0dda45fd7f55a07ace5089646e018874c8b6745e , messageId => 0x4a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293
CCIP Explorer: https://ccip.chain.link/msg/0x4a5b6c7d8e9f0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293
  1. Second transfer (fails: bucket has not refilled):

Run the same command immediately:

Terminal
ccip-cli send \
  --source ethereum-testnet-sepolia \
  --router $ETHEREUM_SEPOLIA_ROUTER \
  --dest ethereum-testnet-sepolia-arbitrum-1 \
  --transfer-tokens $ETHEREUM_SEPOLIA_TOKEN=10 \
  --wallet foundry:$KEYSTORE_NAME \
  --rpc "$ETHEREUM_SEPOLIA_RPC_URL" \
  --rpc "$ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL"

Your output should look something like this:

Terminal
Fee: 130129888907619n = 0.000130129888907619 ETH
✔ Enter password for Foundry keystore 'PRIVATE_KEY'
error: Failed to call "ccipSend"
Error = execution reverted: TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress)
Revert.error = TokenRateLimitReached(uint256,uint256,address)
Revert.minWaitInSeconds = 100n
Revert.available = 0n
Revert.tokenAddress = 0x9602399103Ff5F87587Ac5A28E1551A0bA0c6C0D

The transfer should fail with TokenRateLimitReached because the bucket has not yet refilled. With a capacity of 10 tokens and a rate of 0.1 tokens per second, it takes 100 seconds to fully replenish.

5 (Optional) Configure Rate Limits for Faster Than Finality Transfers

This section applies to TokenPool v2.0 or later only. CCIP 2.0 token pools maintain two independent rate limiter buckets per lane:

  • Standard finality bucket: Used for standard finality transfers (finality=finalized, which is the default when you omit --extra finality=...). This is the bucket you configured in the previous steps.
  • Faster Than Finality bucket: Used for transfers that request faster than finality with finality=<N> (numeric block depth). If this bucket is disabled for a direction, faster than finality transfers for that direction fall back to the standard bucket (enforced on-chain by the token pool).

Check the pool's allowed finality config

Use GetFinalityConfig.s.sol to confirm which faster than finality modes the pool currently allows.

GetFinalityConfig.s.sol

View the finality config query script on GitHub.

  1. Check the Ethereum Sepolia pool:
Terminal
forge script \
  script/configure/finality-config/GetFinalityConfig.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL

Your output should look something like this:

Terminal
========================================
⏱️  Get Finality Config
========================================
Chain:        Ethereum Sepolia
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Action:       View finality config
========================================

Allowed Finality Config (raw): 0x00000000

Mode: WAIT_FOR_FINALITY (default)
Full finality is required. Faster Than Finality transfers are disabled.

========================================
Token Pool:   https://sepolia.etherscan.io/address/0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
========================================

BLOCK_DEPTH (32) sets a minimum for numeric requests: --extra finality=32 (or higher) is allowed; --extra finality=10 reverts with InvalidRequestedFinality(requestedFinality, allowedFinality).

  1. Check the Arbitrum Sepolia pool:
Terminal
forge script \
  script/configure/finality-config/GetFinalityConfig.s.sol \
  --rpc-url $ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL

Approach 1: Allow Faster Than Finality and Configure the Faster Than Finality Bucket in One Command

Use SetFinalityConfig.s.sol with the rate limit environment variables to allow faster than finality and configure the faster than finality bucket in a single broadcast.

SetFinalityConfig.s.sol

View the finality config update script on GitHub.

This example configures the Ethereum Sepolia pool for transfers to Arbitrum Sepolia. To configure the reverse direction, run the same script on Arbitrum Sepolia with DEST_CHAIN=ETHEREUM_SEPOLIA.

Terminal
BLOCK_DEPTH=32 \
  DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  OUTBOUND_RATE_LIMIT_CAPACITY=5000000000000000000 \
  OUTBOUND_RATE_LIMIT_RATE=50000000000000000 \
  INBOUND_RATE_LIMIT_CAPACITY=5500000000000000000 \
  INBOUND_RATE_LIMIT_RATE=55000000000000000 \
  forge script \
  script/configure/finality-config/SetFinalityConfig.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL \
  --account $KEYSTORE_NAME \
  --broadcast

Your output should look something like this:

Terminal
========================================
⏱️  Set Finality Config
========================================
Chain:        Ethereum Sepolia
Remote Chain: Arbitrum Sepolia
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Action:       Set finality config
========================================

  Current Finality Config:      0x00000000
  New Finality Config:          0x00000020
  Mode:                         BLOCK_DEPTH (32 blocks)

[Step 1] Setting finality config on Ethereum Sepolia
✅ Finality config set successfully!

[Step 2] Updating rate limits (faster than finality bucket) on Ethereum Sepolia -> Arbitrum Sepolia
New Configuration:
  Outbound Enabled:  true
  Outbound Capacity: 5000000000000000000
  Outbound Rate:     50000000000000000
  Inbound Enabled:   true
  Inbound Capacity:  5500000000000000000
  Inbound Rate:      55000000000000000

✅ Rate limits updated successfully!

========================================
✅ Configuration Complete on Ethereum Sepolia!
========================================
Token Pool:      0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Finality Config: 0x00000020
Mode:            BLOCK_DEPTH (32 blocks)
Token Pool:      https://sepolia.etherscan.io/address/0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
========================================

Approach 2: Update the Faster Than Finality Bucket Directly

If the pool already allows faster than finality, you can update the faster than finality bucket directly using UpdateRateLimiters.s.sol with FAST_FINALITY=true:

Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  FAST_FINALITY=true \
  OUTBOUND_RATE_LIMIT_CAPACITY=5000000000000000000 \
  OUTBOUND_RATE_LIMIT_RATE=50000000000000000 \
  INBOUND_RATE_LIMIT_CAPACITY=5500000000000000000 \
  INBOUND_RATE_LIMIT_RATE=55000000000000000 \
  forge script \
  script/configure/rate-limiter/UpdateRateLimiters.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL \
  --account $KEYSTORE_NAME \
  --broadcast

Verify the faster than finality bucket

Pass FAST_FINALITY=true to GetCurrentRateLimits.s.sol. Each direction is shown independently: the faster than finality bucket for directions where it is enabled, and the standard bucket fallback for directions where it is not.

Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  FAST_FINALITY=true \
  forge script \
  script/configure/rate-limiter/GetCurrentRateLimits.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL

Your output should look something like this:

Terminal
========================================
📊 Get Current Rate Limits
========================================
Chain:        Ethereum Sepolia
Remote Chain: Arbitrum Sepolia
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Action:       View rate limits
Bucket:       Custom finality (standard finality fallback per direction if not enabled)
========================================

Pool Version: v2

Outbound [custom finality]:
Enabled:  true
Capacity: 5000000000000000000
Rate:     50000000000000000
Tokens:   5000000000000000000
Inbound [custom finality]:
Enabled:  true
Capacity: 5500000000000000000
Rate:     55000000000000000
Tokens:   5500000000000000000

========================================
Token Pool:   0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
Token Pool:   https://sepolia.etherscan.io/address/0x147D4625b71f58D7CFA54BB705DC7741B94e0CE9
========================================

Test a faster than finality transfer

After the pool allows faster than finality and the faster than finality bucket is configured, send a small transfer with numeric block depth (for example --extra finality=32):

Terminal
ccip-cli send \
  --source ethereum-testnet-sepolia \
  --router $ETHEREUM_SEPOLIA_ROUTER \
  --dest ethereum-testnet-sepolia-arbitrum-1 \
  --transfer-tokens $ETHEREUM_SEPOLIA_TOKEN=1 \
  --receiver 0xYourReceiverAddress \
  --extra finality=32 \
  --wallet foundry:$KEYSTORE_NAME \
  --rpc "$ETHEREUM_SEPOLIA_RPC_URL" \
  --rpc "$ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1_RPC_URL"

Your output should look something like this:

Terminal
Fee: 130129888907619n = 0.000130129888907619 ETH
✔ Enter password for Foundry keystore 'PRIVATE_KEY'
🚀 Sending message to 0xE23Fc63F47F08F58B9d7448d4CCE0bCDcc96d7F3 @ ethereum-testnet-sepolia-arbitrum-1 , tx => 0x5f1c2d3e4b5a69788796a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e , messageId => 0x3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2c4d
CCIP Explorer: https://ccip.chain.link/msg/0x3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2c4d

Re-run GetCurrentRateLimits.s.sol with FAST_FINALITY=true to confirm the outbound faster than finality bucket changed after the transfer:

Terminal
DEST_CHAIN=ETHEREUM_TESTNET_SEPOLIA_ARBITRUM_1 \
  FAST_FINALITY=true \
  forge script \
  script/configure/rate-limiter/GetCurrentRateLimits.s.sol \
  --rpc-url $ETHEREUM_SEPOLIA_RPC_URL

What's next

Get the latest Chainlink content straight to your inbox.