For AI agents: use llms.txt as the documentation index. Retrieve the smallest relevant .md page first.

Cross-Chain Token Standard - Token Pools (EVM)

Learn about standard and custom token pool contracts for facilitating CCT transfers on EVM chains. This page covers common requirements like mandatory functions, gas limit considerations, and token decimal handling.

Common Requirements

All token pools, whether standard or custom, must adhere to the following guidelines:

Function Requirements

When CCIP interacts with your token pools, it expects the presence of the following functions:

  1. Sending Tokens (Source):

    • This must include the following function: lockOrBurn(Pool.LockOrBurnInV1 calldata lockOrBurnIn) external returns (Pool.LockOrBurnOutV1 memory).
    • This function locks or burns tokens depending on the implementation of the token pool.
    • See implementation details in TokenPool.lockOrBurn.
  2. Receiving Tokens (Destination):

    • This must include the following function: releaseOrMint(Pool.ReleaseOrMintInV1 calldata releaseOrMintIn) external returns (Pool.ReleaseOrMintOutV1 memory).
    • This function releases or mints tokens depending on the implementation of the token pool.
    • See implementation details in TokenPool.releaseOrMint.

Gas Requirements

On the destination blockchain, the CCIP OffRamp contract performs three key calls:

  1. balanceOf before minting/releasing tokens: To check the token balance of the receiver before the minting or releasing operation.

  2. releaseOrMint to mint or release tokens: To execute the minting or releasing of tokens on the destination blockchain.

  3. balanceOf after minting/releasing tokens: To verify the token balance of the receiver after the operation is complete.

Default Gas Limits

  • The combined execution of these three calls should not exceed the default gas limit of 90,000 gas.
  • To verify this cost, it is recommended to perform a token transfer on testnet.

Handling Execution Failures

  • If the default gas limit of 90,000 gas is exceeded and a custom limit has not been configured, the CCIP execution on the destination blockchain will fail.
  • In such cases, manual intervention by the user will be required to execute the transaction. For more information, see the manual execution page. - The resulting transaction can be inspected for the amount of gas that was used.

Requesting Gas Limit Adjustments

  • If the combined execution requires consistently more than 90,000 gas on the destination blockchain, you should contact Chainlink Labs to update an internal CCIP parameter to avoid execution failure.
  • It is highly recommended to design your token pool to stay within the 90,000 gas limit whenever possible to ensure you enabled your tokens in CCIP without Chainlink Labs intervention.

Token Decimal Handling

When you deploy the same token on multiple blockchains, you set decimals independently on each chain. Those settings determine how CCIP converts amounts during transfers. Use matching decimals on every chain when you can. If decimals differ between chains, read the examples and notes below and account for effects on token supply and on Token Pool Rate Limits; enforcement and scaling depend on your TokenPool contract version.

Precision Loss Examples

For example:

  • On Ethereum: you configure 18 decimals (0.123456789123456789)
  • On Polygon: you configure 9 decimals (0.123456789)

When tokens move between chains with different decimals, CCIP converts values automatically and rounds to match the destination chain’s precision.

Impact Scenario

Consider a token deployed across three blockchains:

  • Blockchain A: 18 decimals
  • Blockchain B: 9 decimals
  • Blockchain C: 18 decimals
ScenarioTransfer PathExampleImpact
High → Low ❌A → B• Send: 1.123456789123456789
• Receive: 1.123456789
Precision is lost when converting to the destination chain’s decimals. The difference (0.000000000123456789) is not represented on the destination chain.
• Burn/mint: not included in the minted amount
• Lock/release: remains in the source pool
Low → High ✅B → A• Send: 1.123456789
• Receive: 1.123456789000000000
No precision loss
Equal ✅A → C• Send: 1.123456789123456789
• Receive: 1.123456789123456789
No precision loss

Token Pool Version Considerations

TokenPool contracts in v1.6.0 and higher correctly handle mixed-decimal tokens and enforce Token Pool Rate Limits (TPRLs) across chains without additional configuration.

Use the latest TokenPool (v1.6.1)

v1.5.1

Token pools in v1.5.1 introduce support for tokens with different decimal places across blockchains. However, INBOUND Token Pool Rate Limits (TPRLs) are not properly enforced when decimals differ between chains (OUTBOUND TPRLs are properly enforced). If you use mixed-decimal tokens in this version, you must manually account for how decimals affect TPRL configuration—and even then, enforcement may be significantly higher or lower than intended. This includes both capacity and refill rate.

Token Pool Rate Limits (TPRL)

When configuring TPRLs for mixed-decimal tokens, you must base calculations on the remote token decimals (the source chain). Using local decimals when they differ will incorrectly scale the configured limits.

Definitions:

  • Remote decimals: token decimals on the destination chain (where tokens are being transferred from)
  • Local decimals: token decimals on the source chain (where tokens are being transferred to)

Inbound capacity rule

  • If remote decimals ≠ local decimals
    capacity = whole_tokens × 10^remote_decimals

  • If remote decimals = local decimals
    capacity = whole_tokens × 10^local_decimals

Example (Solana → Ethereum)

  • Solana: 9 decimals
  • Ethereum: 18 decimals
  • Desired inbound capacity: 50,000 tokens

50_000 × 10^9 = 50_000_000_000_000

If you instead use local decimals (10^18), you will misconfigure the limit. This can cause rate limits to be higher or lower than intended.

v1.5.0

Token pools on v1.5.0 do not support tokens with different decimal places across blockchains. If you deploy tokens with different decimals across chains in this version, transfers will produce inconsistent token amounts.

Transfer PathExampleImpact
12 → 6Send 1.0 (= 10¹² base units) → Receive 1,000,000Artificial increase
6 → 12Send 1.0 (= 10⁶ base units) → Receive 0.000001Significant loss

Upgrade to v1.6.0 or higher for correct handling of mixed-decimal tokens.

Best Practices

  • Use the same token decimals across chains unless you have a clear reason not to
  • If you choose different decimals, account for rounding in both directions
  • Test transfers between all chain pairs before production
  • Monitor lock/release pools for accumulation over time
  • Add UI warnings for transfers that may lose precision

Standard Token Pools

Depending on your use case (token handling mechanism), you need to deploy the appropriate token pool type for each blockchain you want to support. Chainlink provides a set of token pool contracts that you can use to deploy your token pools in minutes. These token pools are fully audited and ready for deployment on your blockchains. You can find the token pool contracts in the Chainlink GitHub repository. For most use cases, you should use either:

  • BurnMintTokenPool: This token pool is used to burn or mint tokens. You can read the API reference here.
  • BurnFromMintTokenPool: This is a variant of the BurnMintTokenPool that uses the burnFrom(from, amount) function to burn tokens from a specified account. You can read the API reference here. Note: If your token supports the standard burn function, you should typically use the BurnMintTokenPool instead of BurnFromMintTokenPool.
  • LockReleaseTokenPool: This token pool is used to lock or release tokens. You can read the API reference here.

Note: Both token pools inherit from the same base TokenPool contract, which provides all the common functions necessary for a token pool. For example, it includes the applyChainUpdates function, which is used to configure the token pool. You can read the API reference here.

Custom Token Pools

Guidelines for Custom Token Pools

If the standard token pools do not meet your requirements, you have the option to build a custom TokenPool. However, it is essential to adhere to the following guidelines:

  • Your custom token pool must inherit from the appropriate base token pool contract depending on your token handling mechanism:
    • Burn and Mint: Your custom token pool should inherit from BurnMintTokenPoolAbstract. Use this base contract if your custom pool involves burning tokens on the source chain and minting them on the destination chain.
    • Lock and Release: Your custom token pool can either inherit from TokenPool and implement the ILiquidityContainer interface, or directly inherit from LockReleaseTokenPool and reimplement lockOrBurn and releaseOrMint functions as needed. This setup is appropriate when your pool involves locking tokens on the source blockchain and releasing them on the destination blockchain.
  • Your custom TokenPool must implement the mandatory functions for both the source and destination blockchains. (Refer to the Common Requirements section for more details.)

Use Cases for Custom Token Pools

Here are some examples of use cases that may require custom token pools:

Tokens with rebasing or fee-on-transfer mechanisms

Use Case

Rebasing tokens are a unique type of token that adjusts its supply in response to specific parameters or events (e.g., price or transfer tax). These tokens require custom logic to handle rebasing events during cross-chain transfers.

Solution
  • Source Blockchain: When initiating a cross-chain transfer of rebasing tokens, the TokenPool on the source blockchain should lock or burn the underlying shares rather than a fixed amount of tokens. This ensures that the value is consistently represented, regardless of changes in supply. The number of shares being locked or burned is recorded in the destPoolDataand passed to the destination blockchain via CCIP.
  • Destination Blockchain: On the destination blockchain, the TokenPool should accurately convert the number of underlying shares to tokens using the current share-to-token ratio. The calculated token amount should then be minted on the destination blockchain and returned in the destinationAmount field of the ReleaseOrMintOutV1 struct, which is returned by the releaseOrMint function.

Tokens with different decimals across blockchains

For v1.5.0 pools:

Use Case

Some tokens have different decimal values across various blockchains.

Solution
  • Source Blockchain: During the lock or burn process on the source blockchain, the TokenPool should include a shared denomination in the destPoolData field of the LockOrBurnOutV1 struct (returned by the lockOrBurn function) to represent the value in a standard format. This data is then passed to the destination blockchain via CCIP.
  • Destination Blockchain: On the destination blockchain, the TokenPool should use the information contained in the sourcePoolData of the ReleaseOrMintInV1 struct (used by the releaseOrMint function) to convert the value into the local denomination. The correct number of tokens should then be minted based on the destination blockchain's decimal format. The minted amount is returned in the destinationAmount field of the ReleaseOrMintOutV1 struct, which is returned by the releaseOrMint function.

Get the latest Chainlink content straight to your inbox.