# Chainlink CCIP - Cross-Chain Interoperability Protocol Source: https://docs.chain.link/ccip Last Updated: 2025-05-19 Blockchain interoperability protocols are important for the Web3 ecosystem and traditional systems that need to interact with different blockchains. These protocols are the foundation for building blockchain abstraction layers, allowing traditional backends and dApps to interact with any blockchain network through a single middleware solution. Without a blockchain interoperability protocol, Web2 systems and dApps would need to build separate in-house implementations for each cross-chain interaction that they want to use, which is a time-consuming, resource-intensive, and complex process. Blockchain interoperability protocols provide the following capabilities: - You can transfer assets and information across multiple blockchains. - Application developers can leverage the strengths and benefits of different chains. - Collaboration between developers from diverse blockchain ecosystems enables the building of cross-chain applications to serve more users and provide additional features or products for them. The *Chainlink Cross-Chain Interoperability Protocol (CCIP)* provides these capabilities and enables a variety of [use cases](#common-use-cases). ## What is Chainlink CCIP? Chainlink CCIP is a blockchain interoperability protocol that enables developers to build secure applications that can transfer tokens, messages (data), or both tokens and messages across chains. Given the [inherent risks of cross-chain interoperability](/resources/bridge-risks), CCIP features [defense-in-depth security](https://blog.chain.link/five-levels-cross-chain-security/#level_5__defense-in-depth) and is powered by Chainlink's industry-standard oracle networks which have a proven track record of securing tens of billions of dollars and enabling over $14 trillion in onchain transaction value. CCIP provides several key security benefits: - Multiple independent nodes run by independent key holders. - Three decentralized networks all executing and verifying every cross-chain transaction. - Separation of responsibilities, with distinct sets of node operators, and with no nodes shared between the transactional DONs and the [Risk Management Network](/ccip/concepts/architecture/key-concepts#risk-management-network). - Increased decentralization with two separate code bases across two different implementations, written in two different languages to create a previously unseen diversity of software clients in the cross-chain world. - Novel risk management system with [level-5 security](https://blog.chain.link/five-levels-cross-chain-security/#level_5__defense-in-depth) that can be rapidly adapted to any new risks or attacks that appear in cross-chain messaging. To understand how Chainlink CCIP works, refer to the [architecture](/ccip/concepts/architecture) section. If you are new to using Chainlink CCIP, read these guides before you deploy any contracts that use CCIP. ## Chainlink CCIP core capabilities Chainlink CCIP supports three main capabilities: ### Arbitrary Messaging The ability to send arbitrary data (encoded as bytes) to a receiving smart contract on a different blockchain. The developer is free to encode any data they wish to send. Typically, developers use arbitrary messaging to trigger an informed action on the receiving smart contract, such as rebalancing an index, minting a specific NFT, or calling an arbitrary function with the sent data as custom parameters. Developers can encode multiple instructions in a single message, enabling them to orchestrate complex, multi-step, multi-chain tasks. ### Token Transfer The ability to transfer tokens to an account on a different blockchain. This capability enables the seamless movement of assets across chains. ### Programmable Token Transfer The ability to simultaneously transfer tokens and arbitrary data (encoded as bytes) within a single transaction. This mechanism allows users to transfer tokens and send instructions on what to do with those tokens. For example, a user could transfer tokens to a lending protocol with instructions to leverage those tokens as collateral for a loan, borrowing another asset to be sent back to the user. ### Receiving account types With CCIP, you send transactions with data (arbitrary messaging), tokens, or both data and tokens (programmable token transfer). The receiver of a CCIP transaction varies by blockchain family: | CCIP capability | What is sent | Receiving account types | | --------------------------- | --------------- | --------------------------------------------------------------------------------------- | | Arbitrary Messaging | Data | EVM: Smart contracts only
SVM: Programs only | | Token Transfer | Tokens | EVM: Smart contracts and EOAs
SVM: User wallets or program-controlled PDAs | | Programmable Token Transfer | Data and tokens | EVM: Smart contracts only
SVM: Data to programs, tokens to program-controlled PDAs | **Note**: On EVM chains, EOAs cannot receive messages. On Solana (SVM), programs work with Program Derived Addresses (PDAs) to manage token reception. ## Common use cases Chainlink CCIP enables a variety of use cases: - **Cross-chain lending:** Chainlink CCIP enables users to lend and borrow a wide range of crypto assets across multiple DeFi platforms running on independent chains. - **Low-cost transaction computation:** Chainlink CCIP can help offload the computation of transaction data on cost-optimized chains. - **Optimizing cross-chain yield:** Users can leverage Chainlink CCIP to move collateral to new DeFi protocols to maximize yield across chains. - **Creating new kinds of dApps:** Chainlink CCIP enables users to take advantage of network effects on certain chains while harnessing compute and storage capabilities of other chains. Read [What Are Cross-Chain Smart Contracts](https://chain.link/education-hub/cross-chain-smart-contracts) to learn about cross-chain smart contracts and examples of use cases they enable. ## CCIP Directory See the [CCIP Directory](/ccip/directory) page for a list of supported networks, tokens, and contract addresses. To learn about tokens, token pools, and the token onboarding process, see the [CCIP Architecture](/ccip/concepts/cross-chain-token/evm/token-pools) page. --- # Getting Started with Chainlink CCIP Source: https://docs.chain.link/ccip/getting-started Last Updated: 2025-05-19 ## Available Blockchain Families CCIP supports multiple blockchain families: - **EVM**: Send messages and tokens between Ethereum, Avalanche, Polygon, and other EVM-compatible networks - **SVM**: Connect Solana with other chain families through CCIP Check the [CCIP Directory](/ccip/directory) for a list of supported blockchains. ## What You Can Build with CCIP - **Cross-Chain dApps**: Create applications that operate seamlessly across multiple blockchains - **Token Bridges**: Transfer tokens between different blockchain networks - **Cross-Chain Data Messaging**: Send arbitrary data between smart contracts on different chains - **Programmable Token Transfers**: Combine token transfers with messaging to trigger specific actions on destination chains ## Getting Started Guides Choose your starting point based on your blockchain platform: - [Getting Started with CCIP on EVM Chains](/ccip/getting-started/evm) - Learn how to deploy sender/receiver contracts and send messages between EVM chains - [Getting Started with CCIP on Solana (SVM)](/ccip/getting-started/svm) --- # Getting Started (EVM) Source: https://docs.chain.link/ccip/getting-started/evm Last Updated: 2025-05-19 A simple use case for Chainlink CCIP is sending data between smart contracts on different blockchains. This guide shows you how to deploy a CCIP sender contract and a CCIP receiver contract to two different blockchains and send data from the sender contract to the receiver contract. You pay the CCIP fees using LINK. Fees can also be paid in alternative assets, which currently include the native gas tokens of the source blockchain and their ERC20 wrapped version. For example, you can pay ETH or WETH when you send transactions to the CCIP router on Ethereum and AVAX or WAVAX when you send transactions to the CCIP router on Avalanche. ## Before you begin - If you are new to smart contract development, learn how to [Deploy Your First Smart Contract](/quickstarts/deploy-your-first-contract) so you are familiar with the tools that are necessary for this guide: - The [Solidity](https://soliditylang.org/) programming language - The [MetaMask](https://metamask.io) wallet - The [Remix](https://remix.ethereum.org/) development environment - Acquire testnet funds. This guide requires testnet AVAX and LINK on *Avalanche Fuji*. It also requires testnet ETH on *Ethereum Sepolia*. If you need to use different networks, you can find more faucets on the [LINK Token Contracts](/resources/link-token-contracts) page. - Go to [faucets.chain.link](https://faucets.chain.link/) to get your testnet tokens. - Learn how to [Fund your contract with LINK](/resources/fund-your-contract). ## Deploy the sender contract Deploy the `Sender.sol` contract on *Avalanche Fuji*. To see a detailed explanation of this contract, read the [Code Explanation](#sender-code) section. 1. [Open the Sender.sol contract](https://remix.ethereum.org/#url=https://docs.chain.link/samples/CCIP/Sender.sol) in Remix. 2. Compile the contract. 3. Deploy the sender contract on *Avalanche Fuji*: 1. Open MetaMask and select the *Avalanche Fuji* network. 2. In Remix under the **Deploy & Run Transactions** tab, select *Injected Provider - MetaMask* in the **Environment** list. Remix will use the MetaMask wallet to communicate with *Avalanche Fuji*. 3. Under the **Deploy** section, fill in the router address and the LINK token contract addresses for your specific blockchain. You can find both of these addresses on the [CCIP Directory](/ccip/directory). The LINK token contract address is also listed on the [LINK Token Contracts](/resources/link-token-contracts) page. For *Avalanche Fuji*, the router address is 0xF694E193200268f9a4868e4Aa017A0118C9a8177 and the LINK address is 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846. 4. Click the **transact** button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to make sure you are deploying the contract to *Avalanche Fuji*. 5. After you confirm the transaction, the contract address appears in the **Deployed Contracts** list. Copy your contract address. 6. Open MetaMask and send 70 LINK to the contract address that you copied. Your contract will pay CCIP fees in LINK. **Note:** This transaction fee is significantly higher than normal due to gas spikes on Sepolia. To run this example, you can get additional testnet LINK from [faucets.chain.link](https://faucets.chain.link) or use a supported testnet other than Sepolia. ## Deploy the receiver contract Deploy the receiver contract on *Ethereum Sepolia*. You will use this contract to receive data from the sender that you deployed on *Avalanche Fuji*. To see a detailed explanation of this contract, read the [Code Explanation](#receiver-code) section. 1. [Open the Receiver.sol](https://remix.ethereum.org/#url=https://docs.chain.link/samples/CCIP/Receiver.sol) contract in Remix. 2. Compile the contract. 3. Deploy the receiver contract on *Ethereum Sepolia*: 1. Open MetaMask and select the *Ethereum Sepolia* network. 2. In Remix under the **Deploy & Run Transactions** tab, make sure the **Environment** is still set to *Injected Provider - MetaMask*. 3. Under the **Deploy** section, fill in the router address field. For *Ethereum Sepolia*, the Router address is 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59. You can find the addresses for each network on the [CCIP Directory](/ccip/directory). 4. Click the **Deploy** button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to make sure you are deploying the contract to *Ethereum Sepolia*. 5. After you confirm the transaction, the contract address appears as the second item in the **Deployed Contracts** list. Copy this contract address. You now have one *sender* contract on *Avalanche Fuji* and one *receiver* contract on *Ethereum Sepolia*. You sent `70` LINK to the *sender* contract to pay the CCIP fees. Next, send data from the sender contract to the receiver contract. ## Send data Send a `Hello World!` string from your contract on *Avalanche Fuji* to the contract you deployed on *Ethereum Sepolia*: 1. Open MetaMask and select the *Avalanche Fuji* network. 2. In Remix under the **Deploy & Run Transactions** tab, expand the first contract in the **Deployed Contracts** section. 3. Expand the **sendMessage** function and fill in the following arguments: | Argument | Description | Value (*Ethereum Sepolia*) | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | | destinationChainSelector | CCIP Chain identifier of the target blockchain. You can find each network's chain selector on the [CCIP Directory](/ccip/directory) | 16015286601757825753 | | receiver | The destination smart contract address | Your deployed contract address | | text | Any `string` | Hello World! | 4. Click the **transact** button to run the function. MetaMask prompts you to confirm the transaction. 1. After the transaction is successful, note the transaction hash. Here is an [example](https://testnet.snowtrace.io/tx/0x113933ec9f1b2e795a1e2f564c9d452db92d3e9a150545712687eb546916e633) of a successful transaction on *Avalanche Fuji*. After the transaction is finalized on the source chain, it will take a few minutes for CCIP to deliver the data to *Ethereum Sepolia* and call the `ccipReceive` function on your receiver contract. You can use the [CCIP explorer](https://ccip.chain.link/) to see the status of your CCIP transaction and then read data stored by your receiver contract. 1. Open the [CCIP explorer](https://ccip.chain.link/) and use the transaction hash that you copied to search for your cross-chain transaction. The explorer provides several details about your request. 2. When the status of the transaction is marked with a "Success" status, the CCIP transaction and the destination transaction are complete. ## Read data Read data stored by the receiver contract on *Ethereum Sepolia*: 1. Open MetaMask and select the *Ethereum Sepolia* network. 2. In Remix under the **Deploy & Run Transactions** tab, expand the receiver contract deployed on *Ethereum Sepolia*. 3. Click the **getLastReceivedMessageDetails** function button to read the stored data. In this example, it is "Hello World!". Congratulations! You just sent your first cross-chain data using CCIP. Next, examine the example code to learn how this contract works. ## Examine the example code ### Sender code The smart contract in this tutorial is designed to interact with CCIP to send data. The contract code includes comments to clarify the various functions, events, and underlying logic. However, this section explains the key elements. You can see the full contract code below. #### Initializing the contract When deploying the contract, you define the router address and the LINK contract address of the blockchain where you choose to deploy the contract. The router address provides functions that are required for this example: - The `getFee` [function](/ccip/api-reference/evm/v1.6.2/i-router-client#getfee) to estimate the CCIP fees. - The `ccipSend` [function](/ccip/api-reference/evm/v1.6.2/i-router-client#ccipsend) to send CCIP messages. #### Sending data The `sendMessage` function completes several operations: 1. Construct a CCIP-compatible message using the `EVM2AnyMessage` [struct](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage): - The `receiver` address is encoded in bytes format to accommodate non-EVM destination blockchains with distinct address formats. The encoding is achieved through [abi.encode](https://docs.soliditylang.org/en/develop/abi-spec.html). - The `data` is encoded from a string text to bytes using [abi.encode](https://docs.soliditylang.org/en/develop/abi-spec.html). - The `tokenAmounts` is an array. Each element comprises a [struct](/ccip/api-reference/evm/v1.6.2/client#evmtokenamount) that contains the token address and amount. In this example, the array is empty because no tokens are sent. - The `extraArgs` specify the `gasLimit` for relaying the CCIP message to the recipient contract on the destination blockchain. In this example, the `gasLimit` is set to `200000`. - The `feeToken` designates the token address used for CCIP fees. Here, `address(linkToken)` signifies payment in LINK. 2. Compute the fees by invoking the router's `getFee` [function](/ccip/api-reference/evm/v1.6.2/i-router-client#getfee). 3. Ensure that your contract balance in LINK is enough to cover the fees. 4. Grant the router contract permission to deduct the fees from the contract's LINK balance. 5. Dispatch the CCIP message to the destination chain by executing the router's `ccipSend` [function](/ccip/api-reference/evm/v1.6.2/i-router-client#ccipsend). ### Receiver code The smart contract in this tutorial is designed to interact with CCIP to receive data. The contract code includes comments to clarify the various functions, events, and underlying logic. However, this section explains the key elements. You can see the full contract code below. #### Initializing the contract When you deploy the contract, you define the router address. The receiver contract inherits from the [CCIPReceiver.sol](/ccip/api-reference/evm/v1.6.2/ccip-receiver) contract, which uses the router address. #### Receiving data On the destination blockchain: 1. The CCIP Router invokes the `ccipReceive` [function](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive). **Note**: This function is protected by the `onlyRouter` [modifier](/ccip/api-reference/evm/v1.6.2/ccip-receiver#onlyrouter), which ensures that only the router can call the receiver contract. 2. The `ccipReceive` [function](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive) calls an internal function `_ccipReceive` [function](/ccip/api-reference/evm/v1.6.2/ccip-receiver#_ccipreceive). The receiver contract implements this function. 3. This `_ccipReceive` [function](/ccip/api-reference/evm/v1.6.2/ccip-receiver#_ccipreceive) expects an `Any2EVMMessage` [struct](/ccip/api-reference/evm/v1.6.2/client#any2evmmessage) that contains the following values: - The CCIP `messageId`. - The `sourceChainSelector`. - The `sender` address in bytes format. The sender is a contract deployed on an EVM-compatible blockchain, so the address is decoded from bytes to an Ethereum address using the [ABI specification](https://docs.soliditylang.org/en/v0.8.20/abi-spec.html). - The `data` is also in bytes format. A `string` is expected, so the data is decoded from bytes to a string using the [ABI specification](https://docs.soliditylang.org/en/v0.8.20/abi-spec.html). --- # Getting Started with Chainlink CCIP on Solana (SVM) Source: https://docs.chain.link/ccip/getting-started/svm Last Updated: 2025-08-22 ## Solana (SVM) Support in CCIP Chainlink CCIP supports Solana through the Solana Virtual Machine (SVM), enabling cross-chain interoperability between Solana and other blockchain families including EVM chains. ## What You Can Build with CCIP on Solana - **Cross-Chain Token Transfers**: Transfer tokens from/to Solana - **Cross-Chain Data Messaging**: Send arbitrary data between Solana programs and smart contracts on other chains - **Programmable Token Transfers**: Combine token transfers with messaging to trigger specific actions on destination chains - **Cross-Chain Token (CCT) Standard**: Enable your tokens in CCIP to be transferred to/from Solana ## Getting Started with Solana CCIP SVM tutorials can be found [here](/ccip/tutorials/svm). --- # CCIP Service Limits Source: https://docs.chain.link/ccip/service-limits Last Updated: 2025-09-03 This section outlines the operational limits for Chainlink CCIP across different blockchain architectures. - **[EVM Service Limits](/ccip/service-limits/evm)**: Service limits for Ethereum and other EVM-compatible blockchains. - **[Solana Service Limits](/ccip/service-limits/svm)**: Service limits for Solana. - **[Aptos Service Limits](/ccip/service-limits/aptos)**: Service limits for Aptos. - **[Network-Specific Limits](/ccip/service-limits/network-specific-limits)**: Documented network-specific limitations (all blockchain families). Understanding these limits is essential for building reliable cross-chain applications that operate within CCIP's intended parameters. --- # CCIP Service Limits (EVM) Source: https://docs.chain.link/ccip/service-limits/evm Last Updated: 2025-05-19 ## Mainnet | Item | Description | Limit | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) | 30 kilobytes | | Message Execution Gas Limit | User specified [gas limit](/ccip/api-reference/evm/v1.6.2/client#genericextraargsv2)

Exception: Lanes originating from CORN have a maximum gas limit of 500,000. | 3,000,000 | | Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction. | 1 | | Token Pool Execution Gas Limit | Maximum gas for executing the combined steps in token pools during cross-chain transfers, including: (1) `balanceOf` check before minting/releasing, (2) `releaseOrMint` function, and (3) `balanceOf` check after minting/releasing. For more details on building custom token pools and handling gas constraints, refer to the [Token Pools documentation](/ccip/concepts/cross-chain-token/evm/token-pools#common-requirements). | 90,000 | ## Testnet | Item | Description | Limit | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | Maximum message `data` length | `data` payload sent within the [CCIP message](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) | 30 kilobytes | | Message Execution Gas Limit | User specified [gas limit](/ccip/api-reference/evm/v1.6.2/client#genericextraargsv2)

Exception: Lanes originating from CORN have a maximum gas limit of 500,000. | 3,000,000 | | Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction | 1 | | Token Pool Execution Gas Limit | Maximum gas for executing the combined steps in token pools during cross-chain transfers, including: (1) `balanceOf` check before minting/releasing, (2) `releaseOrMint` function, and (3) `balanceOf` check after minting/releasing. For more details on building custom token pools and handling gas constraints, refer to the [Token Pools documentation](/ccip/concepts/cross-chain-token/evm/token-pools#common-requirements). | 90,000 | --- # CCIP Service Limits (SVM) Source: https://docs.chain.link/ccip/service-limits/svm Last Updated: 2025-05-19 ## EVM to SVM | Item | Description | Limit | | :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------- | :----------- | | Maximum message data length | The total payload for the message (includes the user specified message.data as well as the accounts, Bitmap, token transfer data) | 640 bytes | | Message execution compute units limit | User specified compute units limit. Mandatory to be set | 400,000 CU | | Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction. | 1 | | Smart Execution time window | Maximum duration CCIP will attempt automatic execution of message | 8 hours | | Token pool execution gas limit | Default compute units for TokenPool execution at the destination, ie., Solana | 150,000 CU | | Out of Order execution | Parameter in the extraArgs of a CCIP message | Must be True | ## SVM to EVM | Item | Description | Limit | | :----------------------------- | :-------------------------------------------------------------------------------------------------- | :------------ | | Maximum message data length | data payload sent within the [CCIP message](/ccip/api-reference/svm/v1.6.0/messages#svm2anymessage) | 400 bytes | | Message execution gas limit | User specified gas limit. Mandatory to be set | 3,000,000 gas | | Maximum number of tokens | Maximum number of distinct tokens a user can transfer in a single transaction. | 1 | | Smart Execution time window | Maximum duration for the execution of a CCIP message | 8 hours | | Token pool execution gas limit | Maximum gas for executing the combined steps in token pools during cross-chain transfers | 90,000 gas | | Out of Order execution | Parameter in the extraArgs of a CCIP message | Must be True | --- # CCIP Service Limits (Aptos) Source: https://docs.chain.link/ccip/service-limits/aptos Last Updated: 2025-09-03 ## EVM to Aptos These limits apply to messages sent from an EVM-compatible chain to the Aptos blockchain. | Item | Description | Limit | | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :------------- | | Maximum message data length | The total payload for the message, including user data and token transfer information. | 30,000 bytes | | Message execution gas limit | The user-specified `gasLimit` in `extraArgs` for the execution of the `ccip_receive` function on Aptos. | 100,000 gas | | Maximum number of tokens | The maximum number of distinct tokens that can be transferred in a single transaction. | 1 | | Smart Execution time window | The maximum duration that CCIP will attempt to automatically execute a message on Aptos before manual execution is required. | 8 hours | | Token pool execution gas limit | The default gas overhead allocated for a standard token transfer operation on Aptos when a message is received from an EVM chain. | 36 gas | | Out of Order execution | The `allowOutOfOrderExecution` parameter in the `extraArgs` of a CCIP message from an EVM chain. | Must be `true` | ## Aptos to EVM These limits apply to messages sent from the Aptos blockchain to an EVM-compatible chain. | Item | Description | Limit | | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------- | :------------- | | Maximum message data length | The `data` payload sent within the [CCIP message](/ccip/api-reference/aptos/v1.6.0/messages#aptos2anymessage). | 30,000 bytes | | Message execution gas limit | The user-specified `gasLimit` in `extraArgs` for the execution of the `ccipReceive` function on the destination EVM chain. | 100,000 gas | | Maximum number of tokens | The maximum number of distinct tokens that can be transferred in a single transaction. | 1 | | Smart Execution time window | The maximum duration for the execution of a CCIP message on the destination EVM chain. | 8 hours | | Token pool execution gas limit | The default gas overhead for executing token pool logic on the destination EVM chain for a standard token transfer. | 36 gas | | Out of Order execution | The `allow_out_of_order_execution` parameter in the `extraArgs` of a CCIP message from Aptos. | Must be `true` | --- # Chainlink CCIP Network Specific Limits Source: https://docs.chain.link/ccip/service-limits/network-specific-limits Last Updated: 2025-06-27 This page describes known network-specific limitations that may result from the design of a particular network. End users, application developers, blockchain development teams, token developers and others should read and understand these risks when considering interacting with these networks on CCIP. ## HyperEVM **Risk**: [Hyperliquid Data availability](/ccip/service-responsibility#blockchain-development-team-responsibilities): If a HyperEVM RPC is out of sync or offline for 10+ minutes, missing logs from during the downtime are only available through a centralized resource that is hosted and maintained by Hyperliquid. In the event that logs related to specific CCIP messages are irrecoverable, those CCIP messages can become stuck or fail. **CCIP Mitigation**: Hyperliquid has provided a workaround to serve logs through a [dedicated resource](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/raw-hyperevm-block-data) in case their network is inaccessible from RPC providers. CCIP is utilizing Hyperliquid's data source to backfill missing logs. In addition, CCIP Node Operators have implemented additional defense, using an multi-RPC approach, allowing for detection of out-of-sync or offline RPCs and an automatic fallback. If all configured RPCs are unavailable for a period of 10+ minutes AND the Hyperliquid data source does not provide them, logs may not be recoverable. --- # CCIP Service Responsibility - Shared Accountability Model Source: https://docs.chain.link/ccip/service-responsibility Last Updated: 2025-06-27 The Chainlink Cross-Chain Interoperability Protocol (CCIP) is a secure, reliable, and easy-to-use interoperability protocol for building cross-chain applications and services. The use of CCIP involves application developers, blockchain development teams, token developers and Chainlink node operators, among others. These participants share responsibility for ensuring that operation and performance match expectations. Please note that CCIP support of a particular blockchain, application, or token does not constitute endorsement of such blockchain, application, or token. Please review the [CCIP Service Limits](/ccip/service-limits) which provides important additional information. ## Application Developer Responsibilities Application developers are responsible for the correctness, security, and reliability of their application. This includes: - **Code and application audits:** Developers are responsible for auditing their code and applications before deploying to production. Developers must determine the quality of any audits and ensure that they meet the requirements for their application. - **CCIP upgrades and best practices:** Developers are responsible for following CCIP documentation regarding implementing CCIP upgrades and best practices for integrating CCIP in their applications. - **Code dependencies and imports:** Developers are responsible for ensuring the quality, reliability, and security of any dependencies or imported packages that they use with Chainlink CCIP, as well as reviewing and auditing these dependencies and packages. - **Code quality and testing:** Developers are responsible for ensuring that their application code, onchain and offchain, meets the quality expectations and has undergone rigorous testing. - **Application monitoring and alerting:** Developers must monitor their applications, inform their users of any abnormal activity, and take appropriate action to restore normal operations. - **Blockchain risk assessment:** Developers are responsible for the risk assessment of any blockchain network where they choose to deploy their application on or decide to interoperate with, when using Chainlink CCIP. This includes reviewing the time-to-finality formally documented by a blockchain's development team, understanding [how CCIP uses it to determine finality](/ccip/ccip-execution-latency), the nuances in the different types of deterministic finality, and being aware of the risks when CCIP uses block depth to determine chain finality. - **Token risk assessment:** Developers are responsible for the risk assessment of any tokens they choose to support or list in their application and expose to their users. - **Risk communication:** Developers must clearly articulate and communicate identified risks to their users. - **Manual execution:** Developers must monitor their CCIP transactions and take action when transactions require manual execution. For example, informing their users and directing them to the appropriate page on the [CCIP Explorer](https://ccip.chain.link). - **Risk Management Network coverage:** Developers must check the deployment status of the Risk Management Network on the chains they build on, such as via the [CCIP Directory](/ccip/directory). If the Risk Management Network is not yet active on a chain, developers must validate that its absence conforms to the requirements of their application's specific use case. ## Blockchain Development Team Responsibilities Blockchain development teams are responsible for the correctness, security, and reliability of their blockchain software. This includes: - **Block finality:** Blockchain development teams must ensure that blocks with a [commitment level](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block) of `finalized` are actually final. The properties of the finality mechanism, including underlying assumptions and conditions under which finality violations could occur, must be clearly documented and communicated to application developers in the blockchain ecosystem. The documented time-to-finality informs how long CCIP waits for finality for outbound transactions from that chain; however, an additional buffer may be added. - **Governance model:** Blockchain development teams are responsible for setting up a clear and effective governance model and communicating its participants and processes clearly to its stakeholders and application developers. - **Fixes and upgrades:** Blockchain development teams must communicate availability of fixes immediately and announce planned upgrades as much in advance as possible so blockchain validators and application developers can prepare themselves accordingly. - **Incident management:** Blockchain development teams are responsible for clearly articulating and communicating any security, reliability and availability incidents to their community. This includes root cause analysis, post-mortem details and a clear plan of action to recover and prevent from happening in the future. - **Blockchain liveness:** Blockchain development teams must take appropriate action to ensure their blockchain maintains a high degree of liveness and aligns with set expectations towards their community members and applications developers. - **Data availability**: The blockchain network must ensure that complete historical data remains consistently accessible through standard RPC interfaces. This includes block headers, transactions, and emitted logs. This includes ensuring that nodes can serve past blocks and logs even after temporary downtime or restarts. Failure to provide reliable access to historical logs can result in stuck or failed transactions. ## Token Developers Responsibilities Token Developers may enable token transfers on CCIP for the tokens that they administer. Enabling token transfers on CCIP allows users to transfer tokens between supported blockchains using either [Burn and Mint](/ccip/concepts/cross-chain-token/overview#burn-and-mint), [Lock and Mint](/ccip/concepts/cross-chain-token/overview#lock-and-mint), or [Lock and Unlock](/ccip/concepts/cross-chain-token/overview#lock-and-unlock) processes. Token Developers who choose to enable token transfers on CCIP are responsible for the correctness, security, and reliability of their token pools, token configurations, and token contracts. This includes: - **Code and application audits**: Token Developers are responsible for auditing their token contract code and token pool contract code. Developers must determine the quality of any audits and ensure that they meet the requirements for their use cases. - **Configuration of CCIP contracts**: Token Developers are responsible for maintaining the correct token pool and token administrator for their token in all applicable TokenAdminRegistry contracts. Users are responsible for maintaining control of the address which is set as the token administrator. This token administrator is the only role authorized to map a token to the corresponding token pool on the same network. - **CCIP upgrades and best practices**: Token Developers are responsible for following CCIP documentation regarding implementing CCIP upgrades and best practices for enabling token transfers on CCIP for their token. - **Code dependencies and imports**: Token Developers are responsible for ensuring the quality, reliability, and security of any dependencies or imported packages that they use with their token contracts, token pools, or configurations including the TokenAdminRegistry. Token Developers are responsible for reviewing and auditing these dependencies and packages. - **Token Developers must retain access to the token administrator account** after it has accepted this role in the TokenAdminRegistry. Neither Chainlink Labs nor the Chainlink Foundation is responsible for any loss of access to these token pools, loss of funds, or disruption to applications due to loss of access to these required functions. - **Blockchain risk assessment**: Token Developers are responsible for the risk assessment of any blockchain network where they choose to deploy their tokens, token pools, and tokens enabled for transfer using Chainlink CCIP. - **Risk communication**: Token Developers must clearly articulate and communicate identified risks to the users of those tokens including any risks specific to the configuration of tokens enabled for transfer using Chainlink CCIP. - **Authorization**: Token Developers must verify that they are authorized to create token pools for a given token. Although anyone may create a token pool, the token developer must properly register that token with Chainlink CCIP. Token Developers must also properly configure the TokenAdminRegistry. - **Token pool configurations for [Rebasable Tokens](/ccip/concepts/cross-chain-token/evm/token-pools#tokens-with-rebasing-or-fee-on-transfer-mechanisms)**: Token Developers must properly write the logic in their token pool for burning and minting tokens based on the rebasing mechanism. - **Token transfer rate limits**: Token DeveloperToken Owners must select and configure appropriate token transfer rate limits for tokens on each lane where they choose to enable their token. - **Token transfer types**: Token Developers must select appropriate token transfer type for their tokens; either [Burn and Mint](/ccip/concepts/cross-chain-token/overview#burn-and-mint), [Lock and Mint](/ccip/concepts/cross-chain-token/overview#lock-and-mint), or [Lock and Unlock](/ccip/concepts/cross-chain-token/overview#lock-and-unlock). Token Developers are responsible for implementing the burn and mint functions, lock and mint functions or lock and unlock functions in their token contracts correctly on all applicable chains. - **Migration between CCIP versions**: Token Developers who wish to adopt future versions of CCIP are responsible for all migration tasks required to adopt new features and functionality. - **Best Practices**: Token Developers are responsible for following the appropriate best practices for creating, managing, and enabling transfers of their tokens on Chainlink CCIP. - **Risk Management Network coverage**: Token Developers must check the deployment status of the Risk Management Network on the chains they build on, which can be found on the [CCIP Directory](/ccip/directory) page. If the Risk Management Network is not yet active on a chain, Token Developers must validate that its absence conforms to their requirements. - **Token Developer Attestation**: Token Developers are responsible for ensuring the quality, reliability, and security of their associated attestation endpoint(s). Token Developers are responsible for adhering to Chainlink-defined specifications and maintaining an up-to-date implementation. Neither Chainlink Labs nor the Chainlink Foundation are responsible for the development, maintenance, or operation of Token Developer Attestation endpoints. - **Following implementation specifications**: Failure to adhere to design specifications for the Chainlink-defined Token Developer Attestation endpoint can result in stuck or failed transactions for users, incorrect accounting of token supply, and/or potential loss of tokens. - **Maintenance**: Failure to maintain up-to-date compatibility with the Chainlink-defined design specifications may result in downtime or unreliable attestations. - **Reliability**: Attestation endpoints must be built to handle user demand, both in terms of transactional capacity and uptime. Failure to respond to attestation requests may result in stuck or failed transactions for users and/or potential loss of tokens. - **Liquidity Management**: Token Developers who choose the **Lock and Mint** or **Lock and Unlock** mechanism must ensure their token pools have sufficient liquidity when releasing tokens. Failure to maintain adequate liquidity can result in stalled or failed cross-chain transfers, causing a degraded user experience. Token Developers are responsible for: - **Ensuring sufficient liquidity**: Continuously monitor transaction volumes and add liquidity to the pool before it is depleted to avoid having user funds stuck in transit. - **Avoiding fragmented liquidity**: Where possible, minimize the use of **Lock and Unlock** across multiple blockchains to reduce operational complexity and prevent liquidity from being split across multiple pools. - **Monitoring liquidity health and automating alerts**: Implement monitoring and alerting systems that notify Token Developers when liquidity drops below certain thresholds, allowing for proactive liquidity management before user transfers fail. - **Proper use of provideLiquidity and withdrawLiquidity**: Only authorized entities (such as a trusted liquidity manager) should manage liquidity. Ensure all access controls are in place to prevent unauthorized manipulation of the token pool. Although Token Developers may request that their tokens be added to Transporter, tokens may be added to Transporter at any time even if it has not been explicitly requested. ## Chainlink Node Operator Responsibilities High-quality Chainlink node operators participate in the decentralized oracle networks (DONs) that power CCIP and the Risk Management Network using a configuration specified in the Chainlink software. As participants in these deployments, Node Operators are responsible for the following components of Chainlink CCIP and the Risk Management Network: - **Node operations:** Chainlink node operators must ensure the proper configuration, maintenance, and monitoring of their nodes participating in the Chainlink CCIP and Risk Management Network DONs. - **Transaction execution:** Chainlink node operators must ensure that transactions execute onchain in a timely manner and that they apply gas bumping when necessary. - **Blockchain client:** Chainlink node operators are responsible for selecting and properly employing blockchain clients, including latest fixes and upgrades, to connect to supported blockchain networks. - **Consensus participation:** Chainlink node operators must maintain continuous uptime and active participation in OCR consensus. - **Infrastructure security:** Chainlink node operators must follow infrastructure security best practices. These include access control, configuration management, key management, software version & patch management, and (where applicable) physical security of the underlying hardware. - **Software version:** Chainlink node operators are responsible for ensuring that Chainlink node deployments are running the latest software versions. - **Responsiveness:** Chainlink node operators must respond to important communication from Chainlink Labs or from other node operators in a timely manner. --- # CCIP Execution Latency - Transaction Finality and Timing Source: https://docs.chain.link/ccip/ccip-execution-latency Last Updated: 2025-06-04 ## CCIP transaction lifecycle As depicted in the [CCIP detailed architecture](/ccip/concepts/architecture/overview) section, the CCIP transaction lifecycle involves multiple stages from the source blockchain to the destination blockchain: **Source blockchain:** 1. A sender contract or externally owned account (EOA) sends a CCIP message. 2. The transaction is included in a block and processed by "network participants" (validators in PoS and miners in PoW blockchains). 3. The Committing Decentralized Oracle Network (DON) waits for the block containing the transaction to achieve finality. **Destination blockchain:** 1. After finality is reached on the source blockchain, the Committing DON relays the Merkle root of a batch of finalized messages to the OffRamp contract on the destination chain. 2. The Risk Management Network verifies and blesses the committed Merkle root on the destination chain, confirming the integrity of the Merkle root and the messages it authenticates. 3. The Executing DON executes the message on the destination chain. If the execution cost on the destination chain is within an acceptable range, the message is executed promptly after being blessed by the Risk Management Network. The combined latencies of each step in the transaction lifecycle on both the source and destination blockchains impact the total end-to-end execution time for a CCIP transaction. Because different blockchains have unique optimizations for their consensus mechanisms, block sizes, and block times, some blockchains are faster at processing transactions than others. However, various factors can lead to variation in transaction times, including when transferring between the same pair of blockchains: - **Finality:** Finality is the assurance that past transactions included onchain are extremely difficult or impossible to revert. Finality varies across different blockchains. Some blockchains offer instant finality, while others require multiple block confirmations. - **Network Congestion:** Network congestion occurs when the volume of transactions exceeds the capacity of the blockchain network, leading to delays in transaction processing. Multiple factors can contribute to network congestion, such as high transaction volumes, increased adoption of blockchain technologies, and events like token launches. - **Gas Price:** Network participants often prioritize transactions with higher gas prices. If a low gas price is set for a transaction, it can take longer to process than one with a higher gas price, especially during network congestion. Waiting for finality on the source blockchain is crucial when transacting across multiple networks, as it helps ensure that actions taken on the destination blockchain are based on transactions on the source blockchain that are extremely difficult or impossible to revert. Because the time to achieve finality varies across blockchains and significantly impacts the total CCIP execution latency, the following sections will focus on explaining the different types of finality and how CCIP approaches source chain security on each supported blockchain. ## Finality Different blockchains employ various consensus mechanisms, leading to different types of finality. This affects users, as even once a transaction is onchain, they must often wait for it to be considered finalized (a time period that varies by blockchain). Finality with blockchains can primarily be categorized into two main types: **probabilistic** finality and **deterministic** finality: - **Probabilistic finality** is mainly used by Proof-of-Work blockchains and is not the main subject of this article. - **Deterministic finality** is widely used in most smart contract enabled blockchains that Chainlink CCIP is integrated with today. ### Types of Deterministic Finality #### Finality on L1 PoA/PoS Chains Typically, Proof of Authority / Proof of Stake (PoA/PoS) chains use a deterministic model to determine when a block/transaction is considered final. The consensus protocol utilized in such a system is usually designed to be Byzantine Fault Tolerant (BFT). This means that under the assumption that some subset (usually between 51% - 67%) of the participating nodes/stake are honest, and there are no errors in the protocol's implementation, the system works as expected and finality assurances are upheld. **Examples:** - **Ethereum's PoS:** Ethereum PoS achieves Byzantine Fault Tolerance (BFT) through economic constraints. Ethereum PoS manages finality using "checkpoint" blocks. The first block in each epoch is designated as a checkpoint. Validators vote on pairs of checkpoints. When two-thirds of the total staked ETH validators agree on the validity of the pair, the earlier of the two checkpoints becomes finalized. To revert a finalized block, an attacker would have to burn at least one-third of the total staked ether, making such an attack extremely costly and difficult to achieve. - **Comet BFT (Cosmos Hub Network):** Comet BFT is a Byzantine Fault Tolerant (BFT) consensus algorithm designed to provide instant finality. It achieves BFT by ensuring that consensus can be reached as long as more than two-thirds of validators are honest. Once these validators confirm a block, it is immediately considered final and irreversible. - **Avalanche:** Avalanche uses a random sampling of validators who repeatedly vote on transactions. This process continues until enough validators agree, achieving sub-second finality. - **BNB Chain:** BNB Chain uses a combination of Proof of Staked Authority (PoSA) and Byzantine Fault Tolerance (BFT) algorithms to finalize transactions. If two-thirds or more of the validators vote as expected, it takes an average of 2.5 blocks to finalize a transaction. #### Finality on L2s Layer 2 blockchains, or L2s, are implementations of blockchain systems built on top of existing blockchains (known as Layer 1, or L1) to improve scalability and reduce transaction costs. While they operate independently, L2s are designed to inherit the security of the underlying Layer 1 blockchain: L2s post periodic checkpoints to the underlying blockchain they are built on top of, settling their state and providing stronger finality guarantees than what is provided by their native model. **Optimistic Rollups:** Most of the popular optimistic rollups that exist today are run through a centralized sequencer. The sequencer is responsible for ordering incoming transactions, including them in blocks, batching them together and posting them as a bundle to the underlying blockchain they settle on. These bundles serve as commitments and once posted provide more certainty on the finalized state of the rollup. Given that the sequencer is centralized, users are faced with the choice to trust that it won't change the order of the transactions or wait until these commitments are posted to the underlying L1 blockchain. The optimistic model means that the commitment is valid by default when it is posted to the Layer 1 (L1) blockchain. This is why optimistic rollups typically provide a challenge period, during which a commitment can be challenged if it turns out to be fraudulent. If a challenge is successful, the commitment is replaced and the rollup state is updated to the correct one. **The typical lifecycle of an optimistic rollup transaction is:** 1. Transaction is included in an L2 block by the sequencer. 2. Transaction is included in a batch that is committed to the L1. 3. Challenge period during which a batch can be challenged if it's invalid - usually lasts a week or more. 4. Transaction is finalized on the L1 - at this point it is considered irreversible. In the popular optimistic rollup implementations that exist (e.g. OP, Arbitrum, etc.), a commitment can only be challenged if it contains an invalid state root. If the commitment is a valid continuation of the L2, it cannot be challenged. Therefore, seeing a commitment and verifying that it is valid is sufficient certainty for most users to assume finality on this type of L2s, as long as they trust the finality guarantees of the underlying L1. Importantly, this guarantee is supported by waiting for the commitment to the L1 to be finalized according to the L1's finality model. **ZK Rollups:** Similarly to optimistic rollups, most popular ZK rollups that exist today are run through a centralized sequencer. The ZK rollup sequencer is also responsible for ordering incoming transactions, including them in blocks, batching them together and posting them as a bundle to the underlying L1 blockchain they settle on. However, in the case of ZK rollups, they also post a validity proof with each batch that is automatically verified onchain on the underlying L1. This validity proof removes the need for a challenge period like on optimistic rollups. **The typical lifecycle of a ZK rollup transaction is:** 1. Transaction is included in an L2 block by the sequencer. 2. Transaction is included in a batch that is committed to the L1. 3. Validity proof is posted on the L1 that proves the commitment from step 2. 4. Transaction is finalized on the L1. 5. Transaction is considered irreversible. In many cases this happens after a considerable rollup-specific "safety delay" (12-24 hours) from the previous step, which is expected to be reduced as the technology matures. ### How CCIP Determines Finality The end-to-end transaction times of CCIP messages depend largely on the time-to-finality of the source blockchain. Different blockchains have varying finality types, leading to different times to reach finality. This section explains how CCIP determines finality for different blockchains. #### Finality Tag Blockchains with deterministic finality often use a finality tag to indicate when a block is considered final. The finality tag delineates which blocks are finalized, offering a standardized way to determine transaction finality. - After The Merge, Ethereum shifted to an epoch-based process in PoS, where finality is achieved when two-thirds of validators agree on block finalization over two epochs (64 slots, approximately 12.8 minutes). The Ethereum team introduced the finality tag to provide a default block parameter in specific [JSON-RPC calls](https://ethereum.org/en/developers/docs/apis/json-rpc/), delineating finalized blocks without ambiguity. The finality tag is supported by various Ethereum clients, including Geth. - Other blockchains have adopted similar finality tags to indicate finalized blocks. #### Block Depth In some cases, CCIP relies on block depth to determine when a transaction can be considered final. The block depth refers to the number of successive blocks added after the one containing a given transaction. CCIP uses a sufficient number of blocks to consider the transaction most likely safe from reorgs. There are three cases where CCIP would use block depth: - Blockchains with probabilistic finality. - Blockchains with deterministic finality but without a finality tag: In some cases, blockchains have deterministic finality but do not provide a finality tag. - Blockchains with deterministic finality but slow finality times: In some cases, deterministic finality can take a significant amount of time to reach, leading to a poor user experience. ### Finality by blockchain This section provides an overview of the finality methods CCIP uses to determine finality for each currently supported blockchain. The table below lists each blockchain and its finality method—whether it uses a finality tag or block depth (with the number of blocks specified for block depth)—and the estimated time required to achieve finality. | Source Blockchain | Finality Method | Estimated Time for Finality | | ----------------- | ----------------------------------------- | --------------------------- | | Apechain | Finality tag | 50 minutes | | Arbitrum | Finality tag | 17 minutes | | Astar | Finality tag | 35 seconds | | Avalanche | Finality tag | \< 1 second | | Base | Finality tag | 18 minutes | | Berachain | Finality tag | 7 seconds | | BitLayer | [Block depth](#block-depth) (21 blocks) | 1 minute | | Blast | Finality tag | 20 minutes | | BNB | Finality tag | 5 seconds | | Bob | Finality tag | 2 hours | | B² | Finality tag | 20 minutes | | Celo | Finality tag | \< 1 second | | Core | [Block depth](#block-depth) (7 blocks) | 1 minute | | Corn | Finality tag | 12 hours | | Cronos | Finality tag | 1 second | | Cronos zkEVM | Finality tag | 31 hours | | Ethereum | Finality tag | 15 minutes | | Fraxtal | Finality tag | 30 minutes | | Gnosis | Finality tag | 3 minutes | | Hashkey | Finality tag | 1 hour | | Ink | Finality tag | 2 hours | | Kroma | Finality tag | 25 minutes | | Linea | [Block depth](#block-depth) (600 blocks) | 20 minutes | | Mantle | Finality tag | 28 minutes | | Metis | Finality tag | 2 hours | | Mind Network | Finality tag | 1 hour | | Mode | Finality tag | 37 minutes | | OP | Finality tag | 20 minutes | | Polygon | [Block depth](#block-depth) (500 blocks) | 17 minutes | | Polygon zkEVM | Finality tag | 2 hours | | Ronin | Finality tag | 10 seconds | | Scroll | Finality tag | 1 hour | | Sei | Finality tag | 1 second | | Solana | Finality tag | \< 1 second | | Soneium | Finality tag | 27 minutes | | Sonic | [Block depth](#block-depth) (10 blocks) | 7 seconds | | Shibarium | Finality tag | 1 minute | | Treasure | Finality tag | 7 hours | | Unichain | Finality tag | 24 minutes | | Wemix | Finality tag | \< 1 second | | Worldchain | Finality tag | 40 minutes | | XLayer | Finality tag | 1 hour | | Zircuit | Finality tag | 21 minutes | | ZKsync | [Block depth](#block-depth) (1200 blocks) | 20 minutes | This page provides details on the expected latency for a cross-chain transaction using CCIP, covering the different stages of transaction processing and the factors that influence overall execution times. For a comprehensive understanding of CCIP's architecture and how messages flow through the system, refer to the [CCIP detailed architecture](/ccip/concepts/architecture/overview) documentation. --- # CCIP Billing Source: https://docs.chain.link/ccip/billing Last Updated: 2025-05-19 The CCIP billing model uses the `feeToken` specified in the [message](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) to pay a single fee on the source blockchain. CCIP uses a gas-locked fee payment mechanism to help ensure the reliable execution of cross-chain transactions regardless of destination blockchain gas spikes. For developers, this means you can simply pay on the source blockchain and CCIP will take care of execution on the destination blockchain. CCIP supports fee payments in LINK and in alternative assets, including blockchain-native gas tokens and their ERC-20 wrapped versions. The payment model for CCIP is designed to significantly reduce friction for users and quickly scale CCIP to more blockchains by supporting fee payments that originate across a multitude of blockchains over time. Aside from billing, remember to [carefully estimate the `gasLimit` that you set](/ccip/concepts/best-practices/evm#setting-gaslimit) for your destination contract so CCIP can have enough gas to execute `ccipReceive()`, if applicable. Any unspent gas from this user-set limit is not refunded. ## Billing mechanism The fee is calculated by the following formula: ``` fee = blockchain fee + network fee ``` Where: - `fee`: The total fee for processing a [CCIP message](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage). **Note:** Users can call the [getFee](/ccip/api-reference/evm/v1.6.2/i-router-client#getfee) function to estimate the fee. - `blockchain fee`: This represents an estimation of the gas cost the node operators will pay to deliver the CCIP message to the destination blockchain. - `network fee`: Fee paid to CCIP service providers, including node operators running the [Decentralized Oracle Network](/ccip/concepts/architecture/key-concepts#decentralized-oracle-network-don) and [Risk Management Network](/ccip/concepts/architecture/key-concepts#risk-management-network). ### Blockchain fee The blockchain fee is calculated by the following formula: ``` blockchain fee = execution cost + data availability cost ``` #### Execution cost The execution cost is directly correlated with the estimated gas usage to execute the transaction on the destination blockchain: ``` execution cost = gas price * gas usage * gas multiplier ``` Where: - `gas price`: The destination gas price. CCIP maintains a cache of destination gas prices on each source blockchain, denominated in each `feeToken`. - `gas multiplier`: Scaling factor. This multiplier ensures the reliable execution of transactions regardless of destination blockchain gas spikes. - `gas usage`: ``` gas usage = gas limit + destination gas overhead + destination gas per payload + gas for token transfers` ``` Where: - `gas limit`: This specifies the maximum amount of gas CCIP can consume to execute [ccipReceive()](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive) on the receiver contract located on the destination blockchain. Users set the gas limit in the [extra argument field](/ccip/api-reference/evm/v1.6.2/client#genericextraargsv2) of the CCIP message. **Note:** Remember to [carefully estimate the `gasLimit` that you set](/ccip/concepts/best-practices/evm#setting-gaslimit) for your destination contract so CCIP can have enough gas to execute `ccipReceive()`. Any unspent gas from this user-set limit is not refunded. - `destination gas overhead`: This is the fixed gas cost incurred on the destination blockchain by CCIP (Committing DON + Executing DON) and Risk Management Network. - `destination gas per payload`: This variable gas depends on the length of the data field in the [CCIP message](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage). If there is no payload (CCIP only transfers tokens), the value is `0`. - `gas for token transfers`: This variable gas cost is for transferring tokens onto the destination blockchain. If there are no token transfers, the value is `0`. #### Data availability cost This cost is only relevant if the destination blockchain is a [L2 layer](https://chain.link/education-hub/what-is-layer-2). Some L2s charge fees for [data availability](https://ethereum.org/en/developers/docs/data-availability). For instance, [optimistic rollups](https://ethereum.org/en/developers/docs/scaling/optimistic-rollups/) process the transactions offchain then post the transaction data to Ethereum as calldata, which costs additional gas. ### Network fee The fee paid to CCIP service providers, including node operators running the [Decentralized Oracle Network](/ccip/concepts/architecture/key-concepts#decentralized-oracle-network-don) and [Risk Management Network](/ccip/concepts/architecture/key-concepts#risk-management-network) is calculated as follows: #### Token transfers or programmable token transfers For token transfers or programmable token transfers (token + data), the network fee varies based on the [token handling mechanism](/ccip/concepts/cross-chain-token/overview#token-handling-mechanisms) and the lanes: - **Lock and Unlock**: The network fee is percentage-based. For each token, it is calculated using the following expression: ``` tokenAmount * price * percentage ``` Where: - `tokenAmount`: The amount of tokens being transferred. - `price`: Initially priced in USD and converted into the `feeToken`. - `percentage`: The values are provided in the [network fee table](#network-fee-table). - **Lock and Mint**, **Burn and Mint** and **Burn and Unlock**: The network fee is a static amount. See the [network fee table](#network-fee-table). #### Messaging (only data) For messaging (only data): The network fee is a static amount, denominated in USD. See the [network fee table](#network-fee-table). #### Network fee table The table below provides an overview of the network fees charged for different use cases on different lanes. Percentage-based fees are calculated on the value transferred in a message. USD-denominated fees are applied per message.
You can use the calculator below to learn the network fees for a specific token. Select the environment (mainnet/testnet), the token, the source blockchain, and the destination blockchain to get the network fee: --- # CCIP Architecture Source: https://docs.chain.link/ccip/concepts/architecture Last Updated: 2025-06-09 This section explains the core architecture of the Cross-Chain Interoperability Protocol (CCIP). Learn about the fundamental components and how they interact to enable secure cross-chain communication. - **[Overview](/ccip/concepts/architecture/overview)**: Get a high-level summary of the CCIP architecture. - **[Key Concepts](/ccip/concepts/architecture/key-concepts)**: Understand the essential terms and components within the CCIP ecosystem. - **[Onchain Components](/ccip/concepts/architecture/onchain)**: Explore the on‑chain components, including EVM smart contracts and Solana programs, and Aptos modules, that operate directly on blockchains. - **[Offchain Components](/ccip/concepts/architecture/offchain)**: Discover the offchain systems, like the Risk Management Network and Decentralized Oracle Network, that support CCIP operations. --- # CCIP Architecture - Overview Source: https://docs.chain.link/ccip/concepts/architecture/overview Last Updated: 2025-08-05 CCIP is a cross-chain messaging protocol built on Chainlink and secured by its decentralized oracle networks (DONs). It provides the following core capabilities: - **Token Transfers**: Transfer tokens to an externally owned account (EOA) or a receiving contract. - **Data Transmission**: Send arbitrary data (i.e., bytes) to a receiving contract. - **Programmable Token Transfers**: Transfer tokens along with instructions for handling them to a receiving contract (referred to as a programmable token transfer or PTT). CCIP supports several token transfer mechanisms—including Burn-and-Mint, Lock-and-Mint, and Lock-and-Unlock—using a common interface. Token developers can enable permissionless cross-chain transfers of their tokens via the Chainlink CCT (Cross-Chain Token) standard (detailed documentation is available here). Additionally, CCIP includes an independent Risk Management Network (RMN) that provides layered security through a defense-in-depth approach. ## High-level Architecture CCIP delivers cross-chain messages from a source chain to a destination chain by combining offchain consensus and onchain execution components. The architecture is composed of two primary layers: 1. **Offchain Architecture** - Decentralized Oracle Network (DON) with nodes that perform distinct roles: - Commit: Observes and validates source-chain events to build a consensus-based report. - Execution: Validates pending messages and optimizes them for execution on the destination chain. - Risk Management Network: - Operating independently from the main DON, the Risk Management Network "blesses" the committed messages offchain by generating independent attestations. This additional step enhances overall security with a defense-in-depth design. 2. **Onchain Architecture** - Router: - Each blockchain has a single immutable Router contract that serves as the unified interface for users and decentralized applications (dApps). - On the source blockchain, onramp functionality consists of: - Providing fee estimates. - Locking or burning tokens. - Dispatching the message (data and/or tokens). - On the destination blockchain, offramping functionality consists of: - Accepting and verifying commit reports from the Committing DON. - Validating Risk Management Network nodes signatures when receiving messages from the Executing DON. - Releasing or minting tokens to the receiver. - Routing the processed message to the designated receiver. **Note:** These high-level descriptions outline the core principles of CCIP. The detailed architectures for offchain and onchain architecture—and variations across implementations (e.g., on Solana, the Router incorporates OnRamp functionalities)—can be found in the relevant documentation. --- # CCIP Architecture - Key Concepts Source: https://docs.chain.link/ccip/concepts/architecture/key-concepts Last Updated: 2025-05-19 This page explains the high-level, chain-agnostic concepts behind Chainlink CCIP. We recommend reading these concepts before diving deep into the architecture pages. ## Prerequisites Before diving into Chainlink CCIP, ensure you understand these fundamental blockchain concepts: - **Accounts / Addresses**: Many blockchains distinguish between user-controlled addresses (called Externally Owned Accounts (EOAs) on EVM blockchains) and onchain program addresses (often called contract accounts in EVM ecosystems). Even if the name or the underlying account model differs, the principle is that a user's private key or authority controls one address (EOA) while the other is controlled by onchain code or logic (contract account). - **Smart Contracts**: On EVM-based blockchains, these are pieces of logic (often written in languages like Solidity) that get compiled into bytecode and deployed onchain. Other blockchains—such as SVM-based (e.g., Solana), **Aptos**, or **Sui**—may refer to these as "modules," "programs," or simply "onchain code." The basic idea is the same: executable code that lives on the blockchain and enforces rules without centralized control. - **Decentralized Applications (dApps)**: [dApps](https://ethereum.org/en/developers/docs/dapps/) are applications that use onchain logic to manage data and transactions. - **Token Standards**: Many chains use standards (like [ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) on EVM-based blockchains) or equivalents (e.g., [SPL tokens on Solana](https://spl.solana.com/token), the [Fungible Asset standard on Aptos](https://aptos.dev/en/build/smart-contracts/fungible-asset), or the [Coin standard on Sui](https://docs.sui.io/standards/coin)) to represent fungible tokens. - **Merkle Trees**: A data structure that summarizes and verifies large information sets with small cryptographic proofs. To learn more, try this [tutorial](https://ethereum.org/en/developers/tutorials/merkle-proofs-for-offline-data-integrity/). ## Blockchain Families When you see references to "smart contracts" or "onchain programs," they are executed within a virtual machine (VM). Different blockchains implement different VMs with unique design choices: - **EVM (Ethereum Virtual Machine)**: Used by Ethereum and many other EVM-compatible chains. Contracts are typically written in languages like [Solidity](https://docs.soliditylang.org/) or [Vyper](https://vyper.readthedocs.io/), compiled to EVM bytecode, and executed in a single-threaded environment. Read this [page](https://ethereum.org/en/developers/docs/evm/) to learn more. - **SVM (Solana Virtual Machine)**: Solana uses the Sealevel parallelization engine to execute onchain programs compiled to Berkeley Packet Filter (BPF) bytecode. This parallel processing approach can handle multiple simultaneous transactions. Read this [page](https://solana.com/news/sealevel---parallel-processing-thousands-of-smart-contracts) to learn more. - **Aptos**: The Aptos blockchain uses the Move language and its own implementation of the Move Virtual Machine (MoveVM). The MoveVM is designed for safety and verification, and Aptos processes transactions in parallel using its Block-STM execution engine. Read this [page](https://aptos.dev/en/network/blockchain/move) to learn more. - **Sui**: The Sui blockchain uses a variant of the Move language. Its architecture is object-centric and designed for high-throughput, parallel execution of transactions, enabling horizontal scaling. Read this [page](https://docs.sui.io/concepts/sui-move-concepts) to learn more. ## Cross-Chain dApps **Cross-chain dApps** are decentralized applications designed to run on—or interact with—multiple blockchains. By leveraging CCIP for cross-chain interoperability, these dApps can: - Execute transactions, send data, and transfer tokens between different blockchains. - Provide their users with access to features or liquidity across multiple ecosystems via a unified experience. - Specialize in the strengths of each underlying chain, such as high throughput, low fees, or specialized contract (or module) functionality. Because of this multi-chain support, cross-chain dApps can offer broader functionality than dApps confined to a single blockchain. ## Blockchain Finality **Blockchain finality** is the assurance that past transactions included on a given blockchain are extremely difficult or impossible to revert. Finality varies across different blockchains. Some blockchains offer instant finality, while others require multiple block confirmations. Finality is crucial for cross-chain transfers, both for arbitrary messaging and non-intents-based token transfers. It ensures that funds are released/minted or that any specified action is executed on the destination chain only after the source chain action (such as a lock or burn) is finalized. This minimizes loss of funds (in the case of token transfers) or unintended actions (in the case of arbitrary messaging) due to source chain reorganizations. To learn more about finality and how CCIP handles this, read the [CCIP Execution Latency](/ccip/ccip-execution-latency) conceptual guide. ## Lane A **lane** in CCIP is a conceptual unidirectional path from one specific blockchain (the source) to another (the destination). For example, the lane from Blockchain A to Blockchain B is distinct from the lane from Blockchain B to Blockchain A. This concept helps in configuring settings that apply uniquely to each path, such as rate limits or other parameters. For instance, sending a message from an EVM blockchain to an SVM blockchain may require different parameters compared to sending a message in the reverse direction. ## Node Operators Organizations with specialized expertise in running Chainlink nodes. Each operator can run multiple nodes across different decentralized oracle networks. ## Decentralized Oracle Network (DON) A **decentralized oracle network (DON)** consists of multiple independent Chainlink nodes. These nodes monitor and retrieve data from specified sources (e.g., a source chain), reach consensus off-chain, and post results to a blockchain (e.g., a destination chain) in a [trust-minimized manner](https://blog.chain.link/what-is-trust-minimization/). For more details on the DONs used in CCIP, refer to the CCIP Offchain architecture page. ## Risk Management Network The Risk Management Network is a unique architectural feature of CCIP that reinforces system security through a defense-in-depth approach based on established engineering principles such as N-version programming. On blockchains where RMN is enabled, a distinct set of node operators—separate from those of the core CCIP DONs—manages RMN functions. The RMN runs a minimal, separate implementation of the Chainlink node software using a different programming language than the primary CCIP system. This client diversity enhances robustness and minimizes external dependencies, thereby reducing the risk of supply chain attacks. On the blockchains where RMN is enabled, the RMN nodes independently verify and attest to source chain actions. The core CCIP components on the destination chain verify these attestations. For more details, refer to the following section. --- # Onchain Architecture Source: https://docs.chain.link/ccip/concepts/architecture/onchain Last Updated: 2025-09-03 This section details the onchain components of the CCIP architecture, covering both EVM-compatible chains and SVM-based chains such as Solana. - **[EVM Architecture](/ccip/concepts/architecture/onchain/evm)**: Learn about the onchain components specific to EVM environments. - **[SVM Architecture](/ccip/concepts/architecture/onchain/svm)**: Explore the onchain programs and components specific for SVM environments. - **[Aptos Architecture](/ccip/concepts/architecture/onchain/aptos)**: Understand the onchain components and message structures for Aptos environments. --- # CCIP Onchain Architecture (EVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/evm Last Updated: 2025-05-19 Chainlink CCIP's EVM onchain architecture consists of a set of smart contracts deployed on both source and destination chains. These components work together with CCIP's offchain infrastructure to provide end-to-end cross-chain interoperability. - **[Overview](/ccip/concepts/architecture/onchain/evm/overview)**: Provides a high-level introduction to CCIP's onchain architecture, including a component diagram, key roles, and a detailed walk-through of a message's lifecycle from source to destination chain. - **[Components](/ccip/concepts/architecture/onchain/evm/components)**: Describes each architectural component in detail. - **[Upgradability](/ccip/concepts/architecture/onchain/evm/upgradability)**: Explains CCIP's approach to secure system evolution. --- # Onchain Architecture - Overview (EVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/evm/overview Last Updated: 2025-05-19 CCIP's onchain architecture includes key smart contracts that receive and process cross-chain messages on the source and destination chains. A sender of a cross-chain message can be either a smart contract or an [externally owned account (EOA)](https://ethereum.org/en/developers/docs/accounts/#types-of-account). The interface for senders is the Router contract, which interacts with other internal CCIP contracts as described in the sections below. When the source chain CCIP contracts successfully process the cross-chain request, a unique message ID is returned to the sender. The offchain components listening to the events on the source chain process the message and submit it to the destination chain, where further verification and execution happen. The different components are described below. ## Key Components The following diagram illustrates the key onchain components: | Component | Ownership | Role | | ------------------------ | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Sender/Receiver | External (User/dApp) | dApp / EOA / smart contract that initiates the cross-chain message on the source chain and/or receives the message on the destination chain. | | Router | CCIP | The interface on a chain for all CCIP messages, which takes the form of a minimal, immutable contract. On the source chain, the sender calls it to send a cross-chain message. On the destination chain, it routes the message to the receiver contract. | | OnRamp | CCIP | A source chain contract that validates and processes messages. It interacts with other contracts for specific actions, such as fees, nonce management, or token handling. It also emits events that are listened to by the CCIP offchain components. | | OffRamp | CCIP | A destination chain contract that receives a committed message from the Chainlink DON and processes the message on the destination chain. It interacts with other contracts for specific actions, such as nonce management or token handling. | | Fee Quoter | CCIP | A contract that validates and computes fees for cross-chain messages. | | Nonce Manager | CCIP | A contract that implements message ordering by tracking nonces. | | Token Admin Registry | CCIP | A contract that contains the mapping of tokens to their token pools. | | Token Pool | External (Token Developer) | A contract that implements token handling mechanisms, such as Lock/Burn/Release, typically set up by the token Developer. There is one token pool per token on a given chain. | | Token Contract | External (Token Developer) | An ERC20 token contract owned by the token Developer | | RMN Contract (RMNRemote) | CCIP | Verifies Risk Management Network (RMN) signatures and handles cursing. | ## Typical Lifecycle of a Message ### Source Blockchain 1. **Preparation** - The Sender prepares a CCIP Message for a destination blockchain of choice. A CCIP Message includes the following information: - Receiver (EOA or smart contract) - Data payload - Tokens and amounts (if applicable) - Fee token - Extra Arguments (e.g., gas limit to use when calling the receiver on the destination chain) - The Sender calls `Router.getFee()` to receive the total fees to pay for using CCIP. Internally, the Router calls the OnRamp, which calls the Fee Quoter to get the fee. - The Sender approves the fee if the fee is not the native token. - The Sender calls the `Router.ccipSend()` function with the CCIP Message and the destination chain selector. For token transfers, the token amount to be transferred must be approved to the Router before this call is made. 2. **Sending** The Router receives the fee tokens and transfers them to the OnRamp: - The Router receives the tokens and transfers them to their corresponding Token Pools, which are retrieved from the Token Admin Registry. If the sender has not approved the tokens to the Router, the operation will fail. - The Router forwards the CCIP Message to the OnRamp for processing, which: - Validates the message by checking parameters, such as the number of tokens, gas limit, and data length. - Validates that the destination chain is not cursed. - For each token included in the Message, instructs the corresponding token pool to lock or burn the tokens, which also verifies the token pool rate limit for that lane. - A `messageId` is generated and returned to the Sender. - The OnRamp emits a `CCIPMessageSent` event containing the sequenced message. The Committing DON detects this event and processes the Message. 3. **Initial Offchain Processing** - The CCIP Commit DON monitors for the `CCIPMessageSent` event to process the Message offchain. More details about the offchain processing are available here. ### Destination Blockchain 1. **Commit Phase** - The final OCR report from the Committing DON is recorded onchain in the OffRamp contract via the `commit` function. This OCR report may include a mix of blessed and non-blessed roots and price updates from multiple source chains. - If the OCR report includes **blessed** merkle roots from RMN-enabled source chains, the OffRamp verifies the RMN node signatures onchain via the `RMNRemote` contract. - The OffRamp also validates that all **unblessed** merkle roots originate from the source chains where RMN is disabled. - The OffRamp emits a `CommitReportAccepted` event, confirming that a valid commit has been accepted. 2. **Secondary Offchain Processing** - The CCIP Executing DON monitors for the `CommitReportAccepted` event to identify commit reports with pending executions. All messages associated with these commit reports are collected and a merkle proof is computed for every message ready for execution. The execution plugin considers each message's gas limits and calldata size during the batching process. Note that the message batch executed by the Executing DON may be a subset of a Committing DON batch. The computed merkle proof is then included in the Execute Plugin Report. More details regarding the offchain architecture are available here. 3. **Execution Phase** - When the execution plugin submits the report, the OffRamp verifies the merkle proofs against the stored merkle roots. - The OffRamp performs validations, including ensuring that the source chain is not cursed. - If the CCIP Message includes tokens, the OffRamp retrieves the relevant token pool from the Token Admin Registry and calls the Token Pool's `releaseOrMint` function. This function validates token pool rate limits, releases or mints the tokens, and transfers them to the specified receiver. - If the CCIP Message contains an arbitrary bytes payload, the OffRamp calls the Router to deliver the CCIP Message to the Receiver. - The OffRamp emits a final event, `ExecutionStateChanged`, containing the execution state. This state corresponds to one of the following numerical values: - `0`: `UNTOUCHED` - `1`: `IN_PROGRESS` - `2`: `SUCCESS` - `3`: `FAILURE` --- # Onchain Architecture - Components (EVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/evm/components Last Updated: 2025-05-19 This section provides more detail on the Onchain components. The [API Reference page](/ccip/api-reference/evm) contains the interface functions and revert reasons. ## Sender/Receiver **CCIP supports the following as senders and receivers:** - An externally owned account (EOA) - A smart contract or smart account **CCIP messages can be sent in any of the following combinations:** - EOA → EOA - EOA → Smart Contract - Smart Contract → EOA - Smart Contract → Smart Contract **Depending on a dApp's architecture, an EOA may interact with the Router using:** - A frontend or middleware component (e.g., a JavaScript program) - A sender smart contract **A CCIP Message can include:** - An arbitrary bytes payload - A token transfer - A programmable token transfer **Sender Responsibilities:** - Prepare a structured CCIP Message. - Retrieve a fee estimate from the Router. - Call the Router to send the message, as described in the Message Lifecycle section. **Receiver Considerations:** - **Data Processing:** If the CCIP Message contains a bytes payload or a programmable token transfer, the receiver must be a smart contract capable of processing the data. Messages sent to an EOA will not deliver the payload. - **Function Implementation:** The receiver should implement the `ccipReceive()` function using the `IAny2EVMMessageReceiver` interface. The Router is the only contract authorized to call this function. **Additional Resources:** - CCIP provides smart contract examples for a Sender/Receiver in the [Applications folder](https://github.com/smartcontractkit/chainlink-ccip/tree/release/contracts-ccip-1.6.2/chains/evm/contracts/applications). For most use cases, consider implementing try-catch mechanisms using a defensive receiver. More details are available [here](https://github.com/smartcontractkit/chainlink-ccip/blob/release/contracts-ccip-1.6.2/chains/evm/contracts/applications/DefensiveExample.sol). ## Router The Router serves as the single interface for a sender or interfacing dApp on the source chain for all CCIP messages. As a minimal, immutable contract, there is only one Router contract per chain. The Router exposes two primary functions for the sender: - `getFee()`: Retrieves the CCIP fee for a given message. - `ccipSend()`: Sends a cross-chain message and returns a unique message ID to the sender. For function signatures and revert reasons, see the [API reference docs](/ccip/api-reference/evm). On the destination chain, if the CCIP Message contains arbitrary data (or data from a programmable token transfer), the Router routes the message from the OffRamp to the receiver contract. Once the receiving smart contract successfully processes the message, the Router emits a `MessageExecuted` event. ## OnRamp The OnRamp is an internal CCIP smart contract and is not meant to be user-facing. It operates on the source chain and is the primary contract that the Router calls to process a message. In previous versions of CCIP, there was an OnRamp per destination chain; with the latest release, a single OnRamp on a chain handles messages for any destination chain. When the Router forwards a `ccipSend()` request to the OnRamp, the contract performs the following actions: 1. **Validations** - Performs required validations before processing the message, such as verifying that the receiver address is valid. 2. **Token Transfer Processing** - If the message involves token transfers, it retrieves the correct Token Pool from the Token Admin Registry. - Initiates calls to lock or burn the token, based on the token handling mechanism. 3. **Nonce Management** - Uses the Nonce Manager to ensure messages requiring in-order execution are processed in the correct order. 4. **Message ID Generation** - Returns a unique message ID to the Router. 5. **Event Emission** - Emits a `CCIPMessageSent` event containing the message ID, source and destination chain information, sender, receiver, and other key message details. ## OffRamp The OffRamp is an internal CCIP smart contract that operates on the destination chain. It is the primary contract that the CCIP DONs call to process incoming messages. ### Commit Phase During the **Commit Phase**, the following steps occur: 1. **Commit Report Submission** - The Committing DON calls the `commit()` function on the OffRamp with a Commit Report that includes messages and/or price reports. 2. **Validation of RMN Blessing** - If the Commit Report includes blessed merkle roots from RMN-enabled source chains, the OffRamp verifies the RMN node signatures onchain via the `RMNRemote` contract. - If the Commit Report does not include RMN signatures, the OffRamp validates that all unblessed merkle roots originate from source chains where RMN is disabled. 3. **Price Report Staleness Check** - The OffRamp validates the staleness of price reports before forwarding the price to the FeeQuoter. 4. **Cursed Source Chain Check** - The OffRamp verifies the RMN status to ensure that messages from a cursed source chain are blocked. 5. **Event Emission** - At the end of the Commit Phase, the OffRamp emits a `CommitReportAccepted` event, which the Execution plugin monitors. ### Execution Phase In the **Execution Phase**, the OffRamp processes the message for final execution: 1. **Merkle Proof Verification** - The OffRamp verifies the merkle proofs included in the Execution Reports against the committed merkle roots. 2. **Additional Validations** - The OffRamp performs validations, including ensuring that the source chain is not cursed. 3. **Token Processing (if applicable)** - If the CCIP Message includes tokens, the OffRamp retrieves the relevant Token Pool from the Token Admin Registry and calls the Token Pool's `unlock/mint` function. This function validates token pool rate limits, unlocks or mints the tokens, and transfers them to the specified receiver. 4. **Message Delivery** - If the CCIP Message contains arbitrary data, the OffRamp uses the Router to deliver the CCIP Message to the Receiver. 5. **Nonce Management** - For ordered messages (i.e., messages with a non-zero nonce), the OffRamp interacts with the Nonce Manager to ensure inbound messages are processed sequentially. 6. **Final Execution Status** - The OffRamp sets the message's final execution status and emits a final ExecutionStateChanged event, indicating either a "Success" or "Failure" state. **Permissionless Manual Execution (Fallback):** If execution fails—due to insufficient gas limit or a logical error in the receiver smart contract—the message can be manually executed by the user or dApp by directly interacting with the OffRamp. Read the [manual execution](/ccip/concepts/manual-execution) documentation for more details. ## FeeQuoter The FeeQuoter is an internal CCIP smart contract that calculates and returns CCIP fees on the source chain. - **Fee Calculation** - When the Router's `getFee()` function is called, the request is forwarded to the FeeQuoter, which estimates and returns the CCIP fee. - **Price Management** - Maintains token and gas prices in USD - Enforces price staleness rules - Calculates all cross-chain fees based on current pricing data - Stores token-specific fee configurations - **Price Updates** - When a Commit Report contains price updates, the FeeQuoter on the destination chain is updated accordingly. For additional details on how CCIP fees are calculated, refer to the [CCIP Billing page](/ccip/billing). ## NonceManager The NonceManager helps order messages in CCIP by tracking outbound nonces on the source blockchain and inbound nonces on the destination blockchain. It ensures strict ordering when the message's extraArgs parameter requires ordering, thus providing a flexible design. 1. **Ordered Messages** - **Non-zero Nonces:** When an OnRamp identifies a message that must preserve ordering, it increments and assigns a non-zero outbound nonce. - **Inbound Validation:** On the destination blockchain, the OffRamp checks that the incoming message's nonce matches the expected inbound nonce. If there is a mismatch, the message is skipped or deferred for later retry. 2. **Out-of-Order Messages** - **Zero Nonces:** For messages marked "out of order," the OnRamp sets the nonce to **0**. - **No Sequence Checks:** Because `nonce == 0` indicates no ordering, the OffRamp does not validate or increment inbound nonces. These messages can execute immediately without waiting for earlier messages. ## Token Admin Registry The Token Admin Registry is a user-facing CCIP smart contract that maintains a one-to-one mapping between token addresses and their corresponding token pool addresses on a given chain. The OnRamp and OffRamp contracts use the Token Admin Registry to retrieve a token's configured token pool address to call the appropriate functions: - **On the source blockchain:** Lock/Burn tokens - **On the destination blockchain:** Release/Mint tokens The `setPool()` method can be invoked by a registered CCIP token administrator via the Registry Module. See the [CCT (Cross-Chain Token)](/ccip/concepts/cross-chain-token/evm) documentation for more details on this interaction. ## Tokens and Token Pools **Tokens:** - Tokens are developed by token developers and exist independently of the core CCIP contracts. - Most ERC20 tokens are compatible with CCIP. For more information on compatibility, see [CCT Compatibility](/ccip/concepts/cross-chain-token/evm/tokens). **Token Pools:** - Token Pools are external contracts that interact with token contracts. The OnRamp/OffRamp calls them to perform operations such as burning, minting, locking, or releasing tokens. - Most token pools follow standard models (Lock/Release and Burn/Mint), with audited code available in the CCIP repository. - For tokens requiring bespoke logic before burn/mint/lock/release, custom pools can be built on top of the base pools. More details are available in the [CCT Pool Types](/ccip/concepts/cross-chain-token/evm/token-pools#standard-token-pools) and [Custom Pools](/ccip/concepts/cross-chain-token/evm/token-pools#custom-token-pools). ## RMN Contract Risk management in CCIP is performed by a separate, independent network that continuously monitors and validates cross-chain operations for anomalous activity, thereby providing an additional layer of security. With the latest CCIP release, the Risk Management Network (RMN) blessing occurs offchain. In this process, the Committing DON interacts offchain with RMN nodes to obtain a blessing, and the resulting RMN node signatures are included in the Commit Report posted on the OffRamp at the destination chain. The RMN Contract is deployed on every chain where CCIP is integrated, even on chains where RMN is not enabled. Its key functions include: 1. **Blessing Verification** - **Verify function**: The RMNRemote contract's `verify()` function is used by the OffRamp to verify RMN signatures for messages originating from RMN-enabled source chains. 2. **Cursing Mechanism** - **Curse Initiation**: When the CCIP Owner manually initiates a curse, the `curse()` function is invoked to mark the appropriate subjects as cursed. - **Curse Detection**: Onchain components (such as the Router, OnRamp, OffRamp, and TokenPool) call the `isCursed()` function on the RMNRemote contract to detect global curses or curses targeting a remote chain. --- # Onchain Architecture - Upgradability (EVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/evm/upgradability Last Updated: 2025-05-19 Chainlink Cross‐Chain Interoperability Protocol (CCIP) is designed to evolve in response to new emerging feature requests, security considerations, and the need to support additional blockchains over time. This requires a secure upgrade process that preserves CCIP's robust security while allowing for iterative improvements. **Note**: The Router contract is intentionally immutable. It remains the primary user entry point on both source and destination blockchains—unmodifiable to preserve reliability, stability, and developer predictability over time. ## What Can Be Upgraded Upgradability in CCIP primarily refers to two categories of changes: 1. **Onchain Configuration** - Many CCIP contracts expose public setter functions that can adjust operational parameters. - Since these parameters can be adjusted without requiring the redeployment of an entire contract, it offers a flexible way to help ensure CCIP's security and reliability over time. 2. **Redeploying Contracts and Redirecting References** - Once a smart contract is deployed, its code cannot be modified. If a new contract version (e.g., OnRamp, OffRamp, or Token Pool) is required, a new updated contract can be deployed, with all existing references updated to point to the new contract address. - For example, an OffRamp address might be updated in the Router or a local mapping so that new inbound messages go through the upgraded contract. - This approach ensures older versions remain stable while new versions can be phased in. ## Implementation Process All onchain configuration changes must pass through a Role-Based Access Control Timelock (RBACTimelock) contract. This mechanism ensures that: 1. **Proposals** - All proposals originate from a ManyChainMultiSig (MCMS), which requires multiple independent signers to sign off. - Signers are selected from multiple high-quality Chainlink node operators with a proven, multi-year track record of securing billions in value within the Chainlink Network, as well as from Chainlink Labs. - Signers are also spread across multiple different geographic locations globally and may be rotated on a periodic basis to help mitigate potential risks as they arise, such as geographic concentration. - Two distinct paths exist for a proposal to succeed: - **Time-locked review:** Node operators securing CCIP can veto a proposal within a defined review period. If no veto occurs, the update proceeds. - **Explicit approval:** A quorum of independent signers (including node operators) actively endorse the proposal, allowing for urgent or time-sensitive fixes. 2. **Review and Veto Window** - During the timelock review period, CCIP node operators can inspect the onchain proposal (e.g., adjusting a rate limit) and reject it if it appears incorrect, preventing the proposal from being executed. 3. **Execution** - Once the timelock finishes (with no veto), the proposal transitions to an "executable" state. - Any party can call the timelock contract to finalize the proposal (e.g., via a [timelock-worker](https://github.com/smartcontractkit/timelock-worker) script). - The timelock then calls the target CCIP contracts with the specified changes. 4. **Public Verifiability** - Information about the MCMS, including the timelock configuration, signer set, and ongoing timelocked proposals are visible onchain. - For instance, the Ethereum mainnet MCMS can be viewed on [Etherscan](https://etherscan.io/address/0xE53289F32c8E690b7173aA33affE9B6B0CB0012F#readContract). - Anyone can track in-progress proposals, track any node operators vetos, and monitor the final execution status using onchain data. ## Additional Resources - **[CCIP Owner Contracts - GitHub](https://github.com/smartcontractkit/ccip-owner-contracts)**: Further documentation and source code for the ManyChainMultiSig, timelock, and other related contracts. - **[Etherscan: MCMS on Ethereum Mainnet](https://etherscan.io/address/0xE53289F32c8E690b7173aA33affE9B6B0CB0012F#readContract)**: Allows anyone to see the current configuration, pending proposals, and signer addresses on Ethereum. - **[Timelock Worker Repo](https://github.com/smartcontractkit/timelock-worker)**: Demonstrates how to automate final execution for proposals that have cleared the timelock period. --- # CCIP Onchain Architecture (SVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/svm Last Updated: 2025-05-19 Chainlink CCIP's SVM onchain architecture consists of specialized Solana programs deployed on both source and destination chains. These components work together with CCIP's offchain infrastructure to provide end-to-end cross-chain interoperability. - **[Overview](/ccip/concepts/architecture/onchain/svm/overview)**: Provides a high-level introduction to CCIP's SVM-based architecture, including component diagrams, key roles, and a detailed walk-through of a message's lifecycle from source to destination chain. - **[Components](/ccip/concepts/architecture/onchain/svm/components)**: Describes each architectural component in detail. - **[Upgradability](/ccip/concepts/architecture/onchain/svm/upgradability)**: Explains CCIP's approach to secure system evolution. --- # Onchain Architecture - Overview (SVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/svm/overview Last Updated: 2025-05-19 On a Solana Virtual Machine (SVM)-based source blockchain, an onchain program or user wallet interacts with the CCIP Router program, which provides a standard interface for sending messages cross-chain. The Router internally calculates fees, locks or burns tokens via a Token Pool program, and then emits a CCIPMessageSent event. The Committing DON observes that event offchain and relays the messages to a destination blockchain. On an SVM-based destination blockchain, an onchain program called the OffRamp receives a commit from the Committing DON containing merkle roots of batched messages. The OffRamp verifies the OCR signatures, checks with the Risk Management Network (RMN) Remote program that the source chain is not cursed, and then stores the merkle root. Later, the Executing DON submits individual messages for execution, one per transaction (unlike EVM chains, which can execute in batches). The OffRamp verifies each message's merkle proof against the previously committed root, tracks the execution state, and processes the message. This includes making CPI calls to Token Pools to release or mint tokens and, if applicable, delivering message data to a receiver program that implements the CCIP Receive interface. ## Key Components **Source Chain**: **Destination Chain**: | Component | Ownership | Role | | ----------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Sender | External (User/Program) | The sender could be a wallet or onchain program. It initiates the cross-chain request by calling the Router program's `ccip_send` instruction. | | Router | CCIP | The Router program is the entry point for sending cross-chain messages from SVM-based blockchains (outbound only). It validates accounts, calculates fees via the Fee Quoter program, collects fees, manages outbound sequence numbers, and transfers tokens to Token Pools when sending tokens. | | OffRamp | CCIP | Receives and processes cross-chain messages on the destination blockchain. It commits message batches (via merkle roots), executes individual messages, verifies RMN curses, and initiates token releases/minting through Token Pools. | | Fee Quoter | CCIP | Computes gas and token fees for cross-chain messages and maintains updated price data for tokens and destination chain gas costs. | | Token Pools | External (Token Developer) | Specialized programs that handle cross-chain token transfers. Used by the Router for `lock_or_burn_tokens` operations on the source blockchain and by the OffRamp for `release_or_mint_tokens` operations on the destination blockchain. | | Token | External (Token Developer) | A token program | | Receiver | External (User/Program) | A wallet or onchain program. If the receiver is a program implementing the `ccip_receive` function, it can handle data and/or tokens, but can only receive tokens to a PDA it has authority over. If it's a wallet, it can only receive tokens through its Associated Token Accounts (ATAs). | | Risk Management Network (RMN) | CCIP | An onchain program that maintains a registry of "curses" (blocklisted chains). Router, OffRamp, and Token Pools all verify with this program that relevant chains are not cursed before processing messages. | ## Typical Lifecycle of a Message ### Source Blockchain (SVM) When initiating a CCIP transaction from a SVM-based source chain. 1. **Preparation** - The Sender prepares a CCIP Message on a SVM-based chain to a destination blockchain of choice. A CCIP message includes the following information: - **Receiver**: A byte array representing the destination address (e.g., Ethereum account). - **Data payload**: Arbitrary bytes to be delivered to the receiver. - **Tokens and amounts** (if applicable) - **Fee token**: A Solana `Pubkey` for the token used to pay fees or default `PubKey` for the native gas token. - **Extra Arguments** (e.g., gas limit to use when calling the receiver on a EVM compatible blockchain). - The Sender calls the Router `get_fee` to receive the total fees for paying CCIP. Internally, the Router calls the Fee Quoter program to determine the fees. - If the fee token is not the native gas token (e.g., LINK): - **For wallet senders**: The sender must include their Associated Token Account (ATA) as a writable account in the transaction and sign it, granting the Router one-time permission to transfer the fee tokens to the fee receiver ATA. - **For program senders**: The sender program must call the SPL token `approve` instruction to authorize the Router's fee billing signer PDA to transfer tokens from the program's token account to the fee receiver ATA. - **Note**: The fee receiver ATA is a token account owned by the Router's fee billing signer PDA that accumulates CCIP fees. There is a separate fee receiver ATA for each supported fee token. - The Sender calls the Router `ccip_send`, with: - The destination chain selector. - The CCIP message (as detailed above). - For token transfers: An ordered list of indexes that tell the Router which accounts in the transaction correspond to which tokens being transferred. - All required accounts in the transaction context, including: 1. Router configuration accounts. 2. RMN accounts that are required for the curse verification. 3. Destination chain state accounts. 4. User nonce account for sequence tracking. 5. Fee token accounts. 6. For each token being transferred: token accounts, pool accounts, and token-specific configuration accounts. - **Note**: SVM's architecture requires explicitly specifying all accounts a transaction will interact with. Developers should consult the [API reference](/ccip/api-reference/svm) for complete details on required accounts and recommended patterns for constructing `ccip_send` transactions. 2. **Sending** - Verifications - The Router verifies with the RMN program that the destination chain is not cursed and that the system-wide emergency (global curse) is not activated. If either check fails, the transaction reverts. - The Router verifies that all token-related accounts in the transaction are legitimate, correctly structured, and properly connected to each other (e.g., user token accounts are proper ATAs, pool accounts are derived from the correct seeds, etc.). - Fee Collection - The Router makes a CPI call to the Fee Quoter program to calculate the exact fee amount based on message size, token transfers, and destination chain gas cost. - For native gas fee token: The Router transfers native gas tokens from the sender's account to the fee receiver's wrapped native token ATA, then issues a synchronize instruction to update the token account balance. - For non-native fee tokens: The Router transfers tokens from the sender's Associated Token Account (ATA) to the fee receiver's ATA using the Router's fee billing signer PDA as the authority for the transfer. - Sequence Management - The Router increments and records the destination chain's message sequence number. - It updates the sender's nonce value, which is used to maintain message ordering within a specific lane (source + destination chain pair) for this sender. - Token Handling (if applicable) for each token being transferred - The Router transfers tokens from the user's ATA to the corresponding token pool ATA. - The Router makes a CPI call to the token pool `lock_or_burn_tokens` function to lock or burn tokens, which also verifies token pool rate limits for the specific destination chain. - A unique message ID is generated. - The Router emits a `CCIPMessageSent` event with the full message details. - The message is returned to the Sender. 3. **Initial Offchain Processing** - The CCIP Commit DON monitors for the `CCIPMessageSent` event to process the message offchain. More details about the offchain processing are available here. ### Destination Blockchain (SVM) When processing a CCIP transaction on a SVM-based destination chain: 1. **Commit Phase** - The final OCR report from the Committing DON is recorded onchain in the OffRamp program via the `commit` function. This report contains information from a single source chain and includes a merkle root of messages along with optional token and gas price updates. - The OffRamp first validates the report's signatures and consensus requirements, ensuring that sufficient CCIP nodes have signed and that sequence numbers are valid and in order. - The OffRamp makes a CPI call to the RMN program, checking that the source chain is not cursed and that the system-wide emergency (global curse) is not activated. - The OffRamp verifies and stores the merkle root, which represents a batch of messages from the source chain. - If the report contains price updates, the OffRamp calls the Fee Quoter program to update token and gas price data. - Upon successful processing, the OffRamp emits a `CommitReportAccepted` event containing the merkle root and any price updates, confirming that the messages are now committed and available for execution. 2. **Secondary Offchain Processing** - The CCIP Executing DON monitors for the `CommitReportAccepted` event to identify commit reports with pending executions. Each message associated with these commit reports is processed individually. For each message ready for execution, the Executing DON computes its specific merkle proof against the committed merkle root. Each message is then submitted in a separate transaction to the SVM chain, with the message's merkle proof included in the Execute Plugin Report. The execution plugin implementation for SVM chains executes one message per transaction. 3. **Execution Phase** - When the execution plugin submits a report, it calls the Offramp program's `execute` function, which verifies the merkle proof against the stored merkle root. **Note**: Unlike EVM implementations, the SVM Offramp verifies a single message execution at a time, with each execution report containing proof specifically for that message. - The OffRamp performs several validations: - Verifying the message sequence number is correct. - Making a CPI call to the RMN program to verify that the source chain is not cursed. - Checking that the message hasn't already been executed successfully. - If the message includes token transfers, the OffRamp makes a CPI call to the token pool `release_or_mint_tokens` function to release or mint the corresponding tokens to the intended Receiver's ATA. This call also verifies token pool rate limits for the specific source chain. - If the message contains arbitrary data, the OffRamp makes a CPI call to the Receiver program to deliver the CCIP message. - Upon successful execution, the OffRamp emits an `ExecutionStateChanged` event containing the source chain selector, sequence number, message ID, message hash, and execution state (2 for `"SUCCESS"`). - **Note**: In SVM transaction model, a failed execution transaction will revert all state changes. So if execution fails, the message remains in its original state rather than being marked as `"FAILED"`. - If automated execution fails, the OffRamp program provides a `manually_execute` function that can be permissionlessly called after a configured time period has passed. This ensures messages can still be executed even if the primary execution path encounters issues. For more information, read the [manual execution](/ccip/concepts/manual-execution) page. --- # Onchain Architecture - Components (SVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/svm/components Last Updated: 2025-05-19 This section provides more detail on the Onchain components. ## Sender/Receiver **CCIP supports the following as senders and receivers**: - A user wallet. - An onchain program. **CCIP messages can be sent in any of the following combinations**: - Wallet → Wallet (or an EOA if the destination is an EVM-based blockchain). - Wallet → Program (or a smart contract if the destination is an EVM-based blockchain). - Program → Wallet (or an EOA if the destination is an EVM-based blockchain). - Program → Program (or a smart contract if the destination is an EVM-based blockchain). **Depending on a dApp's architecture, a wallet may interact with the Router using**: - A frontend or middleware component (e.g., a JavaScript program). - A sender program. **A CCIP Message can include**: - An arbitrary bytes payload. - A token transfer. - A programmable token transfer (data + tokens). **Sender Responsibilities**: - Prepare a structured CCIP Message. - Include all required accounts in the transaction context, due to the SVM account model. To learn more, read the `ccip_send` [API reference](/ccip/api-reference/svm/v1.6.0/router). - Retrieve a fee estimate from the Router. - Call the Router to send the message, as described in the Message Lifecycle section. - Authorization Mechanisms: - For fee payments: - When paying with native gas token (e.g., SOL): No special authorization is needed as the Router will use a system transfer instruction. - When paying with SPL tokens (e.g., wSOL, LINK): - Wallet senders: Must include their fee token account (ATA) as a writable account and sign the transaction. - Program senders: Must call the SPL token `approve` instruction to authorize the Router's fee billing signer PDA to transfer tokens. - For token transfers (if applicable): - Wallet senders: Must include their token accounts (ATAs) as writable accounts and sign the transaction. - Program senders: Must call the SPL token `approve` instruction to authorize the Router's token pools signer PDA to transfer tokens. **Receiver Considerations**: - **Data Processing**: If the CCIP Message contains a bytes payload or a programmable token transfer, the receiver must be a program implementing the `ccip_receive` function with a specific discriminator. To learn more, read the `ccip_receive` [API reference](/ccip/api-reference/svm/v1.6.0/messages). - **Security Validation**: The receiver program should validate that the caller is an authorized OffRamp by checking the `ALLOWED_OFFRAMP` PDA from the Router program. **Additional Resources**: - CCIP provides program examples for a Sender/Receiver in the [Programs folder](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs). ## Router The Router program serves as the entry point for sending cross-chain messages from SVM-based blockchains. 1. **Key Functions** - `get_fee`: Retrieves the CCIP fees for a given message by calling the Fee Quoter program. - `ccip_send`: Sends a cross-chain message, emits a CCIPMessageSent event, and returns a unique message ID. 2. **Key Responsibilities** - **Account Validation**: - Verifies that all provided accounts are properly owned by expected programs. - Checks that token accounts are valid Associated Token Accounts (ATAs). - Ensures token pool accounts are derived from the correct seeds with proper authority. - Validates that all required configuration accounts are included. - **Sequence Management**: - Maintains outbound sequence numbers for message ordering. - Uses a PDA to store nonce values for each lane (sender-destination chain pair). - Initializes the nonce PDA if needed (first-time senders pay the rent for this account). - Updates the nonce value when sequential ordering is required. - **Fee Collection**: - Calculates the exact fee amount by making a CPI call to the Fee Quoter program. - For native gas tokens: Directly transfers tokens from the Sender's account to the fee receiver wrapped native ATA, then issues a synchronize instruction. - For non-native tokens: Transfers tokens from the sender's ATA to the fee receiver's ATA. - **Token Handling (if applicable)**: - Transfers tokens from the sender ATA to the corresponding token pool ATA. - Calls the token pool `lock_or_burn_tokens` function, which also verifies token pool rate limits for the specified destination chain. - **Security Verification**: - Checks with the RMN program that chains are not cursed. ## OffRamp The OffRamp program operates on the destination SVM chain and processes incoming cross-chain messages. It has two distinct phases: Commit and Execute. 1. **Key Functions** - `commit`: Receives and verifies commit reports from the Committing DON containing merkle roots of message batches. - `execute`: Processes individual messages with their merkle proofs against previously committed roots. - `manually_execute`: Provides a fallback mechanism for permissionlessly executing messages after a waiting period. Read the manual execution page to learn more. 2. **Key Responsibilities** 1. **Commit Phase** - Validates nodes' signatures from the Committing DON. - Verifies with the RMN program that the source chain is not cursed. - Stores the merkle root. - Processes optional price updates by calling the Fee Quoter program. - Emits a `CommitReportAccepted` event. 2. **Execute Phase** - Processes one message per transaction. - Verifies the merkle proof against the stored Merkle root. - Sequence Management: - Tracks and validates message sequence numbers to ensure proper ordering. - For sequential messages (non-zero nonce), checks that the message's nonce matches the expected inbound nonce. - If a nonce mismatch is detected, skips the message until the correct sequenced message is received. - Updates the nonce PDA after successful execution to maintain sequence integrity. - **Token Handling (if applicable)**, for each token: - Verifies that token pool addresses are correct by checking with the Router's token admin registry PDA, which maps token mints to an Address Lookup Table (ALT) containing the approved token pool accounts. - Records initial token balances of receiver ATAs. - Makes CPI call to the corresponding token pool `release_or_mint_tokens` function to mint or release tokens to the receiver token account (ATA). - Verifies that post-execution token balances match expected increases. - **Data Delivery (if applicable)**: - Verifies that the Receiver program ID matches what was specified in the original message by including the program ID in the merkle proof verification. - Verifies that all accounts match exactly those specified in the original message on the source chain—any mismatch will cause execution to fail, potentially resulting in blocked funds. - Creates an instruction with: - The `ccip_receive` discriminator `[0x0b, 0xf4, 0x09, 0xf9, 0x2c, 0x53, 0x2f, 0xf5]`. - The serialized `Any2SVMMessage` struct (containing the message ID, source chain selector, sender, data payload and token amounts). Read the API reference to learn more. - Makes a CPI call to the receiver program using the accounts specified in the original message. - **Completion**: - Marks the message as “Success” in the commit report's state tracking. - Emits an `ExecutionStateChanged` event with details, including the source chain selector, sequence number, message ID, message hash and the new state. - **Note**: If any part of the execution process fails, the entire transaction will revert. This means no state changes are persisted, no tokens are transferred, and no events are emitted. The message will remain in the previous state, allowing for retry. 3. **Manual Execution (fallback mechanism)** - Allows any user to permissionlessly execute messages through the `manually_execute` function after a waiting period set by the CCIP admin. - This mechanism primarily addresses where automated execution fails due to: - Temporary network congestion. - Messages requiring more compute units than allocated in the transaction. - Important points about compute units: - Manual executors can allocate more computer units to their transactions. - No modification of the message itself is needed: The same message and execution report are used. - The `compute_units` field in the message specifies the compute units allocated to the Receiver's program `ccip_receive` execution. During manual execution, users can allocate more compute units to their overall transaction, potentially allowing messages that exceeded compute limits during DON execution to complete successfully. - Manual execution follows exactly the same verification and execution process as the Execute Phase: - Validate the source chain is not cursed via a CPI call to the RMN program. - Validates the merkle proof against the committed root. - Verifies accounts. - Processes token transfers and data delivery. - Updates message state and emits `ExecutionStateChanged` event. - **Note**: This mechanism cannot fix fundamental issues with the CCIP message payload. If accounts are incorrectly specified in the source chain, even manual execution will fail and funds may remain locked. ## FeeQuoter The FeeQuoter is a central component in Chainlink CCIP that maintains token and gas prices in USD, enforces rules around price freshness, and calculates all cross-chain fees. 1. **Key Functions** - `get_fee`: Calculates the total fee required to send a CCIP message. - `update_prices`: Updates token and gas prices based on data from commit reports. 2. **Key Responsibilities** - **Fee Calculation**: - Computes execution costs based on destination chain parameters. - Converts costs to USD-denimated rates. - The CCIP Billing page provides further details on how CCIP fees are calculated. - **Price Management**: - Maintains token and gas prices in USD. - Enforces price freshness rules. - Stores token-specific fee configurations. ## Tokens and Token Pools ### Tokens - Tokens are developed by token developers and exist independently of the core CCIP programs. - Most SPL tokens are compatible with CCIP. For more information on compatibility, read the CCT documentation. ### Token Pools - Token Pools are deployed by token developers and exist independently of the core CCIP programs. - Token Pools are programs that interact with SPL token programs. - Token pools follow standard models (Lock/Release and Burn/Mint), with audited code available in the [CCIP repository](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs). - For tokens requiring bespoke logic before burn/mint/lock/release, custom pools can be built on top of the base pools. More details are available in the CCT documentation. ## Risk Management Network The Risk Management Network (RMN) adds an additional security layer to CCIP by performing offchain risk validation and maintaining an onchain "cursing" mechanism. The RMN program enables verification of cross-chain messages and can halt message transmission for specific blockchains or globally when security threats are detected. ### Blessing and Verification - Offchain RMN nodes generate ECDSA signatures over merkle roots representing cross-chain message batches. - CCIP programs (such as Router or OffRamp) make CPI calls to the `verify_not_cursed` function to check whether the relevant chain (source or destination) is cursed. - The RMN program checks its `Curses` account to verify the subject isn't cursed before allowing the transaction to proceed. ### Cursing Mechanism - The RMN program lets an owner curse any blockchain (by chain selector) or declare a global curse through the `curse` instruction that halts all CCIP traffic. - Once cursed, any CCIP transaction that performs the verification check for that chain selector (or the global curse) will revert, preventing cross-chain message processing. - The owner calls `uncurse` to lift the curse when the threat is resolved, restoring normal CCIP operations. --- # Onchain Architecture - Upgradability (SVM) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/svm/upgradability Last Updated: 2025-05-19 Chainlink's Cross-Chain Interoperability Protocol (CCIP) is designed to evolve in response to new security considerations, emerging feature requests, and the need to onboard additional blockchains. This evolution requires a secure, transparent upgrade process that preserves users' trust in CCIP while allowing for iterative improvements. ## What Can Be Upgraded On SVM-based blockchains, upgradability primarily happens in two ways: 1. **Onchain Configuration** Many CCIP programs (e.g., Router, OffRamp) offer public functions that allow for parameter updates without redeploying the entire program. Examples might include: - Adding or removing supported blockchains. - Enabling a new fee token. Because these modifications only change onchain data, operational parameters can be adjusted without redeploying an entire program. 2. **Program Code Upgrades** In SVM-based blockchains, programs are mutable by default (unless the upgrade authority is removed after deploying them). This allows the same program ID to point to new code if an upgrade is published. Developers typically use this mechanism to: - Patch vulnerabilities or correct unforeseen implementation errors. - Introduce new features or improved logic for cross-chain messaging. Once the program code is upgraded, external references (such as PDAs or user accounts) are not broken because they rely on the original program ID. **Special case:** Unlike other CCIP components, the OffRamp program follows a different upgrade pattern. Instead of upgrading in place, new OffRamp program instances are deployed when upgrades are needed. This approach ensures backward compatibility with in-flight messages that need to be processed by the correct OffRamp version. ## Implementation Process All security-critical onchain configuration changes to CCIP on Solana pass through a secure upgrade process using the ManyChainMultiSig (MCMS) and Timelock programs. Any proposal must follow one of two paths: 1. **Time-locked Review**: The proposal is submitted to the Timelock program and enters a mandatory review period. During this window, node operators securing CCIP can veto the proposal. If no veto occurs, the proposal becomes executable after the delay expires. 2. **Expedited Approval**: The proposal receives explicit approval from a quorum of independent signers, providing an alternative path for time-sensitive circumstances. Any onchain update that passes the timelock review period without a veto becomes executable and can be implemented by accounts with the Executor role calling the `execute_batch` instruction. The MCMS program's configuration and all scheduled operations are stored in public accounts that anyone can inspect for transparency and verification. --- # CCIP Onchain Architecture (Aptos) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/aptos Last Updated: 2025-09-03 Chainlink CCIP's Aptos onchain architecture consists of specialized Move modules deployed on both source and destination chains. These modules work together with CCIP's offchain infrastructure to provide end-to-end cross-chain interoperability. - **[Overview](/ccip/concepts/architecture/onchain/aptos/overview)**: Provides a high-level introduction to CCIP's Aptos-based onchain architecture, including a component diagram, key roles, and a detailed walk-through of a message's lifecycle from source to destination chain. - **[Components](/ccip/concepts/architecture/onchain/aptos/components)**: Describes each architectural component in detail. - **[Upgradability](/ccip/concepts/architecture/onchain/aptos/upgradability)**: Explains CCIP's approach to secure system evolution. --- # Onchain Architecture - Overview (Aptos) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/aptos/overview Last Updated: 2025-09-03 ## Aptos as Source Chain On the Aptos source blockchain, a user or another module initiates a transaction by calling an entry function in the CCIP `Router` module. The `Router` module serves as the primary entry point, performing initial validations before forwarding the call to the appropriate `OnRamp` module. The `OnRamp` module then calculates fees, interacts with a designated Token Pool to lock or burn tokens, and finally emits a `CCIPMessageSent` event. This event is observed offchain by the Committing DON, which then securely relays the message to the destination blockchain. ## Aptos as Destination Chain On the Aptos destination blockchain, an onchain module called the `OffRamp` receives a `commit` from the Committing DON. This `commit` contains Merkle roots of batched messages. The `OffRamp` module verifies the OCR signatures from the DON, checks the Risk Management Network (RMN) Remote module to ensure the source chain is not cursed, and then stores the verified Merkle root onchain. Subsequently, the Executing DON submits messages for execution. Aptos, like SVM, follows a one-message-per-transaction execution pattern, which is different from the batch execution possible on EVM chains. For each message, the `OffRamp` verifies its Merkle proof against a committed root, tracks the execution state by emitting an `ExecutionStateChanged` event, and processes the payload. This processing includes calling the appropriate Token Pool to release or mint tokens and, if the message contains data, calling the `ccip_receive` entry function on a designated receiver module. ## Key Components **Source Chain**: **Destination Chain**: | Component | Ownership | Role | | ---------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Sender/Receiver | External (User/Module) | An end-user wallet or a custom Move module that initiates a cross-chain message on the source chain and/or receives the message on the destination chain via the `ccip_receive` function. | | Router | CCIP | The primary, user-facing entry point for sending outbound messages from Aptos. It validates the destination chain, determines the correct `OnRamp` version to use, and forwards the `ccip_send` call. | | OnRamp | CCIP | A module that processes outbound messages on the source chain. It validates parameters, calculates fees by calling the FeeQuoter, manages token interactions with `Token Pools` via the TokenAdminDispatcher, increments sequence numbers, and emits a `CCIPMessageSent` event that offchain DONs observe. | | Nonce Manager | CCIP | A module that tracks outbound nonces for messages on a per-destination-chain and per-sender basis, ensuring ordered message processing where required. | | OffRamp | CCIP | A destination chain module that receives committed message roots from the offchain DON. It verifies these roots, checks the `RMN Remote` for curses, executes messages by verifying their Merkle proofs, and dispatches token/data payloads to their final destinations. It emits an `ExecutionStateChanged` event upon completion. | | Fee Quoter | CCIP | A module that calculates the total fee required for a cross-chain message. It uses onchain price data for tokens and destination chain gas to provide an accurate cost estimate in the user's chosen fee_token. | | Token Admin Dispatcher | CCIP | A dispatcher module that acts as a middleman between the `OnRamp`/`OffRamp` and the TokenAdminRegistry. It ensures that only authorized CCIP modules can initiate token lock, burn, release, or mint operations. | | Token Admin Registry | CCIP | A module that acts as a central registry and maps a given token's address to its designated Token Pool module. It also manages administrative roles for each token, such as who can update its pool configuration. | | Receiver Dispatcher | CCIP | A dispatcher module that ensures only the authorized `OffRamp` module can call the `ccip_receive` function on a registered receiver module. It uses the ReceiverRegistry to verify the destination. | | Receiver Registry | CCIP | A module where custom Move modules can register themselves as valid CCIP message receivers. This registration is necessary for the `OffRamp` to be able to dispatch data and tokens to them. | | Token Pools | External (Token Developer) | Specialized modules that handle cross-chain token transfers. Used by the `Token Admin Dispatcher` for dispatch_lock_or_burn operations on the source blockchain and dispatch_release_or_mint operations on the destination blockchain. | | Token | External (Token Developer) | A fungible asset created on Aptos. | | RMN Remote | CCIP | A module that maintains a list of "cursed" (i.e., blocklisted) chains. Various CCIP modules query this component to ensure they are not interacting with a cursed chain before processing a message. | ## Typical Lifecycle of a Message ### Source Blockchain (Aptos) This outlines the process when initiating a CCIP transaction from the Aptos blockchain. 1. **Preparation** - The Sender (a user's wallet or another module) prepares the information for a CCIP Message, including: - **Receiver**: A byte array representing the destination address (e.g., a 20-byte EVM address). - **Data payload**: Arbitrary bytes to be delivered to the receiver. - **Tokens and amounts** (if applicable). - **Fee token**: The address of the token for paying fees (e.g., native APT or LINK). - **Extra Arguments**: An encoded byte vector containing destination-specific parameters, like a gas limit for EVM. - The Sender calls the `ccip_router::router::get_fee` view function to determine the total CCIP fee required for the message. This function internally calls the `Fee Quoter` module for the cost calculation. - The Sender prepares to call the `ccip_router::router::ccip_send` entry function. Unlike EVM and SVM chains, a separate token approve transaction is not required. The user's signature on the `ccip_send` transaction itself authorizes the CCIP modules to withdraw the necessary tokens and fees from the sender's account. 2. **Sending** - When the `ccip_send` transaction is executed: - The `Router` module, as the entry point, performs initial validations and forwards the call to the appropriate `OnRamp` module. - The `OnRamp` module executes the core logic: - Verifications: It ensures that the destination chain is not cursed by checking with the `RMN Remote` module. - Fee Collection: It withdraws the required fee from the sender's fungible asset store. If the fee_token_store address is specified as 0x0, the module automatically resolves to the primary_fungible_store corresponding to the sender's account and the chosen fee_token. - Sequence Management: It increments the sequence number for the given destination chain. If required, it also interacts with the `Nonce Manager` to handle ordered message nonces. - Token Handling (if applicable): For each token in the message, the `OnRamp` withdraws the funds from the sender's token store and calls the `Token Admin Dispatcher`. This dispatcher uses the `Token Admin Registry` as a secure state machine: - It first calls `start_lock_or_burn` on the registry to store the message context (sender, receiver, etc.). - It then invokes the appropriate Token Pool's `lock_or_burn` function. - The Token Pool module, in turn, calls back into the registry to securely `get_lock_or_burn_input` and, after processing, `set_lock_or_burn_output`. This secure callback pattern ensures token operations are only performed within a valid CCIP transaction context. - Event Emission: A unique messageId is generated, and the `OnRamp` emits a `CCIPMessageSent` event containing the full, sequenced message details. 3. **Initial Offchain Processing** - The CCIP Committing DON monitors the Aptos blockchain for the `CCIPMessageSent` event and begins processing the message offchain to prepare it for commitment on the destination chain. ### Destination Blockchain (Aptos) This outlines the process when Aptos is the receiving chain for a CCIP message. 1. **Commit Phase** - The final OCR report from the Committing DON, containing Merkle roots of batched messages and any price updates, is submitted to the `OffRamp` module's `commit` function. - The `OffRamp` verifies the DON's signatures. - If the report includes blessed Merkle roots, the `OffRamp` verifies the RMN signatures. - If the report contains price updates, the `OffRamp` calls the `Fee Quoter` module to update its onchain token and gas price data. - The `OffRamp` stores the verified Merkle root and emits a `CommitReportAccepted` event, confirming the messages are ready for execution. 2. **Secondary Offchain Processing** - The CCIP Executing DON monitors for the `CommitReportAccepted` event. For each message in the committed batch, the DON computes its specific Merkle proof. Each message is then submitted in a separate transaction to the Aptos blockchain. 3. **Execution Phase** - When the Executing DON calls the `OffRamp` module's `execute` function, the `OffRamp` first verifies the message's Merkle proof against a stored Merkle root. It performs several validations, including checking the `RMN Remote` module and ensuring the message has not already been executed. - If the message includes tokens, the `OffRamp` calls the `Token Admin Dispatcher`. This dispatcher follows a secure callback pattern using the `Token Admin Registry`: it initiates a `release_or_mint` operation, which invokes the correct Token Pool. The pool then calls the registry to get its input data, mints or releases the tokens, and sets the output. The released tokens are then deposited into the receiver's primary fungible store. - If the message contains arbitrary data, the `OffRamp` calls the `Receiver Dispatcher`. The dispatcher does not pass the message data directly. Instead, it securely stores the payload in the `Receiver Registry` and then triggers the `ccip_receive` entry function of the registered receiver module. The receiver module, in turn, must call receiver_registry::`get_receiver_input` within its own execution to securely fetch the message payload it is meant to process. - The `OffRamp` emits a final `ExecutionStateChanged` event with the outcome. Due to the atomic nature of Aptos transactions, a successfully processed message will have a state of `SUCCESS` (2). If the transaction fails for any reason, it fully reverts, and the message remains `UNTOUCHED` (0), allowing for a later retry. - The `OffRamp` module also provides a `manually_execute` function. If automated execution by the DON fails, this function can be permissionlessly called after a configured time delay to ensure the message can still be processed. For more information, read the [manual execution](/ccip/concepts/manual-execution) page. --- # Onchain Architecture - Components (Aptos) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/aptos/components Last Updated: 2025-09-03 This section provides more detail on the Onchain components for Aptos. ## Sender/Receiver **CCIP supports the following as senders and receivers:** - A user-controlled account – A wallet/user account controlled by a private key (also called EOA on EVM blockchains). - An onchain program – Smart contract logic (called "programs" on Solana, "modules" on Aptos, "smart contracts" on EVM chains). **CCIP messages can be sent in any of the following combinations (terminology adapts to the destination blockchain):** - **User Account → User Account / EOA / Wallet** - **Supported Message Type**: Token-only transfers. - **Reason**: The recipient is a simple user account that does not have executable code. While a data payload can be sent in the CCIP message, the recipient account has no way to act upon or process that data. - **User Account → Module / Smart Contract / Program** - **Supported Message Type**: Token transfers, arbitrary data, and programmable token transfers (data + tokens). - **Reason**: The receiving module is designed with a `ccip_receive` function that can be programmed to handle incoming tokens, process arbitrary data, or both simultaneously according to your application's logic. - **Module / Smart Contract / Program → User Account / EOA / Wallet** - **Supported Message Type**: Token-only transfers. - **Reason**: Similar to the first case, the destination is a simple user account that cannot execute logic to process an incoming data payload. - **Module / Smart Contract / Program → Module / Smart Contract / Program** - **Supported Message Type**: Token transfers, arbitrary data, and programmable token transfers (data + tokens). - **Reason**: Both the sender and receiver are programmable entities, allowing for any combination of token and data to be sent and processed according to custom application logic. **A CCIP Message can include:** - An arbitrary bytes payload. - A token transfer. - A programmable token transfer (data + tokens). **Sender Responsibilities:** - Prepare the arguments for the `ccip_send` function. - Retrieve a fee estimate by calling the `router::get_fee` view function. - Call the `router::ccip_send` entry function to send the message. The sender's signature on this transaction authorizes the withdrawal of any tokens and fees. **Receiver Considerations:** - **Data Processing:** If the CCIP Message contains a data payload or a programmable token transfer, the receiver must be a Move module that implements a `ccip_receive` entry function. - **Registration:** The receiver module must be registered with the `Receiver Registry` to be able to receive messages from the CCIP OffRamp. - **Security Validation:** The receiver module should validate that any call to its `ccip_receive` function originates from an authorized CCIP OffRamp address. ## Router The `ccip_router::router` module serves as the single, user-facing interface for sending all outbound CCIP messages from the Aptos blockchain. As a minimal module, its primary role is to act as a stable entry point that directs traffic to the appropriate `OnRamp`. The Router exposes two primary functions for the sender: - `get_fee`: A view function that retrieves the CCIP fee for a given message by forwarding the request to the `OnRamp`. - `ccip_send`: The entry function that initiates a cross-chain message. It performs initial validations and then forwards the call to the correct `OnRamp` module for processing. ## OnRamp The `ccip_onramp::onramp` module is an internal CCIP module that handles the core logic for processing outbound messages on the source chain. When the Router forwards a `ccip_send` request, the OnRamp performs the following actions: - **Validations** - Ensures that the destination chain is not cursed. - Verifies that the sender is on the allowlist if one is enabled for the destination lane. - **Fee Collection** - Withdraws the pre-calculated fee amount from the sender's specified `fee_token_store`, defaulting to the user's primary store if `0x0` is provided. - **Token Handling** - If the message involves token transfers, it initiates a secure callback pattern by calling the `Token Admin Dispatcher`. - The dispatcher uses the `Token Admin Registry` to store the message context (sender, receiver, etc.) before invoking the correct `Token Pool` to `lock_or_burn` the assets. - **Nonce Management** - For ordered messages, it calls the `Nonce Manager` to retrieve and increment the sender's nonce for that destination. - **Event Emission** - Generates a unique `messageId` and emits a `CCIPMessageSent` event containing the complete, sequenced message details. ## Nonce Manager The `ccip::nonce_manager` module enables optional strict message ordering. It tracks outbound nonces on a per-sender and per-destination-chain basis. When out-of-order execution is disabled for a message, the `OnRamp` module uses the `Nonce Manager` to assign an incrementing nonce, which is then verified by the `OffRamp` on the destination chain. ## OffRamp The `ccip_offramp::offramp` module is an internal CCIP module that operates on the destination chain. It is the primary module that the offchain DONs interact with to deliver messages to Aptos. ### Commit Phase During the **Commit Phase**, the following steps occur: 1. **Commit Report Submission**:\ The Committing DON calls the `commit` function on the `OffRamp` with a report that includes Merkle roots and price updates. 2. **Validation**: - The `OffRamp` verifies the DON's signatures. - It verifies any "blessed" Merkle roots. 3. **Price Updates**:\ It calls the `Fee Quoter` to update its onchain token and gas price data. 4. **Event Emission**:\ At the end of the Commit Phase, the `OffRamp` emits a `CommitReportAccepted` event. ### Execution Phase In the **Execution Phase**, the `OffRamp` processes messages one by one: 1. **Merkle Proof Verification**:\ The `OffRamp` verifies the message's Merkle proof against a previously committed Merkle root. 2. **Additional Validations**:\ The `OffRamp` performs final validations, including ensuring the source chain is not cursed. 3. **Token Processing (if applicable)**:\ It calls the `Token Admin Dispatcher` to initiate the `release_or_mint` process. This follows a secure callback pattern where the dispatcher and `Token Pool` interact with the `Token Admin Registry` to securely process the token transfer before depositing the assets into the receiver's primary fungible store. 4. **Message Delivery (if applicable)**:\ It calls the `Receiver Dispatcher`, which securely stores the message payload in the `Receiver Registry` and then triggers the `ccip_receive` function on the registered receiver module. The receiver module is then responsible for calling the `Receiver Registry` to fetch this payload. 5. **Final Execution Status**:\ The `OffRamp` emits a final `ExecutionStateChanged` event, indicating a `SUCCESS` state. Due to Aptos's transaction atomicity, failed executions revert entirely, leaving the message state as `UNTOUCHED` for a potential retry. ### Permissionless Manual Execution (Fallback) The `OffRamp` includes a `manually_execute` function. If automated execution fails, this function can be called permissionlessly after a configured time delay to ensure message delivery. For more information, read the [manual execution](/ccip/concepts/manual-execution) page. ## Fee Quoter The `ccip::fee_quoter` module is responsible for all fee-related calculations. - **Source Chain**: Called by the `OnRamp` to provide a precise fee estimate for a given message. - **Destination Chain**: Called by the `OffRamp` to receive and store updated token and gas price data included in commit reports from the CCIP network. ## Token Admin Dispatcher The `ccip::token_admin_dispatcher` is a security-focused module that acts as a secure entry point for all token operations. Only authorized CCIP modules (specifically, the `OnRamp` and `OffRamp`) are permitted to call this dispatcher. It then looks up the correct `Token Pool` via the `Token Admin Registry` and initiates the `lock_or_burn` or `release_or_mint` operations, preventing direct, unauthorized calls to the token pools. ## Token Admin Registry The `ccip::token_admin_registry` is a central onchain module that maintains the critical mapping between a token's address and the address of its designated `Token Pool` module. It serves as the single source of truth for the CCIP network to determine how to handle a specific token for cross-chain transfers. It also manages administrative rights for token configurations. ## Receiver Dispatcher The `ccip::receiver_dispatcher` is a security-focused module that ensures only the authorized `OffRamp` module can deliver a message to a registered receiver module. When a message containing data arrives, the `OffRamp` calls this dispatcher. The dispatcher first verifies that the destination module is registered in the `Receiver Registry` before safely calling the `ccip_receive` function on that end-user's module. ## Receiver Registry The `ccip::receiver_registry` is a module where developers can register their custom Move modules to make them officially recognizable as valid CCIP message receivers. A module must be registered here before it can receive data or programmatic token transfers from the `OffRamp` via the `Receiver Dispatcher`. ## Tokens and Token Pools ### Tokens - Tokens are developed by token developers and exist independently of the core CCIP modules. - Most tokens built on the Aptos [Fungible Asset](https://aptos.dev/en/build/smart-contracts/fungible-asset) standard are compatible with CCIP. For more information on compatibility, refer to the CCT documentation. ### Token Pools - Token Pools are deployed by token developers and exist independently of the core CCIP modules. - Token Pools are modules interact with tokens created using the [Fungible Asset](https://aptos.dev/en/build/smart-contracts/fungible-asset) standard. - Token pools follow standard models: - **Lock/Release** - **Burn/Mint** - Audited code for `lock_release_token_pool` and `burn_mint_token_pool` modules is available in the CCIP repository. - For tokens requiring custom logic before burn/mint/lock/release, developers may build custom pools on top of these base modules. More details are available in the CCT documentation. ## RMN (Risk Management Network) Remote The `ccip::rmn_remote` module is a critical security component deployed on every CCIP-enabled chain. Various CCIP modules (like `OnRamp`, `OffRamp`, `Token Pools`) query this component to verify the status of other chains in the network. It maintains an onchain list of "cursed" (i.e., blocklisted) chains. If a source or destination chain is cursed, CCIP transactions involving that chain are halted. --- # Onchain Architecture - Upgradability (Aptos) Source: https://docs.chain.link/ccip/concepts/architecture/onchain/aptos/upgradability Last Updated: 2025-09-03 Chainlink's Cross-Chain Interoperability Protocol (CCIP) is designed to evolve in response to new feature requests, security considerations, and the need to support additional blockchains over time. This requires a secure upgrade process that preserves CCIP's robust security while allowing for iterative improvements. ## What Can Be Upgraded On the Aptos blockchain, upgradability primarily happens in two ways: 1. **Onchain Configuration** Many CCIP modules (like `onramp`, `offramp`, `fee_quoter`) expose public entry functions that allow authorized accounts to adjust operational parameters. These functions modify onchain data stored in resources without requiring a new deployment. Examples include: - Enabling support for a new destination chain. - Updating fee parameters. 2. **Module Code Upgrades** In the CCIP deployment on Aptos, the core modules (`router`, `onramp`, `offramp`, `fee_quoter`, etc.) are grouped into packages and published under a single, unified Object. Aptos allows for module code to be upgraded in-place. This means a code upgrade for the CCIP protocol involves publishing the new, updated module bytecode to the existing Object address. Because the Object address remains unchanged: - External modules and off-chain clients that interact with CCIP do not need to update their stored addresses. - They seamlessly begin interacting with the new code after the upgrade is published. This approach ensures that bug fixes and new features can be rolled out atomically and consistently while maintaining a stable on-chain address for the protocol. ## Implementation Process All critical onchain configuration changes to CCIP on Aptos are governed by a secure, cross-chain process using the **ManyChainMultiSig (MCMS)** system, which functions similarly across all CCIP-supported chains. The onchain mechanism for this on Aptos is the `mcms_entrypoint` function found within the core CCIP modules. This function is designed to be called by the `mcms_registry`, allowing the multi-chain governance process to execute proposals. Any proposal must follow one of two paths: 1. **Time-locked Review**: The proposal is submitted onchain and enters a mandatory review period. During this window, node operators securing CCIP can inspect the proposed change and veto it if necessary. If no veto occurs, the proposal becomes executable after the delay expires. 2. **Expedited Approval**: For time-sensitive situations, a proposal can be passed via an expedited path if it receives explicit approval from a quorum of independent signers. Once a proposal is approved through either path, it can be executed, and the `mcms_entrypoint` on the target Aptos module is called with the specified changes. This entire process is publicly verifiable, ensuring transparency for all onchain upgrades. --- # Offchain Architecture Source: https://docs.chain.link/ccip/concepts/architecture/offchain Last Updated: 2025-05-19 This section describes the offchain components that support the CCIP network, ensuring security, reliability, and efficient cross-chain message processing. - **[Overview](/ccip/concepts/architecture/offchain/overview)**: A high-level summary of the offchain systems, including Decentralized Oracle Networks (DONs), the Risk Management Network, and node capabilities. - **[Risk Management Network](/ccip/concepts/architecture/offchain/risk-management-network)**: Understand how this network monitors cross-chain activity for potential risks. --- # Offchain Architecture - Overview Source: https://docs.chain.link/ccip/concepts/architecture/offchain/overview Last Updated: 2025-05-19 CCIP's offchain architecture includes the following: - **Decentralized Oracle Networks (DONs)**: Running offchain consensus using the Offchain Reporting Protocol (OCR). - **Risk Management Network (RMN)**: Enhancing security through a defense-in-depth design. - **Interaction Components**: Managing communication between the CCIP DONs and the RMN. ## Components ### CCIP Decentralized Oracle Networks With the CCIP v1.6 architecture, there is a single DON called the Role DON that includes all participating nodes. Two OCR plugins run on these nodes: 1. **Commit OCR Plugin** - Coordinates observations from multiple source chains. - Requests blessings from the RMN (when applicable) and posts a Commit Report on the destination chain. 2. **Executing OCR Plugin** - Monitors pending executions on the destination chain. - Verifies events on the source chain and executes messages accordingly. **Note**: For simplicity, we sometimes refer to the nodes running the Commit OCR Plugin as the Committing DON and those running the Executing OCR Plugin as the Executing DON. However, these are not separate oracle networks; they are subsets of the same Role DON, distinguished solely by their assigned roles. ### Risk Management Network The Risk Management Network (RMN) is a unique architectural feature of CCIP that reinforces system security through a defense-in-depth approach. Key aspects include: - **Separate Implementation**: RMN runs a minimal, separate implementation of the Chainlink node software, which uses a different programming language than the primary CCIP system. This design provides an extra layer of independent attestation, enhancing overall security. - **Distinct Node Operators**: A dedicated set of node operators—separate from those managing the core CCIP DONs—participates in RMN functions. - **Blessing Mechanism**: On blockchains where RMN blessing is enabled, RMN nodes "bless" CCIP messages by providing independent attestations of the source chain observation. ### Interaction - **Commit OCR and RMN Blessings**: The Commit OCR plugin interacts directly with RMN nodes to query for observations and receive independent RMN blessings. These blessings serve as attestations of the validity of source chain observations and are used to form a consolidated set of merkle roots. - **Configuration and Role Validation**: The roles of nodes are defined and managed on the "Home Chain" (i.e., Ethereum) through specific contracts. Each node software reads from these contracts to verify that nodes are authorized for their roles. - The **CCIP Home** contract contains the configuration required to bootstrap the CCIP network (Role DON) and includes both chain-level and Role DON-level details. - The **RMN Home** contract maintains the configuration for RMN nodes. ## Role DON: Diagram and Explanation Let's take an example from the diagram above to understand the key components. The diagram shows a Commit OCR instance configured for a destination chain (Chain C). Key details include: - **Commit Role DON** - Represents all participating nodes. - Within the Role DON, a specific group of nodes—sometimes called a "sub-committee"—may be assigned chain-specific roles. - **Chain-Specific Roles** (as illustrated) - **Green Rectangle**: Nodes observe Chain A (i.e., they read from Chain A). - **Orange Rectangle**: Nodes observe Chain B. - **Blue Rectangle**: Nodes write commit reports to Chain C. - **Overlap**: Note that groups may overlap. For example, the four nodes in the bottom-left corner of the Role DON might not connect to Chain A or Chain C, demonstrating flexibility in node participation. - **Interaction with RMN**: The Commit OCR interacts with RMN nodes to receive RMN blessings. These blessings act as independent attestations of the validity of source chain observations and are used to form a consolidated set of merkle roots. - **Configuration and Role Validation**: - Node roles are configured on the Home Chain (i.e., Ethereum) through specific contracts. - The node software reads from the **CCIP Home** and **RMN Home** contracts to verify that each node is authorized for its role—whether for making observations or providing RMN blessings. ## High Level Flow Below are the high-level steps for the Commit OCR process and the Executing OCR process. ### Commit OCR Process - **Observation Phase** - Each subcommittee reading from a source chain reaches consensus on the range of messages to build a merkle root. - A minimum threshold of valid observations is required for consensus. - **Report Building Phase** - For source chains where RMN blessing is enabled, the leader of the OCR round queries the RMN nodes for the defined message ranges. - RMN nodes return independently constructed merkle roots for these ranges. - After validating the returned merkle roots, the leader sends the consolidated set of merkle roots to the RMN for signatures (a single signature may cover multiple merkle roots). - **Query Phase** - The leader shares the Commit Report (which may include RMN signatures) with the rest of the nodes in the Role DON for validation. - Invalid observations are dropped and the remaining valid ones must meet the threshold to achieve consensus among the nodes. - **Reporting Phase** - A subcommittee of nodes writing to the destination chain submits the final Commit Report onchain. - The report may include merkle roots from multiple sources—some of which have RMN signatures. - Additionally, the Commit plugin posts price reports for fee tokens, so the Commit Report can contain a combination of merkle roots and price reports. ### Executing OCR Process - **Pending Execution Check** - The subcommittee connected to the destination chain checks for pending executions that have been committed via a Commit Report. - **Validation and Optimization**: - The DON coordinates to validate the corresponding source chain events for these pending executions. - Once validated, the Executing DON optimizes the set of messages to be batched for execution. This optimization considers factors such as the gas limit and specific nuances of the destination chain. - **Execution**: - The message (or batch of messages) is executed on the destination chain. --- # Risk Management Network Source: https://docs.chain.link/ccip/concepts/architecture/offchain/risk-management-network Last Updated: 2025-05-19 The **Risk Management Network (RMN)** is an independent network that continuously monitors and validates the behavior of CCIP, providing an additional layer of security by independently verifying cross-chain operations for anomalous activity. The RMN independently reconstructs the batches of cross-chain messages from source chains, then blesses valid messages and authorizes them for execution on destination chains. Its core job today is to bless committed cross-chain messages offchain, ensuring they match what was observed on the source blockchain. The RMN utilizes a separate, minimal implementation of the Chainlink node software, creating a form of client diversity for increased robustness while also minimizing external dependencies to prevent supply chain attacks. Specifically, the RMN was written in a different programming language than the primary CCIP system, developed by a different internal team, and uses a distinct non-overlapping set of node operators compared to the CCIP DONs. The Risk Management Network is a wholly unique concept in cross-chain interoperability that builds upon established engineering principles (N-version programming). ## Process The verification process of the RMN can be detailed as follows: 1. **Observing Merkle Roots** - When the Committing DON posts a merkle root of new cross-chain messages on the destination blockchain, each RMN node independently fetches the underlying messages from the source blockchain. - If the node's locally reconstructed merkle root matches the merkle root posted on the destination chain, it signs an RMN observation stating that the commit is valid. 2. **Assembling Offchain Reports** - The Committing DON collects identical observations from a sufficient number of RMN nodes, and those nodes produce a final signed RMN report. - This report consists of cryptographic signatures proving that most RMN nodes have confirmed the commit's integrity. 3. **Verification in Final Onchain Commit** - The Committing DON attaches these RMN signatures to its final onchain commit. - On EVM-based blockchains, the OffRamp contract checks the RMN signatures via the RMNRemote contract, ensuring the Executing DON can only finalize merkle roots with sufficient RMN blessings. A similar verification mechanism applies on non-EVM blockchains, tailored to their specific contract or program model. ## RMN Configuration Although the RMN primarily acts offchain, it does rely on certain onchain configurations: - **RMNHome**: Deployed on a home chain (e.g., Ethereum), this contract stores global RMN settings, such as node identities, chain selectors, and public keys. - **RMNRemote**: Deployed on each destination blockchain (including the home chain), this contract verifies RMN node signatures whenever the Committing DON submits its final commit. Only commits carrying valid RMN blessings proceed to complete execution. ## Cursing While the RMN focuses on blessings, there are ways to address anomalies: 1. **Monitoring and Alerting**: Suspicious activity is monitored, such as replayed messages or potential double-executions. 2. **Propose a Pause**: If needed, a "curse" transaction can be proposed to pause CCIP on a specific blockchain. 3. **Investigation and Resolution**: Once the root cause is addressed, the pause can be lifted. --- # Cross-Chain Token Standard Source: https://docs.chain.link/ccip/concepts/cross-chain-token Last Updated: 2025-06-09 This section explains the Cross-Chain Token (CCT) standard, a Chainlink CCIP feature enabling secure and reliable cross-chain token transfers. Learn how to make your tokens compatible with CCIP. - **[Overview](/ccip/concepts/cross-chain-token/overview)**: Get a high-level summary of the CCT standard and its benefits. - **[EVM Tokens](/ccip/concepts/cross-chain-token/evm)**: Guidance for integrating tokens on EVM-compatible blockchains. - **[SVM Tokens](/ccip/concepts/cross-chain-token/svm)**: Guidance for integrating tokens on SVM-based blockchains like Solana. --- # Cross-Chain Token Standard - Overview Source: https://docs.chain.link/ccip/concepts/cross-chain-token/overview Last Updated: 2025-05-19 The Cross-Chain Token (CCT) standard offers a streamlined and decentralized approach to enabling token transfers across blockchains using Chainlink's Cross-Chain Interoperability Protocol (CCIP). Traditionally, token developers had a friction-laden and manual process to enable their tokens for cross-chain operations. This process required manual deployment of token pools, making it time-consuming. With the introduction of CCTs, token developers now have the power to deploy, configure, and manage their own token pools in CCIP via a simple interface. This self-service model not only accelerates the deployment process but also empowers token developers with greater autonomy and control over their cross-chain token operations. This guide will explore the motivations behind Cross-Chain Token (CCT) and provide an overview of the architectural components in enabling a compatible token in CCIP. ## Motivations The motivation for Cross-Chain Token (CCT) originates from two primary challenges: liquidity fragmentation in a multi-chain ecosystem and the need for greater autonomy for token developers. ### Liquidity Fragmentation [Hundreds](https://chainlist.org/) of blockchains exist, each with their own rules, consensus mechanisms, and liquidity. This expansion has created fragmented liquidity, where assets are siloed on individual blockchains, making it difficult for users and developers to access liquidity across different ecosystems. Token developers face the dilemma of choosing which blockchain to deploy on, often deciding between the blockchain with the most liquidity or a new, emerging chain with future potential. Each decision comes with risks, such as competing in a crowded market or dealing with trust assumptions on new blockchains. ### Need for Greater Autonomy Another critical motivation behind Cross-Chain Token is to empower projects to enable their tokens for cross-chain operations without the support of third-parties. By providing a self-service model, Cross-Chain Token allows projects to take control of their cross-chain applications. This enables rapid expansion to other blockchains supported by CCIP. Cross-Chain Token (CCT) solves these challenges by allowing token developers to create assets that can move seamlessly across multiple blockchains without fragmenting liquidity. By deploying a token in multiple blockchain environments, token developers can maintain a consistent supply, manage liquidity efficiently, and reach users on different blockchains without introducing complex bridging processes. The CCT standard offers the following benefits: #### Self-service and Permissionless Deployment With CCT, token developers can launch a cross-chain token or enable an existing token in a self-service manner within minutes. The fully audited token pool contracts handle the complexities of burning/minting or locking/minting tokens across blockchains, all without requiring liquidity pools. This enables a streamlined and permissionless deployment process that significantly reduces the barriers to cross-chain expansion. Starting from v1.5.1, token pools support zero-downtime upgrades, allowing seamless transitions between pool versions while maintaining support for in-flight messages. #### Developer Control and Flexibility Token developers retain complete ownership of their token contracts, token pools, and implementation logic. This includes configuring rate limits across all blockchains, ensuring that token developers have full autonomy to manage their deployments. The CCT standard avoids vendor lock-in, hard-coded functions, and external dependencies, giving projects the flexibility they need to succeed. #### Defense-in-Depth Security The CCT standard leverages Chainlink's industry-standard oracle networks, which secure over $16 trillion in Total Value Enabled (TVE). This strong foundation is further enhanced by additional layers of protection, such as the Risk Management Network and configurable transfer rate limits, providing comprehensive security for cross-chain operations. #### Programmable Token Transfers Cross-Chain Token (CCT) supports the simultaneous transfer of tokens and messages in a single transaction. This programmability allows for complex use cases, such as executing on-chain actions alongside token transfers, improving the overall efficiency of cross-chain transactions. For instance, a programmable token transfer could execute a function to stake tokens on the destination blockchain. #### No Liquidity Pools Required The fully audited token pool contracts provided by CCT eliminate the need for liquidity pools, simplifying cross-chain token management. By locking or burning tokens on the source chain and minting or unlocking them on the destination chain, token developers can maintain consistent liquidity without managing fragmented liquidity pools across multiple blockchains. #### Reduced Liquidity Fragmentation The CCT standard addresses liquidity fragmentation by allowing tokens to move seamlessly across multiple blockchains rather than requiring liquidity to be present on both source and destination chains. This unified approach ensures that liquidity remains consistent and accessible across all supported blockchains, reducing inefficiencies and improving the user experience. #### Zero-Slippage Transfers Token developers are provided access to pre-audited token pool contracts that enable zero-slippage cross-chain transfers of CCTs. The exact amount of tokens sent to the CCIP OnRamp on the source chain is always the exact amount of tokens received on the CCIP OffRamp on the destination chain. #### Ease of Cross-Chain Integration With CCT, existing compatible tokens can be easily extended to support cross-chain functionality. This makes it possible for token developers to extend their token's reach across multiple blockchains with minimal effort, avoiding the complexities associated with traditional bridging solutions. ## Key Concepts ### Token Developer The entity responsible for issuing and managing the token. Token developers decide which blockchains to deploy the token on, manage its lifecycle, and control its availability across different blockchains. ### Token Owner The entity that owns the token contract. The token owner has the authority to manage the token contract, including upgrading the contract, changing the token administrator, or transferring ownership to another address. ### Token Administrator A role assigned to manage cross-chain operations of a token. The token administrator is responsible for mapping a token to a token pool in the TokenAdminRegistry.. The token administrator can be the token owner or another designated entity assigned by the token owner. Technically, the token administrator can be an EOA or a smart account. ### EOA (Externally Owned Account) A standard blockchain account controlled by a private key, typically used by individuals. EOAs do not have associated code, unlike smart accounts, and are used for directly signing transactions. ### Smart Account A blockchain account represented by a smart contract, offering advanced functionality like multi-signature authorization or custom transaction logic. Smart accounts provide better security and flexibility compared to EOAs. For example, smart accounts can use multi-signature mechanisms to add an extra layer of security. ### Compatible Token Cross-Chain tokens need to adhere to specific requirements to ensure interoperability across different blockchain ecosystems. Each blockchain family has its own requirements documentation. For example, you can read about [EVM token requirements](/ccip/concepts/cross-chain-token/evm/tokens) or visit the respective documentation pages for other blockchain families like SVM. ### Token Pool Each token is associated with its own token pool, which acts as an abstraction layer over compatible tokens designed to facilitate token-related operations for OnRamping and OffRamping. Token pools provide rate limits, a security feature enabling token developers to set a maximum rate at which their token can be transferred per lane. Token pools are configured to `lock` or `burn` tokens on the source blockchain and `unlock` or `mint` tokens on the destination blockchain. ## Token Pool rate limits Token administrators may set-up token pool rate limits. A token pool rate limit (TPRL) has a maximum capacity and a refill rate, which is the speed at which the maximum capacity is restored after a token transfer has consumed some or all of the available capacity. You can find the complete list of CCIP supported tokens and their rate limits on the [CCIP Directory page](/ccip/directory). ## Token Handling Mechanisms The setup of token pools on source and destination blockchain results in four primary token handling mechanisms: ### Burn and Mint Tokens are burned on the source blockchain, and an equivalent amount of tokens are minted on the destination blockchain. This keeps the total supply of the token constant across blockchains. ### Lock and Mint This mechanism is suitable for projects with existing tokens on a single blockchain that have already been minted and lack functionality to control the supply via burn and mint functions on the source blockchain. In this approach, tokens are locked on the issuing blockchain, and fully collateralized "wrapped" tokens are minted on the destination blockchain. These wrapped tokens, which must support burn and mint functionality, can subsequently be transferred across other non-issuing blockchains using the Burn and Mint mechanism. ### Burn and Unlock Tokens are burned on the source blockchain (which is the non-issuing blockchain), and an equivalent amount of tokens are released on the destination blockchain (the issuing blockchain). This mechanism is the inverse of the Lock and Mint mechanism and applies when you send tokens back to their issuing source blockchain. ### Lock and Unlock Tokens are locked on the source blockchain, and an equivalent amount of tokens are released on the destination blockchain. For additional guidance on Lock and Unlock configuration, please review best practices. ### Token-Specific Transfer Mechanisms The mechanism for handling tokens varies depending on the characteristics of each token. Below are several examples to illustrate this: - LINK Token is minted on a single blockchain (Ethereum mainnet) and has a fixed total supply. Consequently, CCIP cannot natively mint it on another blockchain. For LINK, the token pool is configured to lock tokens on Ethereum mainnet (the issuing blockchain) and mint them on the destination blockchain. Conversely, when transferring from a non-issuing blockchain to Ethereum mainnet, the LINK token pool is set to burn the tokens on the source (non-issuing) blockchain and unlock them on Ethereum Mainnet (issuing). For example, transferring 10 LINK from Ethereum mainnet to Base mainnet involves the LINK token pool locking 10 LINK on Ethereum mainnet and minting 10 LINK on Base mainnet. Conversely, transferring 10 LINK from Base mainnet to Ethereum mainnet involves the LINK token pool burning 10 LINK on Base mainnet and unlocking 10 LINK on Ethereum mainnet. - Wrapped native Assets (e.g., WETH) use a Lock and Unlock mechanism. For instance, when transferring 10 WETH from Ethereum mainnet to Optimism mainnet, the WETH token pool will lock 10 WETH on Ethereum mainnet and unlock 10 WETH on Optimism mainnet. Conversely, transferring from Optimism mainnet back to Ethereum mainnet involves the WETH token pool locking 10 WETH on Optimism mainnet and unlocking 10 WETH on the Ethereum mainnet. - Stablecoins (e.g., USDC) can be minted natively on multiple blockchains. Their respective token pools employ a Burn and Mint mechanism, burning the token on the source blockchain and then minting it natively on the destination blockchain. - Tokens with a Proof Of Reserve (PoR) with a PoR feed on a specific blockchain present a challenge for the Burn and Mint mechanism when applied across other blockchains due to conflicts with the PoR feed. For such tokens, the Lock and Mint approach is preferred. --- # Cross-Chain Token Standard (EVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/evm Last Updated: 2025-05-19 This section provides detailed guidance for integrating tokens with Chainlink CCIP on EVM-compatible blockchains. - **[Architecture](/ccip/concepts/cross-chain-token/evm/architecture)**: Understand the specific architecture for CCTs on EVM chains. - **[Tokens](/ccip/concepts/cross-chain-token/evm/tokens)**: Learn about the requirements and compatibility for your tokens. - **[Token Pools](/ccip/concepts/cross-chain-token/evm/token-pools)**: Explore the different types of token pools and how to use them. - **[Registration and Administration](/ccip/concepts/cross-chain-token/evm/registration-administration)**: Details on registering your token with CCIP and managing administrative roles. - **[Upgradability](/ccip/concepts/cross-chain-token/evm/upgradability)**: Information on upgrading token pool contracts. --- # Cross-Chain Token Standard - Tokens (EVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/evm/tokens Last Updated: 2025-05-19 Before enabling an ERC20-compatible token in CCIP, it's important to understand the requirements it must fulfill to integrate with CCIP. ## Registration functions A token administrator can use any of the following supported function signatures for enabling their tokens in CCIP: - `owner()`: This function returns the token contract owner's address. - `getCCIPAdmin()`: This function returns the token administrator's address and is recommended for new tokens, as it allows for abstraction of the CCIP Token Administrator role from other common roles, like `owner()`. ## Transfer functions The token's smart contract must meet minimum requirements to integrate with CCIP. ### BurnMint Requirements This section describes the requirements for tokens that are used with the BurnMint token pools. - The token smart contract must have the following functions: - `mint(address account, uint256 amount)`: This function is used to mint the `amount` of tokens to a given `account` on the destination blockchain. - `burn(uint256 amount)`: This function is used to burn the `amount` of tokens on the source blockchain. - `decimals()`: Returns the token's number of decimals. - `balanceOf(address account)`: Returns the current token balance of the specified `account`. - `burnFrom(address account, uint256 amount)`: This function burns a specified number of tokens from the provided account on the source blockchain. Note: This is an optional function. We generally recommend using the `burn` function, but if you use a tokenPool that calls `burnFrom`, your token contract will need to implement this function. - The token contract must support granting mint and burn permissions. The token developers or another role (such as the token administrator) will grant these permissions to the token pool. ### LockRelease Requirements This section describes the requirements for tokens that are used with the LockRelease token pools. - The token smart contract must have the following functions: - `decimals()`: Returns the token's number of decimals. - `balanceOf(address account)`: Returns the current token balance of the specified `account`. - On the destination blockchain, the token contract must support granting mint and burn permissions. The token developers or another role (such as the token administrator) will grant these permissions to the token pool. ## Token Handling Mechanisms To facilitate cross-chain token transfers, you need to choose the appropriate token handling mechanism and deploy the correct combination of token pools for the source and destination blockchains. The table below summarizes the different token handling mechanisms and the [recommended token pools](https://github.com/smartcontractkit/chainlink-ccip/tree/release/contracts-ccip-1.6.2/chains/evm/contracts/pools) to deploy for each scenario, ensuring a seamless token transfer process. | Token Handling Mechanism | Source Blockchain Token Pool | Destination Blockchain Token Pool | Notes | | ------------------------ | ---------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Burn and Mint | BurnMintTokenPool | BurnMintTokenPool | - Standard burn and mint mechanism for cross-chain token transfers. | | Lock and Mint | LockReleaseTokenPool | BurnMintTokenPool | - The source blockchain is the issuing blockchain.
- LockReleaseTokenPool must be deployed on the issuing blockchain. | | Burn and Unlock | BurnMintTokenPool | LockReleaseTokenPool | - The destination blockchain is the issuing blockchain.
- BurnMintTokenPool is used to burn tokens on the source blockchain, and LockReleaseTokenPool is used to unlock tokens on the issuing blockchain. | | Lock and Unlock | LockReleaseTokenPool | LockReleaseTokenPool | - Tokens are locked on the source blockchain and unlocked on the destination blockchain.
- Not recommended due to fragmented liquidity and requires careful management of liquidity across multiple blockchains. | --- # Cross-Chain Token Standard - Token Pools (EVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/evm/token-pools Last Updated: 2025-05-19 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`](/ccip/api-reference/evm/v1.6.2/token-pool#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`](/ccip/api-reference/evm/v1.6.2/token-pool#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](/ccip/concepts/manual-execution). - 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](https://chain.link/ccip-contact?v=Tokens:%20Gas%20limit%20update) 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 #### v1.5.0 Token pools on **v1.5.0** do not support tokens with different decimal places across blockchains. When transferring a token between blockchains with differing decimal places, the token loses precision resulting in different amounts of tokens between the source and destination blockchain. Consider a token developer who deployed their token across two blockchains with different decimal configurations: - Blockchain A: Token with 12 decimals - Blockchain B: Token with 6 decimals | Transfer Path | Example | Explanation | Impact | | -------------- | --------------------------------------------------------------------- | --------------------- | ------------------------------ | | A → B (12 → 6) | • Send 1.0 from A (= 10^12 base units)
• Receive on B: 1,000,000 | 10^12/10^6= 1,000,000 | Gain of 999,999 (1,000,000 -1) | | B → A (6 → 12) | • Send 1.0 from B (= 10^6 base units)
• Receive on A: 0.000001 | 10^6/10^12= 0.000001 | Loss of 0.999999 (1-0.000001) | We highly recommend upgrading to v1.5.1 to leverage the native support for token decimal handling. If using v1.5.0, please make sure to configure a custom v1.5.0 token pool. #### V1.5.1 and Higher Starting **from v1.5.1**, token pools support tokens with different decimal places across blockchains. This feature can impact the total number of tokens in circulation because tokens locked/burned on the source chain might result in a smaller number of tokens minted/released on the destination chain due to decimal rounding. **Understanding Token Decimals** When deploying their token, token developers can configure different decimal places for each blockchain. For example: - On Ethereum: The developer sets 18 decimals (0.123456789123456789) - On Polygon: The developer sets 9 decimals (0.123456789) When transferring tokens between these blockchains, CCIP handles decimal conversion automatically but must round numbers to match the destination's configured precision. **Impact Scenario** Consider a token developer who deployed their token across three blockchains with different decimal configurations: - Blockchain A: High precision (18 decimals) - Blockchain B: Low precision (9 decimals) - Blockchain C: High precision (18 decimals) | Scenario | Transfer Path | Example | Impact | | --------------------------- | ------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **High to Low Precision ❌** | A → B | • Send from A: 1.123456789123456789
• Receive on B: 1.123456789 | Lost: 0.000000000123456789
• Burn/mint: Tokens permanently burned on Blockchain A
• Lock/release: Tokens locked in pool on Blockchain A | | **Low to High Precision ✅** | B → A | • Send from B: 1.123456789
• Receive on A: 1.123456789000000000 | • No precision loss | | **Equal Precision ✅** | A → C | • Send from A: 1.123456789123456789
• Receive on C: 1.123456789123456789 | • No precision loss | **Best Practices** - Deploy tokens with the same number of decimals across all blockchains whenever possible - This prevents any loss of precision during cross-chain transfers - Different decimals should only be used when required by blockchain limitations (e.g., non-EVM chains with decimal constraints) - Verify decimal configurations on both source and destination blockchains before transfers - Consider implementing UI warnings for transfers that might be affected by rounding - When using high-to-low precision transfers, be aware that: - In burn/mint pools: Lost precision results in permanently burned tokens - In lock/release pools: Lost precision results in tokens accumulating in the source pool ## 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](https://github.com/smartcontractkit/chainlink-ccip/tree/release/contracts-ccip-1.6.2/chains/evm/contracts/pools). For most use cases, you should use either: - **[BurnMintTokenPool](https://github.com/smartcontractkit/chainlink-ccip/blob/release/contracts-ccip-1.6.2/chains/evm/contracts/pools/BurnMintTokenPool.sol)**: This token pool is used to burn or mint tokens. You can read the API reference here. - **[BurnFromMintTokenPool](https://github.com/smartcontractkit/chainlink-ccip/blob/release/contracts-ccip-1.6.2/chains/evm/contracts/pools/BurnFromMintTokenPool.sol)**: 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](https://github.com/smartcontractkit/chainlink-ccip/blob/release/contracts-ccip-1.6.2/chains/evm/contracts/pools/LockReleaseTokenPool.sol)**: 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](https://github.com/smartcontractkit/chainlink-ccip/blob/release/contracts-ccip-1.6.2/chains/evm/contracts/pools/TokenPool.sol) 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](#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 `destPoolData`and 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. --- # Cross-Chain Token Standard - Architecture (EVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/evm/architecture Last Updated: 2025-05-19 The Cross-Chain Token (CCT) architecture offers a streamlined, self-service approach to enabling cross-chain operations. This system integrates tightly with Chainlink's Cross-Chain Interoperability Protocol (CCIP), allowing token developers to configure, manage, and transfer tokens across multiple blockchains without requiring the support of third parties. The detailed architecture diagram below provides a comprehensive view of how CCT fits within the CCIP ecosystem, illustrating the interaction between key components such as token contracts, token pools, and registry modules. While you can explore the full details of these components in the CCIP Architecture document, the key takeaway for Cross-Chain Token (CCT) is understanding how the `TokenAdminRegistry` contract is used. The **TokenAdminRegistry** contract is essential when transferring tokens across blockchains. CCIP's `onRamp` and `offRamp` contracts interact with it to fetch the token pool associated with a given token. For cross-chain transfers to work, token administrators need a way to set or configure the token pool linked to a token. In the following sections, we will explore how token administrators can register their tokens in the TokenAdminRegistry, link them to the relevant token pools, and configure them for use in CCIP. In the Cross-Chain Token (CCT) standard, several key contracts work together to facilitate the secure transfer and management of tokens across multiple blockchains. These contracts can be categorized into three main groups: ## Registry The Registry contracts manage the registration tokens within the CCT system. They ensure that the correct entities have control over cross-chain operations. - `TokenAdminRegistry`: This contract stores the token administrators and pools for all registered cross-chain tokens. It allows tokens to be registered in a self-service manner and handles administrator role changes via a two-step process (transfer request and acceptance). - `RegistryModuleOwnerCustom`: This contract facilitates the registration of token administrators. It works with the TokenAdminRegistry to ensure that only authorized administrators are assigned to manage cross-chain operations. ## Token The Token contract represents the actual token being managed and transferred across blockchains. This contract must be ERC20-compatible and have additional functionalities depending on the cross-chain handling mechanism used. For more details on the requirements for ERC20-compatible tokens, refer to the Tokens page. ## Token Pool The Token Pool contract is responsible for executing the cross-chain token transfers. It manages how tokens are locked, burned, minted, or unlocked across blockchains. Each blockchain has its own token pool that interacts with the token contract. Depending on the token handling mechanism (e.g., Burn & Mint or Lock & Mint), different token pool contracts will be deployed. For example: - `BurnMintTokenPool`: Handles the burning, or minting of tokens depending whether it is the source or destination blockchain. - `LockReleaseTokenPool`: Handles the locking or releasing of tokens depending on whether it is the source or destination blockchain. For more information on the token pool contracts and their functionalities, refer to the [Token Pools](/ccip/concepts/cross-chain-token/evm/token-pools) page. --- # Cross-Chain Token Standard - Registration & Administration (EVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/evm/registration-administration Last Updated: 2025-05-19 The process of registering your token for **Cross-Chain Token (CCT)** involves a **two-step process** to ensure that the correct administrator is assigned. The token's administrator will initially be placed in a proposed state. The administrator must then explicitly accept the role to complete the registration. ## Self-Service Registration Flow If the token contract includes any of the necessary functions (`getCCIPAdmin()`, or `owner()`), the registration can be done autonomously by the token administrator: 1. Admin Initiates Registration: The token administrator begins the process by calling one of these functions on the `RegistryModuleOwnerCustom` contract: - `registerAdminViaGetCCIPAdmin()` - `registerAdminViaOwner()` 2. Determine Administrator: The RegistryModuleOwnerCustom contract retrieves the administrator from the token contract using the appropriate method: - Via `getCCIPAdmin()` - Via `owner()` 3. Propose Administrator: The retrieved administrator is then proposed to the `TokenAdminRegistry` using the `proposeAdministrator()` function, placing the administrator in a pending state. 4. Pending Administrator: At this point, the administrator is marked as "pending" in the `TokenAdminRegistry` and must complete the second step—**accepting the role**—before being officially assigned. ## Non-Self-Service Registration Flow If the token contract does not have the necessary functions (`getCCIPAdmin()` or `owner()`), the token developer must manually initiate the registration by submitting a request here. ## Interacting with the Registry ### Proposing the Administrator The following sequence diagram illustrates the process of proposing the administrator for both self-service registration flow: ### Accepting the Administrator Role Once the administrator has been proposed and is in a pending state, they must accept the role to complete the registration process. This step finalizes the assignment of the administrator. 1. **Administrator Accepts Role**: The pending administrator must explicitly call the `acceptAdminRole()` function on the `TokenAdminRegistry` to complete the registration. 2. **Finalize Registration**: Once the administrator accepts the role, they are assigned as the token administrator, and the registration process is complete. At this point, the token administrator can set a token pool for the token in the `TokenAdminRegistry`. Once the administrator has been proposed and is in a pending state, the final step in the registration process is for the pending administrator to accept the role. This sequence diagram illustrates how the pending administrator interacts with the `TokenAdminRegistry` to complete the registration. It also covers the scenario where an incorrect entity attempts to accept the role, leading to an error. ### Transfer Administrator Role The `transferAdminRole` function allows the current token administrator to initiate the transfer of their role to a new administrator. The transfer process is a secure two-step procedure, requiring the new administrator to explicitly accept the role before the transfer is finalized. 1. **Initiate Role Transfer**: The current administrator calls the `transferAdminRole()` function on the `TokenAdminRegistry`, specifying the token address and the new administrator's address. 2. **Set Pending Administrator**: The `TokenAdminRegistry` contract verifies that the caller is the current administrator of the token and sets the new administrator as pending. The role will remain in this pending state until it is accepted. 3. **Accept the Role**: The new administrator must call the `acceptAdminRole()` function to finalize the transfer and assume the administrator role. The following sequence diagram illustrates the process of transferring the administrator role and how the new administrator must accept the role to complete the transfer. ### Setting the Token Pool The `setPool` function allows the token administrator to assign or update the token pool for a specific token in the **TokenAdminRegistry**. 1. **Set Token Pool**: The current administrator calls the `setPool()` function on the `TokenAdminRegistry`, providing the token address and the new pool address. 2. **Validate Pool**: If the new pool address is not `address(0)`, the contract validates that the provided pool supports the token by calling `isSupportedToken()` on the pool contract. 3. **Update or Remove Pool**: If validation succeeds, the token's pool is updated in the registry. Setting the pool to `address(0)` effectively delists the token from cross-chain operations. The sequence diagram below shows how the token administrator sets or updates the pool for a token. If the pool is set to `address(0)`, the token is delisted from cross-chain operations. ## Configuring the Token Pool The configuration of token pools includes adding new blockchains, setting remote pool addresses, and applying rate limits for cross-chain transfers. The following functions from the `TokenPool` contract are used for configuring token pools: ### `applyChainUpdates` - **Purpose**: This function is the primary method for configuring which blockchains the token pool supports and defining rate limits for cross-chain transfers. - **Details**: - It allows the token pool owner to add new chains or remove existing ones. - Configures the pool and token addresses for remote blockchains. - Sets rate limits for both outbound and inbound transfers. - **Usage**: - To add a new blockchain, the pool owner provides the remote chain selector, pool address, token address, and rate limiter configurations. - To remove a blockchain, the `allowed` flag is set to `false`, and the chain is removed from the list of supported chains. ### `addRemotePool` - **Purpose**: Adds a new remote pool address for a specific blockchain, enabling support for multiple pools per chain. - **Details**: - Allows adding multiple pools for a single chain selector, which is crucial during pool upgrades - Maintains support for in-flight messages from existing pools while adding new ones - Validates chain selector support and prevents duplicate pool additions - Each pool address is hashed and stored for efficient lookup - **Usage**: This function is particularly useful during pool upgrades, allowing seamless transitions between pool versions while maintaining transaction support. ### `setChainRateLimiterConfig` - **Purpose**: Configures the rate limits for outbound and inbound token transfers between blockchains. - **Details**: - The outbound rate limiter config controls how many tokens can be transferred out of the token pool per unit of time. - The inbound rate limiter config limits how many tokens can be transferred into the token pool per unit of time. - Only the pool owner or rate limit admin can call this function. - **Notes on Rate Limit Admin**: The rate limit admin is a designated address that is authorized to configure the rate limits for a token pool. This admin can be set by the pool owner using the `setRateLimitAdmin` function. If no rate limit admin is set, only the pool owner can modify the rate limits. You can retrieve the current rate limit admin address using the `getRateLimitAdmin` function. - **Usage**: This function adjusts rate limits to prevent excessive token transfers or overload of the token pool. Note: This function also supports disabling rate limits. ### `applyAllowListUpdates` - **Purpose**: Manages an allowlist of addresses permitted to interact with the token pool. - **Details**: - This function is only relevant if the token pool is access-controlled (i.e., an allowlist is enabled). - The allowlist ensures that only specific addresses, such as trusted addresses, can transfer tokens. - **Usage**: The pool owner uses this function to add or remove addresses from the allowlist, controlling who can transfer the tokens through CCIP. --- # Cross-Chain Token Standard - Upgradability (EVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/evm/upgradability Last Updated: 2025-05-19 Starting from **CCIP v1.5.1**, token pools support zero-downtime upgrades, allowing seamless transitions between versions while maintaining support for in-flight messages. This section outlines the requirements and considerations for upgrading token pools. ## Upgrade Paths When upgrading token pools, there are three scenarios to consider: ### Complete Blockchain Upgrade (Recommended) In this scenario, you upgrade all token pools across your connected blockchains to the latest version simultaneously. For example, if you have pools deployed on Ethereum and Polygon: - Both pools are upgraded to the latest version - All cross-chain messages continue processing without interruption - No manual intervention is required for in-flight transactions ### Partial Network Upgrade (Not Recommended) In this scenario, not all token pools are upgraded uniformly across blockchains (e.g., upgrading the token pool on Ethereum to version **v1.5.1** while retaining version **v1.5.0** on Polygon). This approach primarily affects v1.5.0 pools due to their strict source blockchain validation requirements. For example, consider an upgrade between Ethereum and Polygon: - If you upgrade only the Ethereum pool to **v1.5.1**, any messages that were already sent ("in-flight messages") through the **v1.5.0** pool will fail when they reach Polygon. This happens because **v1.5.0** pools are designed to accept messages only from a single configured remote pool address. When you upgrade the Ethereum pool, messages will come from its new address, which the Polygon pool won't recognize as valid. - To prevent message delivery failures: - Always upgrade all connected **v1.5.0** pools to **v1.5.1** simultaneously - Follow the detailed steps in the [Upgrading from v1.5.0](#upgrading-from-v150) section ### Adding New Blockchains You can safely deploy the latest pool version when expanding to new blockchains without affecting existing operations. For example, if you have pools on Ethereum and Polygon and want to expand to Avalanche: - Deploy the latest version on Avalanche - Existing pools continue operating normally - New cross-chain routes become available through Avalanche ## Upgrading from v1.5.0 Token pools in **v1.5.0** have a strict source validation mechanism in their `_validateReleaseOrMint` function that only accepts messages from a single configured remote pool address. Unlike **v1.5.1**, which can validate against multiple remote pools, **v1.5.0** pools reject messages if the source pool address doesn't match their configured remote pool. This limitation requires careful handling of upgrades to prevent in-flight messages from failing validation. ### 1. **Deploy New Pools** - Deploy v1.5.1 pool on Ethereum -> `0xNewPoolEth` - Deploy v1.5.1 pool on Polygon -> `0xNewPoolPoly` ### 2. **Configure New Pools** Configure both new pools to accept messages from both the old and new pools. This dual configuration ensures that after the upgrade, the new pools can still process any in-flight messages sent from the old pools before the upgrade.Use `applyChainUpdates` to set up the new pools: - On Ethereum's new pool: ```solidity ChainUpdate memory updateForPoly = ChainUpdate({ remoteChainSelector: POLYGON_SELECTOR, remotePoolAddresses: [abi.encode(0xOldPoolPoly), abi.encode(0xNewPoolPoly)], // Both old and new pools remoteTokenAddress: TOKEN_POLY_ADDRESS, outboundRateLimiterConfig: outboundConfig, inboundRateLimiterConfig: inboundConfig }); tokenPool.applyChainUpdates([], [updateForPoly]); ``` - On Polygon's new pool: ```solidity ChainUpdate memory updateForEth = ChainUpdate({ remoteChainSelector: ETH_SELECTOR, remotePoolAddresses: [abi.encode(0xOldPoolEth), abi.encode(0xNewPoolEth)], // Both old and new pools remoteTokenAddress: TOKEN_ETH_ADDRESS, outboundRateLimiterConfig: outboundConfig, inboundRateLimiterConfig: inboundConfig }); tokenPool.applyChainUpdates([], [updateForEth]); ``` ### 3. **Throttle Existing Pools** Before upgrading, minimize new token transfers by setting near-zero rate limits on the existing pools. Use `setChainRateLimiterConfig` on both existing pools: - On Ethereum's old pool: ```solidity // Set minimal rate limit on old pools RateLimiter.Config memory minConfig = RateLimiter.Config({ rate: 1, // 1 wei per second capacity: 1, // 1 wei capacity isEnabled: true // Keep enabled but effectively paused }); oldPoolEth.setChainRateLimiterConfig(POLYGON_SELECTOR, minConfig, minConfig); ``` - On Polygon's old pool: ```solidity oldPoolPoly.setChainRateLimiterConfig(ETH_SELECTOR, minConfig, minConfig); ``` ### 4. **Update Token Admin Registry** Use `setPool` to update the registry on both blockchains. This step switches token transfers to use the new pools. - On Ethereum: ```solidity tokenAdminRegistry.setPool(tokenAddress, 0xNewPoolEth); ``` - On Polygon: ```solidity tokenAdminRegistry.setPool(tokenAddress, 0xNewPoolPoly); ``` ### 5. **Handle Manual Executions** For any failed transactions, follow the manual execution process. ### 6. **Clean Up** After verifying all transactions are processed successfully: - Use `removeRemotePool` to remove old pool addresses - This prevents future messages from using the old pools - Only proceed with cleanup after confirming: - No pending transactions in CCIP Explorer - All manual executions are complete - New pools are operating correctly - On Ethereum's new pool: ```solidity newPoolEth.removeRemotePool(POLYGON_SELECTOR, abi.encode(0xOldPoolPoly)); ``` - On Polygon's new pool: ```solidity newPoolPoly.removeRemotePool(ETH_SELECTOR, abi.encode(0xOldPoolEth)); ``` --- # Cross-Chain Token Standard (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm Last Updated: 2025-05-19 This section provides detailed guidance for integrating tokens with Chainlink CCIP on SVM-based blockchains like Solana. - **[Architecture](/ccip/concepts/cross-chain-token/svm/architecture)**: Understand the specific architecture for CCTs on SVM chains. - **[Tokens](/ccip/concepts/cross-chain-token/svm/tokens)**: Learn about the requirements and compatibility for your SPL tokens. - **[Token Pools](/ccip/concepts/cross-chain-token/svm/token-pools)**: Explore the different types of token pool programs and how to use them. - **[Integration Guide](/ccip/concepts/cross-chain-token/svm/integration-guide)**: Determine your integration path based on mint authority control and deployment preferences. Includes decision tree and touch points with Chainlink governance. - **[Registration and Administration](/ccip/concepts/cross-chain-token/svm/registration-administration)**: Details on registering your token with CCIP and managing administrative roles on SVM. - **[Upgradability](/ccip/concepts/cross-chain-token/svm/upgradability)**: Information on upgrading token pool programs. --- # Cross-Chain Token Standard - Architecture (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm/architecture Last Updated: 2025-05-19 The Cross-Chain Token (CCT) architecture offers a streamlined, self-service approach to enabling cross-chain operations to any token. The CCT standard is token logic agnostic, meaning token developers can deploy pre-audited token pool contracts to turn any SPL-compatible token into a CCT or deploy their own custom token pool contracts for bespoke token use cases. Importantly, CCTs do not require token developers to inherit any CCIP-specific code within their token's smart contract. The CCT standard leverages the Chainlink Cross-Chain Interoperability Protocol (CCIP) for cross-chain transfers and allows token developers to configure, manage, and transfer tokens across multiple blockchains. The architecture diagrams below provide an overview of how CCT fits within the CCIP ecosystem, illustrating the interaction between key components. **Source Chain** **Destination Chain** The CCIP architecture for SVM-based blockchains (e.g., Solana) is described on this page, where we focus specifically on cross-chain token management. ## Architecture This document explains the four key components of CCIP's Cross-Chain Token architecture on SVM: 1. **Token**: SPL token requirements and registration for CCIP integration 2. **Token Pool**: Self-contained programs that execute cross-chain token operations 3. **Router**: Central coordinator for token administration and outbound transfers 4. **OffRamp**: Handler for inbound cross-chain token reception and release Each component builds on the previous, forming a secure, integrated system for cross-chain token transfers. The sections below are organized in dependency order - understanding each component provides the foundation for understanding how they work together in the complete architecture. ## Token In the Cross-Chain Token (CCT) architecture, the token is an SPL Mint managed by either the standard SPL Token program or Token-2022 (Token Extensions). To enable cross-chain transfers, every token requires: 1. **Registration**: Establishes a [token administrator](/ccip/concepts/cross-chain-token/overview#token-administrator) role and creates the TokenAdminRegistry [PDA](https://solana.com/docs/core/pda) that governs CCIP integration 2. **Pool-Specific Requirements**: Token requirements vary by chosen pool type: - **BurnMint pools**: Must support `mint_to` and `burn` instructions; mint authority must be transferable to the pool - **LockRelease pools**: Must support `transfer_checked` instruction; no mint authority requirements 3. **Compatibility**: Must support standard token interfaces including [Associated Token Accounts](https://spl.solana.com/associated-token-account) and provide decimal precision For detailed compatibility requirements and pool type selection guidance, see [Tokens documentation](/ccip/concepts/cross-chain-token/svm/tokens). For the complete registration process and administrator role management, see [Registration & Administration](/ccip/concepts/cross-chain-token/svm/registration-administration). ## Token Pool A Token Pool is a self-contained program responsible for executing cross-chain token transfers for a specific token. Each token must have exactly one pool per blockchain, deployed using one of three approaches: self-serve mode (recommended), self-deployed standard pools, or custom pools. **Cross-Chain Token Operations:** - **Source Chain Operations** (called by Router during `ccip_send`): - **BurnMint pools**: Transfer tokens from sender's [ATA](https://spl.solana.com/associated-token-account) to pool's ATA, then burn from pool's ATA - **LockRelease pools**: Lock tokens by transferring from sender's ATA to pool's [ATA](https://spl.solana.com/associated-token-account) - **Destination Chain Operations** (called by OffRamp during `execute`): - **BurnMint pools**: Mint tokens directly to receiver's [ATA](https://spl.solana.com/associated-token-account) - **LockRelease pools**: Release tokens by transferring from pool's [ATA](https://spl.solana.com/associated-token-account) to receiver's ATA For comprehensive implementation details, deployment approaches, and technical requirements, see [Token Pools documentation](/ccip/concepts/cross-chain-token/svm/token-pools). ## Router The CCIP Router program is central to cross-chain token management. It has two flows: 1. **Token Administration** - **TokenAdminRegistry (one [PDA](https://solana.com/docs/core/pda) per mint):** Stores the designated CCIP [token administrator](/ccip/concepts/cross-chain-token/overview#token-administrator) public key for a specific token mint (distinct from the SPL token's own mint authority) and references the token's pool [address lookup table](https://docs.solana.com/developing/lookup-tables) (ALT). The PDA is derived using seeds `[seed::TOKEN_ADMIN_REGISTRY, mint.key().as_ref()]`. This registry is created during token registration (see [Token section](#token)) and governs all subsequent CCIP operations for that token. - **Token Pool Address Lookup Table:** A Solana [address lookup table](https://docs.solana.com/developing/lookup-tables) that precisely defines the set of account addresses (including pool configuration [PDAs](https://solana.com/docs/core/pda), pool signer PDAs, and other required accounts) that are authorized for cross-chain operations with this token. For the complete list of required accounts and their exact order, see the [`set_pool` ALT requirements](/ccip/api-reference/svm/v1.6.0/router#address-lookup-table-requirements). - **Administrator Methods:** The token administrator can register or update cross-chain support for their mint through a sequence of instructions: 1. [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator): Initiated by the mint authority to designate a token administrator. 2. [`accept_admin_role_token_admin_registry`](/ccip/api-reference/svm/v1.6.0/router#accept_admin_role_token_admin_registry): Confirmation by the proposed administrator to accept the role. 3. [`set_pool`](/ccip/api-reference/svm/v1.6.0/router#set_pool): Links the token to a specific token pool configuration via an address lookup table, including writable account indexes for proper permission management. For detailed step-by-step procedures, sequence diagrams, and complete registration workflows, see [Registration & Administration](/ccip/concepts/cross-chain-token/svm/registration-administration). - **Pool configuration:** The TokenAdminRegistry maps each mint to exactly one token pool configuration through the ALT. This ensures that during cross-chain transfers, only the authorized token pool implementation can interact with the token. - **Security Architecture:** The one-to-one mapping between tokens and pools provides critical security guarantees: - **Single Authorized Pool:** Each token mint can only have one authorized pool configuration, preventing unauthorized token handling - **Atomic Pool Updates:** Changing pool configurations requires updating the entire ALT reference, ensuring consistent state - **Access Control:** Only the registry administrator can modify pool assignments, maintaining strict governance 2. **Lock or Burn Flow (when sending tokens cross-chain)** - [`ccip_send`](/ccip/api-reference/svm/v1.6.0/router#ccip_send) entrypoint: When a CCIP sender initiates a cross-chain token transfer, they invoke the [`ccip_send`](/ccip/api-reference/svm/v1.6.0/router#ccip_send) instruction on the Router, providing the token mint, amount, destination chain, receiver address, and other transaction parameters. - **Multi-step Validation:** The Router performs comprehensive validation: - Verifies the destination chain is not cursed (via RMN Remote CPI) - Checks sender authorization against allow-list if enabled - Validates token amounts are non-zero - Ensures proper account relationships and permissions - **Registry Validation:** The sender must provide the token pool account addresses as part of the transaction. The Router validates these accounts by checking them against the addresses stored in the address lookup table (ALT) referenced by the mint's TokenAdminRegistry PDA, including proper writable permissions. - **Fee Processing:** Before token interaction, the Router handles fee collection either in native SOL (with wrapping to WSOL) or supported fee SPL tokens through the fee quoter program. - **[CPI](https://docs.solana.com/developing/programming-model/calling-between-programs) to [`lock_or_burn_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#lock_or_burn_tokens):** After validation, the Router executes a CPI call to the token pool's [`lock_or_burn_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#lock_or_burn_tokens) instruction, passing the appropriate context including nonce information and destination chain details for the token pool to either lock tokens in its [Associated Token Account](https://spl.solana.com/associated-token-account) (ATA) or burn them from circulation, depending on the pool implementation (see [Token Pool section](#token-pool) for operation details). ## OffRamp The CCIP OffRamp program handles cross-chain token reception and release on the destination chain. This section focuses specifically on the **execute phase**. For the complete OffRamp functionality including commit phase and general message processing, see [OffRamp Components](/ccip/concepts/architecture/onchain/svm/components#offramp). During the execute phase, when processing messages containing token transfers, the OffRamp performs comprehensive token-specific validations and operations. 1. **Token Pool Authorization Validation** Before releasing or minting tokens, the OffRamp validates its authorization to interact with token pools: - **Router Authorization Check**: Verifies a special [PDA](https://solana.com/docs/core/pda) exists, derived using seeds `[seed::ALLOWED_OFFRAMP, source_chain_selector.to_le_bytes().as_ref(), offramp.key().as_ref()]` with the Router as the program ID. This PDA must be owned by the Router, ensuring only authorized OffRamps can trigger token operations. - **TokenAdminRegistry Validation**: Checks the TokenAdminRegistry [PDA](https://solana.com/docs/core/pda) (owned by the Router) to confirm the provided token pool accounts match the authorized configuration for the token mint. - **[Address Lookup Table](https://docs.solana.com/developing/lookup-tables) Verification**: Validates that the address lookup table and TokenAdminRegistry PDAs correspond to the mint's registry record, ensuring interaction with the correct token pool program. 2. **Token Pool Operations** Once authorization is confirmed, the OffRamp executes token operations: - **[CPI](https://docs.solana.com/developing/programming-model/calling-between-programs) to [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens)**: Makes a cross-program invocation to the token pool's [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens) instruction, passing the validated cross-chain token transfer details. - **Authorization Signer**: Uses the `external_token_pools_signer` [PDA](https://solana.com/docs/core/pda) as the CPI signer, derived using seeds `[seed::EXTERNAL_TOKEN_POOLS_SIGNER, pool_program.key().as_ref()]` with the OffRamp as the program ID. Token pools recognize this as an authorized caller from the OffRamp. - **Token Balance Verification**: Validates that the recipient's [Associated Token Account](https://spl.solana.com/associated-token-account) receives the correct amount of tokens as specified by the pool's response. 3. **Cross-Chain Token Operations** The OffRamp handles different token pool types based on their configuration: - **BurnMint token pools**: Mint new tokens directly to the recipient's [ATA](https://spl.solana.com/associated-token-account) - **LockRelease token pools**: Transfer (release) tokens from the pool's [ATA](https://spl.solana.com/associated-token-account) to the recipient's ATA The OffRamp ensures secure token operations by leveraging the same TokenAdminRegistry and address lookup table system established by the Router (see [Token section](#token)), maintaining the one-to-one mapping between tokens and their authorized pools while providing the necessary authorization for cross-chain token reception. ## Component Integration The four components work together to enable secure cross-chain transfers: 1. **Token Registration**: Creates the TokenAdminRegistry [PDA](https://solana.com/docs/core/pda) that authorizes all subsequent operations and links to the designated token pool 2. **Token Pool Deployment**: Provides the execution logic for transfers, with the specific pool type (BurnMint or LockRelease) determined by the token's requirements and operational preferences 3. **Router Coordination**: Manages token administration through the TokenAdminRegistry and validates outbound transfers by ensuring proper authorization against the pool's [Address Lookup Table](https://docs.solana.com/developing/lookup-tables) 4. **OffRamp Execution**: Validates authorization against the same TokenAdminRegistry system and executes inbound transfers through [CPI](https://docs.solana.com/developing/programming-model/calling-between-programs) calls to the authorized token pool This architecture ensures that every cross-chain token operation is governed by the initial registration decision, maintaining security and consistency across the entire transfer lifecycle. The one-to-one mapping between tokens and pools, established during registration and enforced by all components, provides the foundation for secure cross-chain token transfers. --- # Cross-Chain Token Standard - Tokens (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm/tokens Last Updated: 2025-05-19 This document provides essential guidance for integrating tokens with Chainlink CCIP on SVM-based blockchains, such as Solana, using the Cross-Chain Token (CCT) standard. It covers token compatibility requirements, decimal planning considerations, registration processes, and strategic guidance for selecting the right token pool type for your use case. ## Compatibility Before implementing CCIP support for your token, it's crucial to understand the compatibility requirements. These requirements ensure proper functionality of your token within the CCIP ecosystem. ### Mandatory Compatibility Requirements All tokens must meet these requirements for CCIP compatibility: 1. **Supported Token Programs**: Must use either the standard SPL Token program or Token-2022 (Token Extensions) program 2. **Interface Requirements**: Must support standard token interfaces: - **Associated Token Accounts (ATA)**: Required for all CCIP operations - **Decimals**: Must provide decimal precision via `mint.decimals` - **Program Identification**: Must be correctly referenced through `mint.to_account_info().owner` ### Decimal Planning When deploying your token across multiple blockchains, **decimal configuration is a critical strategic decision** that affects cross-chain transfer behavior and token supply management: **Key Decision:** Deploy your token with the **same number of decimals across all blockchains** whenever possible. This eliminates precision loss during cross-chain transfers. **Impact on Token Pools:** - **Different decimals**: Can result in precision loss and permanently locked/burned tokens - **Same decimals**: Enable perfect 1:1 cross-chain transfers For comprehensive guidance on decimal compatibility impacts, strategic recommendations, and technical implementation requirements, see [Decimal Compatibility Considerations](/ccip/concepts/cross-chain-token/svm/token-pools#decimal-compatibility-considerations). ### Token Requirements by Pool Type Your token's requirements depend on which token pool type you'll deploy, determined by your chosen [token handling mechanism](/ccip/concepts/cross-chain-token/svm/token-pools#token-handling-mechanisms). Refer to the token handling mechanisms table to understand which pool type you need for your SVM chain deployment. #### BurnMint Pool **Token Requirements:** - Must support `mint_to` and `burn` instructions - **Mint authority** must be transferable (directly to pool signer PDA or via SPL Token Multisig). See [BurnMint Pool mint authority configuration](/ccip/concepts/cross-chain-token/svm/token-pools#mint-authority-management) for detailed multisig requirements #### LockRelease Pool **Token Requirements:** - Must support `transfer_checked` instruction - **No mint authority requirements** (pool operates through transfers only) For detailed pool implementation, deployment approaches, and configuration requirements, see [Token Pools documentation](/ccip/concepts/cross-chain-token/svm/token-pools). ### Custom Token Pool Implementation Token developers can alternatively implement their own custom Token Pool Programs to support additional Token-2022 extensions not supported by the standard token pools. Custom implementations must adhere to CCIP's interface specifications while allowing developers to control which instructions are called when handling tokens. For technical requirements and implementation guidance for custom pools, see [Custom Token Pools](/ccip/concepts/cross-chain-token/svm/token-pools#custom-token-pools). ## Registration To enable your token for CCIP cross-chain transfers, you must register it and establish a token administrator role. This process ensures secure assignment of administrative control over your token's CCIP integration. ### Registration Requirements - **Self-Registration**: If you control the token's `mint_authority`, you can complete registration automatically - **Manual Registration**: If `mint_authority` is not accessible (e.g., set to `None` to cap supply), manual registration is available ### Registration Process The registration follows a secure two-step process: 1. **Propose Administrator**: Designate who will administer your token's CCIP integration 2. **Accept Role**: The proposed administrator must explicitly accept the role to complete registration For comprehensive guidance including detailed instructions, sequence diagrams, and administrator role management, see [Registration & Administration](/ccip/concepts/cross-chain-token/svm/registration-administration). ## Next Steps Once your token meets the compatibility requirements, you need to complete two key steps to enable cross-chain transfers: ### Register Your Token Complete the token registration process to establish administrative control over your token's CCIP integration. For detailed instructions, sequence diagrams, and administrator role management, see [Registration & Administration](/ccip/concepts/cross-chain-token/svm/registration-administration). ### Deploy and Configure Token Pool After registration, deploy and configure a token pool to enable cross-chain transfer operations. For comprehensive implementation guidance including deployment approaches (self-serve, self-deployed, custom), detailed architecture requirements, and step-by-step configuration instructions, see [Token Pools documentation](/ccip/concepts/cross-chain-token/svm/token-pools). --- # Cross-Chain Token Standard - Token Pools (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm/token-pools Last Updated: 2025-05-19 On SVM-based blockchains (e.g., Solana), a [token pool](/ccip/concepts/cross-chain-token/overview#token-pool) program mediates the cross-chain transfer of [cross-chain tokens](/ccip/concepts/cross-chain-token/overview) by coordinating with the SPL Token Program. This guide describes all the requirements that your token pools must meet. For architectural context and how token pools fit into the complete CCIP system, see [Architecture Overview](/ccip/concepts/cross-chain-token/svm/architecture). For token-specific requirements and pool type selection guidance, see [Tokens documentation](/ccip/concepts/cross-chain-token/svm/tokens). ## Deployment Approaches Before diving into the technical details, it's important to understand your three deployment options. Each approach offers different levels of control and responsibility based on your operational preferences and infrastructure requirements. ### Approach 1: Self-Serve Mode (Recommended) **What it is:** Chainlink Labs deploys and maintains the standard token pool programs (BurnMint and LockRelease) on Solana Devnet and Mainnet. You simply initialize your token pool from these existing deployed programs. **Key Benefits:** - No need to build, audit, or deploy pool programs yourself - Automatic access to upgrades and security fixes - Fastest time to deployment - Lower operational overhead **Who should use this:** [Token developers](/ccip/concepts/cross-chain-token/overview#token-developer) that want to integrate with CCIP quickly without managing infrastructure. **How it works:** 1. **Program Admin** (Chainlink's upgrade authority) deploys the program and initializes the Global Config PDA with: - `self_served_allowed: true` (enables user self-service) - Default router address for the network - Default RMN remote address for security 2. **Users** can initialize pools for their tokens by calling the [`initialize`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) instruction, provided they control the token's mint authority 3. New pools automatically inherit the default router and RMN addresses from the Global Config PDA ### Approach 2: Self-Deployed Standard Pools **What it is:** You build and deploy the standard Chainlink token pool programs yourself, giving you control over the upgrade authority. **Key Benefits:** - Full control over when to apply upgrades - Can customize deployment timing and parameters - Maintain upgrade authority for your pools - Use proven, audited standard implementations **Who should use this:** Projects that need control over upgrade timing or have specific governance requirements. **How it works:** You compile and deploy the Chainlink-provided [BurnMint](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/burnmint-token-pool) or [LockRelease](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/lockrelease-token-pool) programs to your chosen address, retaining upgrade authority. ### Approach 3: Custom Token Pools **What it is:** You build your own token pool program with custom logic while ensuring CCIP compatibility. **Key Benefits:** - Complete customization for unique token mechanics - Support for specialized features (rebasing, fee-on-transfer, etc.) - Full control over program logic **Who should use this:** Projects with unique token mechanics that standard pools cannot accommodate. **Requirements:** Must implement all [technical requirements](#technical-requirements) and follow the specifications detailed in the [Custom Token Pools](#custom-token-pools) section. Pay special attention to [decimal compatibility considerations](#decimal-compatibility-considerations) as custom pools require manual decimal conversion implementation. ## Token Handling Mechanisms Before implementing token pools, you need to choose the appropriate [token handling mechanism](/ccip/concepts/cross-chain-token/overview#token-handling-mechanisms) for your cross-chain token transfers. This strategic decision determines which combination of token pools you'll deploy on source and destination blockchains. The table below summarizes the different token handling mechanisms and the [recommended token pools](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs) to deploy for each scenario, ensuring a seamless token transfer process. | Token Handling Mechanism | Source Blockchain Token Pool Type | Destination Blockchain Token Pool Type | How it Works | | ------------------------ | --------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Burn & Mint | BurnMint | BurnMint | - Standard burn and mint mechanism for CCT transfers. | | Lock & Mint | LockRelease | BurnMint | - The source blockchain is the issuing blockchain.
- The LockRelease token pool must be deployed on the issuing blockchain. | | Burn & Unlock | BurnMint | LockRelease | - The destination blockchain is the issuing blockchain.
- The BurnMint token pool burns tokens on the source blockchain, and the LockRelease token pool unlocks tokens on the issuing blockchain. | | Lock & Unlock | LockRelease | LockRelease | - Tokens are locked on the source blockchain and unlocked on the destination blockchain.
- Can result in fragmented liquidity and requires careful management of liquidity across multiple blockchains to avoid stuck token transfers due to insufficient liquidity locked in the token pool on the destination blockchain. | ## Decimal Compatibility Considerations Before implementing token pools, it's critical to understand how decimal differences across blockchains can impact your cross-chain token transfers. This section provides strategic guidance for making informed decisions about token deployment and decimal configuration. For initial decimal planning considerations when deploying your token, see [Decimal Planning](/ccip/concepts/cross-chain-token/svm/tokens#decimal-planning). ### The Decimal Challenge When deploying a token across multiple blockchains, [token developers](/ccip/concepts/cross-chain-token/overview#token-developer) can configure different decimal places for each blockchain. However, this flexibility comes with important trade-offs: **Example Configuration:** - **Ethereum**: 18 decimals (0.123456789123456789) - **Solana**: 9 decimals (0.123456789) **What Happens During Transfers:** When transferring between blockchains with different decimal precision, CCIP automatically handles conversion but **must round numbers** to match the destination's configured precision. ### Impact on Token Supply Different decimal configurations can affect your token's total supply across chains: | Transfer Direction | Result | Supply Impact | | ------------------------ | --------------------------------------------------------------- | --------------------------------------------------------------- | | **High → Low Precision** | Precision loss due to rounding (e.g., 18 decimals → 9 decimals) | **Permanent loss**: Tokens burned/locked exceed tokens released | | **Low → High Precision** | No precision loss (e.g., 9 decimals → 18 decimals) | **No impact**: Perfect conversion possible | | **Equal Precision** | No precision loss (e.g., 18 decimals → 18 decimals) | **No impact**: Perfect 1:1 transfers | **Critical Considerations:** - **BurnMint Pools**: Lost precision results in **permanently burned tokens** on the source chain - **LockRelease Pools**: Lost precision results in **tokens permanently locked** in the source pool - Small amounts are most affected by rounding; large transfers typically see minimal percentage impact ### Strategic Recommendations **Primary Recommendation:** Deploy tokens with the **same number of decimals across all blockchains** whenever possible. This completely eliminates precision loss during cross-chain transfers. **When Different Decimals Are Necessary:** - Only use different decimals when required by blockchain limitations - Clearly communicate rounding risks to users in your UI - Consider implementing transfer warnings for high-to-low precision transfers - Plan for locked/burned token accumulation in your tokenomics **Development Considerations:** - Verify decimal configurations on both source and destination before going live - Test small-amount transfers to understand rounding behavior - Consider implementing minimum transfer amounts to minimize relative precision loss **Standard token pools** (BurnMint, LockRelease) handle decimal conversion automatically - no additional implementation required. **Custom token pools** must implement decimal conversion manually - see [Decimal Implementation Requirements](#decimal-implementation-requirements) for technical details. ## Standard Token Pools: An Overview Chainlink provides two standard token pool types that implement the token handling mechanisms described above. Each pool type implements specific logic for how tokens are managed during cross-chain transfers. ### SVM Program Architecture Overview On SVM-based blockchains like Solana, applications consist of **programs** (executable code) and **accounts** (data storage). Programs are stateless and store their data in separate accounts. **Program-Derived Addresses (PDAs)** are special accounts with addresses [deterministically derived](https://solana.com/docs/core/pda) from seeds and a program ID. PDAs have no private keys, allowing programs to "sign" for them. This enables secure, predictable account management. ### CCIP Token Pool Architecture Both BurnMint and LockRelease token pool programs use PDAs to organize data into two categories: - **Global PDAs**: Shared configuration for all pools deployed from this program - **Pool-Specific PDAs**: Individual configuration for each token's pool Each token has its own independent pool (one pool per mint), with pool-specific PDAs derived using the mint address as a seed. ### Program-Derived Addresses (PDAs) **Global Configuration:** - **Global Config PDA** (`seeds: ["config"]`): Program-wide settings including `self_served_allowed`, default router, and RMN remote addresses **Pool-Specific Configuration (per mint):** - **Pool State PDA** (`seeds: ["ccip_tokenpool_config", mint]`): Token-specific pool configuration, ownership, and operational settings - **Pool Signer PDA** (`seeds: ["ccip_tokenpool_signer", mint]`): Signing authority for token operations (burns, mints, transfers) - **Chain Config PDAs** (`seeds: ["ccip_tokenpool_chainconfig", chain_selector, mint]`): Per-destination chain configuration including rate limits and remote pool addresses The detailed sections below will help you understand the specific requirements and implementation details for each pool type. ### BurnMint Token Pool The BurnMint token pool provides burn and mint operations for cross-chain token transfers: #### How It Works **Outbound token transfer (when the SVM chain is the source chain):** - User transfers tokens to the pool's Associated Token Account (ATA) - Pool program burns the tokens using the standard SPL burn instruction - Tokens are permanently removed from circulation on the source chain **Inbound token transfer (when the SVM chain is the destination chain):** - Pool program mints new tokens using the SPL `mint_to` instruction - New tokens are created in the user's associated token account (ATA) - Pool program must control the `mint_authority` for the token #### BurnMint Pool Specifics The BurnMint token pool specializes in burn and mint operations, requiring specific mint authority management for minting operations. #### Mint Authority Management **Mint Authority Control:** The mint authority follows this lifecycle for BurnMint pools: **Required First: Pool Initialization** - You must control the token's `mint_authority` to initialize the pool - The [`initialize`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) instruction requires the mint authority as the signer - **The pool automatically sets the initializer as the pool owner during initialization** - Pool owner must create the [Associated Token Account (ATA)](https://www.solana-program.com/docs/associated-token-account) for the Pool Signer PDA before pool operations can begin **Ongoing: Pool Management** - As the pool owner, you can configure CCIP settings throughout the pool's lifetime: - Set up chain configurations for new remote chains - Configure or update rate limits for existing chains - Manage allowlists and other pool settings - Add or remove remote pool addresses - **Transfer pool ownership to any address using [`transfer_ownership`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#transfer_ownership)** **When Ready: Mint Authority Transfer** - Transfer the `mint_authority` when you're ready to enable the pool for minting operations - This enables the pool to mint tokens on inbound flows when the SVM chain is the destination - Choose one of two transfer mechanisms (multisig approach strongly recommended for production): **Option 1: Direct Transfer (Simple)** **Configuration:** - Transfer the `mint_authority` directly to the Pool Signer PDA **Best suited for:** - Development environments - Simple production deployments where the token exclusively integrates with CCIP - Scenarios with a single trusted actor interacting with the token **Limitations:** - Only the token pool can mint tokens - Reduced flexibility for future integrations **Option 2: Multisig Configuration (Recommended for Production)** **Configuration:** - Set up an M-of-N [SPL token multisig account](https://spl.solana.com/token#multisig-usage) as the mint authority - Include the Pool Signer PDA as a required signer **exactly M times** (where M is your threshold) - Include additional signers under your exclusive control as needed **SPL Token Multisig Requirements:** - **Threshold (M)**: Any value from 1 to N (your choice based on security needs) - **Total Signers (N)**: At least 2 signers total - **Pool Signer Repetition**: The Pool Signer PDA must appear **at least M times** in the signer list (typically exactly M times for optimal configuration) - **Additional Signers**: Remaining (N-M) signer slots for your governance control **Valid Configuration Examples:** | Threshold (M) | Total Signers (N) | Pool Signer PDA Occurrences | Your Signers | | ------------- | ----------------- | --------------------------- | ------------ | | 1 | 3 | 1 | 2 | | 2 | 4 | 2 | 2 | | 2 | 5 | 2 | 3 | | 3 | 6 | 3 | 3 | **Key benefits:** - Enables multiple authorized parties to mint tokens - Maintains governance control through other multisig signers - Provides extensibility for future integrations - Enhanced security through distributed control - Flexible threshold configuration for different security requirements **Technical Requirements:** - Must use [SPL token multisig](https://spl.solana.com/token#multisignatures) - other multisig implementations are not supported - The Pool Signer PDA must be included at least M times as a multisig signer - Pool must be able to mint tokens for cross-chain transfers without requiring additional manual signatures **Why Pool Signer Must Appear At Least M Times:** This design ensures the pool can always mint tokens for cross-chain transfers without requiring your manual intervention, while still maintaining your governance control: - **Autonomous Operation**: Pool operations always have sufficient signatures (≥M occurrences of Pool Signer) - **Governance Control**: Your additional signers can still initiate mint operations independent of the pool (≤N-M pool signer occurrences ensures ≥M governance signer slots remain) - **Flexible Security**: You choose the threshold (M) based on your security requirements, constrained by M ≤ N/2 - **Optimal Configuration**: Most deployments use exactly M pool signer occurrences for optimal balance **Multisig Configuration Validation:** The system validates your multisig configuration with these rules: - `total_signers >= 2` (must have at least 2 signers) - `threshold >= 1` (must require at least 1 signature) - `threshold <= total_signers` (can't require more signatures than available signers) - `pool_signer_occurrences >= threshold` (Pool Signer PDA must appear at least M times for autonomous operation) - `pool_signer_occurrences <= (total_signers - threshold)` (Pool Signer PDA cannot occupy all governance signer slots) **Constraint Implications:** - **Minimum Pool Signer Occurrences**: M times (enables autonomous pool operation) - **Maximum Pool Signer Occurrences**: N-M times (preserves governance control capability) - **Threshold Limit**: M ≤ N/2 (ensures both pool autonomy and governance control are possible) **Common Misconceptions:** To ensure proper implementation, avoid these common misconceptions about SPL token multisig configurations: **Incorrect**: "Only 1-of-N multisig is supported"\ **Correct**: "Any M-of-N multisig is supported with proper Pool Signer repetition" **Incorrect**: "Pool Signer appears once in the multisig"\ **Correct**: "Pool Signer appears at least M times (typically exactly M times) in the multisig" **Incorrect**: "Pool can operate with any multisig configuration"\ **Correct**: "Pool Signer must appear ≥M and ≤(N-M) times to ensure both autonomous operation and governance control capability" **Incorrect**: "Any threshold M is valid for any total signers N"\ **Correct**: "Threshold M must satisfy M ≤ N/2 to allow both pool autonomy and governance control" After this transfer, the pool can mint tokens when the SVM chain is the destination chain for cross-chain transfers. #### Access Control Understanding who can call which instructions is critical for secure pool operation and proper integration. This table shows the authorization model for all BurnMint token pool instructions, helping you understand the security boundaries and operational responsibilities. | Instruction Category | Instruction | Program Upgrade Authority | Pool Owner | Router Authority | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- | :-----------------------: | :--------: | :--------------: | | **Global Config** | [`init_global_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#init_global_config) | ✅ | ❌ | ❌ | | | [`update_self_served_allowed`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#update_self_served_allowed) | ✅ | ❌ | ❌ | | | [`update_default_router`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#update_default_router) | ✅ | ❌ | ❌ | | | [`update_default_rmn`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#update_default_rmn) | ✅ | ❌ | ❌ | | **Pool Lifecycle** | [`initialize`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) | ✅ OR Mint Authority | ❌ | ❌ | | **Ownership** | [`transfer_ownership`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#transfer_ownership) | ❌ | ✅ | ❌ | | | [`accept_ownership`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#accept_ownership) | ❌ | ✅\*\* | ❌ | | **Security** | [`transfer_mint_authority_to_multisig`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#transfer_mint_authority_to_multisig) | ✅ | ❌ | ❌ | | | [`configure_allow_list`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#configure_allow_list) | ❌ | ✅ | ❌ | | | [`remove_from_allow_list`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#remove_from_allow_list) | ❌ | ✅ | ❌ | | **Chain Config** | [`init_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#init_chain_remote_config) | ❌ | ✅ | ❌ | | | [`edit_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#edit_chain_remote_config) | ❌ | ✅ | ❌ | | | [`append_remote_pool_addresses`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#append_remote_pool_addresses) | ❌ | ✅ | ❌ | | | [`set_chain_rate_limit`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#set_chain_rate_limit) | ❌ | ✅ | ❌ | | | [`delete_chain_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#delete_chain_config) | ❌ | ✅ | ❌ | | **Cross-Chain** | [`lock_or_burn_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#lock_or_burn_tokens) | ❌ | ❌ | ✅ | | | [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens) | ❌ | ❌ | ✅ | **Notes:** - \* **accept_ownership**: Must be the `proposed_owner` from `transfer_ownership` - **Critical**: Pool Signer PDA MUST have mint capability (direct or via multisig) for minting operations - **Critical**: Pool owner must create the [Associated Token Account (ATA)](https://www.solana-program.com/docs/associated-token-account) for the Pool Signer PDA before pool operations can begin #### When to Use **Key Characteristics:** - Requires mint authority control for initialization, then transfer to pool for operations - Total supply can vary across chains during transfers - No liquidity management needed - Suitable for tokens designed to expand/contract supply **Choose BurnMint When:** - You control the `mint_authority` for your token (required for initialization) - You are willing to transfer mint authority to the pool after CCIP registration - Your token is designed to have variable supply across chains - You want to avoid liquidity management complexity - Your token participates in [Burn and Mint](/ccip/concepts/cross-chain-token/overview#burn-and-mint) or [Lock and Mint/Burn and Unlock](/ccip/concepts/cross-chain-token/overview#lock-and-mint) token handling mechanisms For detailed mint authority configuration options, see [Mint Authority Management](#mint-authority-management). ### LockRelease Token Pool The LockRelease token pool implements a lock-and-release strategy for cross-chain token transfers: #### How It Works **Outbound token transfer (when the SVM chain is the source chain):** - User transfers tokens to the pool's Associated Token Account (ATA) - Pool program "locks" tokens by holding them in its account - Tokens remain in circulation but are held by the pool as escrow - No tokens are destroyed - total supply remains constant **Inbound token transfer (when the SVM chain is the destination chain):** - Pool program transfers tokens from its ATA to the user's ATA - Pool must have sufficient token balance (liquidity) to fulfill transfers - No minting occurs - only transfers of existing tokens from the pool's reserves #### LockRelease Pool Specifics The LockRelease token pool specializes in lock and release operations, requiring liquidity management rather than mint authority control. Unlike BurnMint pools, LockRelease pools: - **Do not require mint authority transfer** - the original mint authority remains unchanged - **Rely on token reserves** - manage liquidity through the pool's token holdings - **Use rebalancer role** - designated address manages liquidity operations #### Liquidity Management **Pool Initialization:** - Program upgrade authority can always initialize pools - When self-serve is enabled (`self_served_allowed: true`), the token's mint authority can also initialize pools - **The pool automatically sets the initializer as the pool owner during initialization** - Pool is configured with liquidity acceptance settings during initialization - Pool owner must create the [Associated Token Account (ATA)](https://www.solana-program.com/docs/associated-token-account) for the Pool Signer PDA before pool operations can begin **Ongoing Liquidity Operations:** - **Rebalancer Role**: Designated address that can provide or withdraw pool liquidity - **Liquidity Acceptance**: Configurable setting (`can_accept_liquidity`) controls whether the pool accepts new liquidity - **Liquidity Operations**: - `provide_liquidity`: Rebalancer adds tokens to pool reserves - `withdraw_liquidity`: Rebalancer removes tokens from pool reserves (can transfer to other pools) **Liquidity Requirements:** - Pool must maintain sufficient token balance to fulfill cross-chain transfers - Insufficient liquidity will cause transfer failures - Rebalancer responsible for maintaining adequate liquidity across all supported chains #### Access Control Understanding who can call which instructions is critical for secure pool operation and proper integration. This table shows the authorization model for all LockRelease token pool instructions, helping you understand the security boundaries and operational responsibilities. | Instruction Category | Instruction | Program Upgrade Authority | Pool Owner | Router Authority | Rebalancer | | -------------------- | --------------------------------------------------------------------------------------------------------------------- | :-----------------------: | :--------: | :--------------: | :--------: | | **Global Config** | [`init_global_config`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#init_global_config) | ✅ | ❌ | ❌ | ❌ | | | [`update_self_served_allowed`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#update_self_served_allowed) | ✅ | ❌ | ❌ | ❌ | | | [`update_default_router`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#update_default_router) | ✅ | ❌ | ❌ | ❌ | | | [`update_default_rmn`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#update_default_rmn) | ✅ | ❌ | ❌ | ❌ | | **Pool Lifecycle** | [`initialize`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#initialize) | ✅ OR Mint Authority | ❌ | ❌ | ❌ | | **Ownership** | [`transfer_ownership`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#transfer_ownership) | ❌ | ✅ | ❌ | ❌ | | | [`accept_ownership`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#accept_ownership) | ❌ | ✅\*\* | ❌ | ❌ | | **Security** | [`configure_allow_list`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#configure_allow_list) | ❌ | ✅ | ❌ | ❌ | | | [`remove_from_allow_list`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#remove_from_allow_list) | ❌ | ✅ | ❌ | ❌ | | **Liquidity Mgmt** | [`set_rebalancer`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_rebalancer) | ❌ | ✅ | ❌ | ❌ | | | [`set_can_accept_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_can_accept_liquidity) | ❌ | ✅ | ❌ | ❌ | | | [`provide_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#provide_liquidity) | ❌ | ❌ | ❌ | ✅ | | | [`withdraw_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#withdraw_liquidity) | ❌ | ❌ | ❌ | ✅ | | **Chain Config** | [`init_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#init_chain_remote_config) | ❌ | ✅ | ❌ | ❌ | | | [`edit_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#edit_chain_remote_config) | ❌ | ✅ | ❌ | ❌ | | | [`append_remote_pool_addresses`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#append_remote_pool_addresses) | ❌ | ✅ | ❌ | ❌ | | | [`set_chain_rate_limit`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_chain_rate_limit) | ❌ | ✅ | ❌ | ❌ | | | [`delete_chain_config`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#delete_chain_config) | ❌ | ✅ | ❌ | ❌ | | **Cross-Chain** | [`lock_or_burn_tokens`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#lock_or_burn_tokens) | ❌ | ❌ | ✅ | ❌ | | | [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#release_or_mint_tokens) | ❌ | ❌ | ✅ | ❌ | **Notes:** - \* **accept_ownership**: Must be the `proposed_owner` from `transfer_ownership` - **Critical**: Pool owner must create the [Associated Token Account (ATA)](https://www.solana-program.com/docs/associated-token-account) for the Pool Signer PDA before pool operations can begin #### When to Use **Key Characteristics:** - Program upgrade authority can always initialize pools; mint authority can self-initialize when accessible - No mint authority required for ongoing operations (unlike BurnMint) - Fixed total supply per chain - tokens are only transferred, never created or destroyed - Requires active liquidity management and funding - Suitable for tokens where you want to retain mint authority control OR where mint authority is no longer accessible **Choose LockRelease When:** - You want to retain control of the token's mint authority (not transfer it to the pool) - Your token's mint authority is disabled/revoked (e.g., to cap token supply) or otherwise inaccessible - You prefer not to delegate minting capabilities to the token pool - You want to maintain fixed total supply on each blockchain - You can manage liquidity requirements and rebalancing across chains - You have operational capacity to monitor and maintain pool liquidity levels - Your token participates in [Lock and Mint/Burn and Unlock](/ccip/concepts/cross-chain-token/overview#burn-and-unlock) or [Lock and Unlock](/ccip/concepts/cross-chain-token/overview#lock-and-unlock) token handling mechanisms Both pool types are built on the shared [`base-token-pool`](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/base-token-pool) foundation, which provides common functionality including [rate limiting](/ccip/concepts/cross-chain-token/overview#token-pool-rate-limits), allowlists, cross-chain configuration, and event handling. ## Next Steps: Pool Configuration After deploying your token pool using any of the approaches above, you'll need to configure it for cross-chain operations. This includes: - Setting up remote chain configurations - Configuring rate limits for cross-chain transfers - Managing allowlists and access controls - Setting up liquidity management (for LockRelease pools) For detailed instructions on configuring your token pool parameters, see [Token Pool Configuration](/ccip/concepts/cross-chain-token/svm/registration-administration#token-pool-configuration-pool-side). ## Custom Token Pools If the standard BurnMint and LockRelease token pool programs don't meet your requirements, you can create a custom token pool program that is compatible with CCIP. This advanced approach gives you complete control over token handling logic while maintaining CCIP compatibility. ### When to Build Custom Token Pools **Consider building a custom token pool when:** - **Complex Token Mechanics**: Your token has unique behavior like rebasing, fee-on-transfer, or complex reward mechanisms - **Specialized Business Logic**: You need custom validation, compliance checks, or integration with other protocols - **Governance Requirements**: You need custom access control patterns **Standard pools are sufficient for:** - Basic SPL tokens with standard mint/burn/transfer functionality - Tokens that don't require custom logic during cross-chain transfers - SPL tokens and Token-2022 tokens ### Technical Requirements All custom token pools must implement the following requirements to be CCIP-compatible: #### Required Program-Derived Addresses (PDAs) Your custom token pool **must** use these exact PDA seeds so the CCIP Router can correctly derive and interact with your pool accounts: **Required PDAs and Their Seeds:** 1. **Global Config PDA** - Seeds: `["config"]` - Purpose: Program-wide settings including `self_served_allowed`, default router, and RMN addresses - Used by: Program admin for deployment configuration 2. **Pool State PDA** - Seeds: `["ccip_tokenpool_config", mint_address]` - Purpose: Token-specific pool configuration, ownership, and operational settings - Used by: Pool management and cross-chain operations 3. **Pool Signer PDA** - Seeds: `["ccip_tokenpool_signer", mint_address]` - Purpose: Signing authority for token operations (burns, mints, transfers) - Used by: Token transfer operations and mint authority control 4. **Chain Config PDAs** - Seeds: `["ccip_tokenpool_chainconfig", chain_selector, mint_address]` - Purpose: Per-destination chain configuration including rate limits and remote pool addresses - Used by: Cross-chain transfer validation and routing #### Mandatory Instructions When CCIP interacts with your custom token pools, it expects the presence of the following functions with exact signatures: 1. **For source chain operations (locking or burning tokens):** - [`lock_or_burn_tokens(ctx: Context, lock_or_burn: LockOrBurnInV1,) -> Result`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#lock_or_burn_tokens) - This function handles token operations when your chain is the source of a cross-chain transfer - Read the [API reference](/ccip/api-reference/svm/v1.6.0/base-token-pool#lockorburnoutv1) to learn more about the parameters 2. **For destination chain operations (releasing or minting tokens):** - [`release_or_mint_tokens(ctx: Context, release_or_mint: ReleaseOrMintInV1,) -> Result`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens) - This function handles token operations when your chain is the destination of a cross-chain transfer - Read the [API reference](/ccip/api-reference/svm/v1.6.0/base-token-pool#releaseormintinv1) to learn more about the parameters #### Integration with Base Token Pool All custom token pools should integrate the [`base-token-pool`](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/base-token-pool) library for core functionality: - **Ownership management**: Pool ownership and proposed ownership transfers - **Rate limiting**: Inbound and outbound rate limit controls per destination chain - **Cross-chain configuration**: Remote pool addresses and chain-specific settings - **Access control**: Allowlists and permission management - **Event handling**: Standardized event emissions for monitoring - **Decimal conversion**: Use `to_svm_token_amount` for proper decimal handling between chains #### Decimal Implementation Requirements All custom token pools **must** implement proper decimal conversion to handle tokens with different decimal configurations across blockchains. For strategic guidance on decimal compatibility decisions, see [Decimal Compatibility Considerations](#decimal-compatibility-considerations). **Required Implementation:** All standard token pools (BurnMint, LockRelease) automatically call [`to_svm_token_amount`](/ccip/api-reference/svm/v1.6.0/base-token-pool#to_svm_token_amount) during the [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens) flow. If you build a custom token pool, you **must** use [`to_svm_token_amount`](/ccip/api-reference/svm/v1.6.0/base-token-pool#to_svm_token_amount) from the base token pool library. **How [`to_svm_token_amount`](/ccip/api-reference/svm/v1.6.0/base-token-pool#to_svm_token_amount) Works:** 1. Reads the incoming amount from the source chain 2. Converts it from the source token decimals to the local token decimals 3. Returns a u64 amount or an error if the conversion exceeds u64::Max (maximum token supply on SVM-based blockchains) ### Implementation Examples #### Example 1: Rebasing Token Pool **Use Case**: Rebasing tokens adjust their supply periodically based on external parameters (e.g., price, yield). These tokens require custom logic to handle rebasing events during cross-chain transfers. **Implementation Approach**: - **Source Blockchain**: Instead of burning or locking a specific amount of tokens, track "underlying shares" that represent proportional ownership. In [`lock_or_burn_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#lock_or_burn_tokens), convert the user's tokens into an internal share count and store that in [`LockOrBurnOutV1.dest_pool_data`](/ccip/api-reference/svm/v1.6.0/base-token-pool#lockorburnoutv1) for the destination token pool. - **Destination Blockchain**: In [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens), parse the share count from [`ReleaseOrMintInV1.source_pool_data`](/ccip/api-reference/svm/v1.6.0/base-token-pool#releaseormintinv1) and convert those shares back into the current token amount based on the latest rebase. If your token supply has been rebased upward/downward since the transfer initiated, recalculate the final amount before minting or transferring to the user's ATA. #### Example 2: Fee-on-Transfer Token Pool **Use Case**: Tokens that deduct fees during transfers need custom logic to ensure cross-chain transfer amounts are accurate. **Implementation Approach**: - **Source Blockchain**: In [`lock_or_burn_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#lock_or_burn_tokens), calculate the net amount after fees and ensure the correct amount is locked/burned. Store the original intended amount in `dest_pool_data`. - **Destination Blockchain**: In [`release_or_mint_tokens`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#release_or_mint_tokens), use the original intended amount from `source_pool_data` to mint/release the correct amount, accounting for any destination-side fees. ### Development Resources **Implementation References:** - [BurnMint Token Pool](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/burnmint-token-pool) - Standard burn/mint implementation - [LockRelease Token Pool](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/lockrelease-token-pool) - Standard lock/release implementation - [Base Token Pool Library](https://github.com/smartcontractkit/chainlink-ccip/tree/v1.6.0-solana/chains/solana/contracts/programs/base-token-pool) - Shared functionality foundation **Testing and Validation:** - Test with CCIP Router integration to ensure proper PDA derivation - Validate decimal conversion handling for cross-chain transfers - Ensure rate limiting and access control functions work correctly - Test both source and destination chain operations thoroughly --- # Cross-Chain Token Standard - Integration Guide (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm/integration-guide This integration guide helps you determine the optimal path for integrating your SPL token with Chainlink CCIP on SVM-based blockchains like Solana. Follow the decision tree below to understand your specific requirements, deployment options, and when you can use self-service versus when you need Chainlink assistance. ## Prerequisites Before starting integration, ensure you meet these requirements: - **Token Compatibility**: Your token must meet [compatibility requirements](/ccip/concepts/cross-chain-token/svm/tokens#compatibility) - **Decimal Planning**: Plan [decimal configuration](/ccip/concepts/cross-chain-token/svm/tokens#decimal-planning) across all target blockchains - **Pool Type Selection**: Choose the appropriate token pool type for your use case: - **[BurnMint Pools](/ccip/concepts/cross-chain-token/svm/token-pools#burnmint-token-pool)**: Burn tokens on source, mint on destination. Requires transferring mint authority to pool. Best for tokens designed to have variable supply across chains. - **[LockRelease Pools](/ccip/concepts/cross-chain-token/svm/token-pools#lockrelease-token-pool)**: Lock tokens on source, release on destination. No mint authority transfer required. Requires liquidity management. Best for retaining mint authority control or capped-supply tokens. For detailed comparisons and technical requirements, see [Token Handling Mechanisms](/ccip/concepts/cross-chain-token/svm/token-pools#token-handling-mechanisms). ## Integration Overview CCIP Cross-Chain Token (CCT) integration involves two key phases: 1. **Token Registration**: Establishing a CCIP token administrator role 2. **Token Pool Deployment**: Setting up the pool program that handles cross-chain operations Your path through these phases depends on your **mint authority control** and chosen **[deployment approach](/ccip/concepts/cross-chain-token/svm/token-pools#deployment-approaches)**. ## Decision Flow Diagram The following diagram visualizes the complete decision process based on your mint authority control: ## Mint Authority Assessment The first critical decision point is understanding your mint authority control, as this determines your available options throughout the integration process. **Why mint authority control matters for token registration:** The Router's [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) instruction can only be called by the actual mint authority holder. The Router verifies onchain that the signer account matches the token's `mint_authority` field. If you cannot sign this instruction as the mint authority, the Router cannot verify your authorization onchain, requiring manual governance assistance. **You have direct control when:** - You hold the mint authority private key directly - You control a governance multisig that holds the mint authority (non-SPL token multisig) - You can execute mint authority operations through your governance process **You do NOT have direct control when:** - Mint authority is set to `None` (supply capped) - Mint authority is held by an SPL token multisig - Mint authority is controlled by another party you cannot coordinate with - You have lost access to the mint authority private key ## Integration Paths Based on your mint authority control, follow the appropriate integration path: ### Path A: Full Self-Service (Mint Authority Controlled) **When to use:** You have direct control over the mint authority **Registration Process:** - **Self-service registration**: Use [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) and [`accept_admin_role_token_admin_registry`](/ccip/api-reference/svm/v1.6.0/router#accept_admin_role_token_admin_registry) instructions (propose + accept) - **Immediate control**: Complete registration without external assistance - **Administrator flexibility**: Choose any address as the CCIP token administrator **Pool Deployment Options:** 1. **[Self-Serve Mode](/ccip/concepts/cross-chain-token/svm/token-pools#approach-1-self-serve-mode-recommended) (Recommended)** - Use Chainlink-deployed standard pool programs - Initialize your pool using [`initialize`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) instruction - Automatic governance-controlled upgrades - Lowest operational overhead 2. **[Self-Deployed Standard Pools](/ccip/concepts/cross-chain-token/svm/token-pools#approach-2-self-deployed-standard-pools)** - Deploy standard pool programs yourself - Control upgrade authority and timing - Initialize your own pools 3. **[Custom Token Pools](/ccip/concepts/cross-chain-token/svm/token-pools#approach-3-custom-token-pools)** - Build custom pool logic for specialized requirements - Full control over implementation and upgrades ### Path B: Assisted Registration + Self-Serve Pools (Mint Authority Not Controlled) **When to use:** You want to use self-serve mode pools, but you cannot call [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) as the mint authority **Registration Process:** - **Manual registration required**: Submit [registration request](https://chain.link/ccip-contact?v=Tokens:%20Token%20admin%20registration) - **CCIP governance assistance**: Router upgrade authority will propose the administrator using [`ccip_admin_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#ccip_admin_propose_administrator) - **Choose administrator**: You specify who should be the CCIP token administrator **Pool Deployment:** - **Assisted pool initialization**: Submit [registration request](https://chain.link/ccip-contact?v=Tokens:%20Token%20admin%20registration) to have the pool program upgrade authority initialize your pool - **Self-serve mode benefits**: Once initialized, you become the pool owner with full configuration control - **Governance-controlled upgrades**: Benefit from automatic security updates and features ### Path C: Full Manual Process (Custom or Self-Deployed) **When to use:** You cannot call [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) as the mint authority and want self-deployed or custom pools **Registration Process:** - Same manual registration process as Path B **Pool Deployment:** - **Deploy your own programs**: Full control over pool program deployment - **Control upgrade authority**: Manage your own upgrade process - **Initialize independently**: Use your upgrade authority to initialize pools ## Implementation Steps Once you've determined your path, follow these implementation steps: ### Step 1: Complete Registration - **Self-service path**: Follow [self-service registration](/ccip/concepts/cross-chain-token/svm/registration-administration#self-service-registration-flow) - **Assisted path**: Submit [registration request](https://chain.link/ccip-contact?v=Tokens:%20Token%20admin%20registration) ### Step 2: Deploy and Initialize Token Pool Choose your deployment approach: - **Self-serve mode**: Initialize pool from existing Chainlink-deployed programs - **If you can sign as mint authority**: Use the [`initialize`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) instruction directly - **If you cannot sign as mint authority** (mint authority is `None`, held by SPL token multisig, or controlled by another party): Submit [registration request](https://chain.link/ccip-contact?v=Tokens:%20Token%20admin%20registration) for assisted initialization - **Self-deployed**: Deploy standard pool programs yourself, then initialize pool from your deployed program - **Custom**: Implement and deploy custom pool following [technical requirements](/ccip/concepts/cross-chain-token/svm/token-pools#custom-token-pools), then initialize pool from your deployed program ### Step 3: Create Associated Token Account - Create the [Associated Token Account (ATA)](https://www.solana-program.com/docs/associated-token-account) for the Pool Signer PDA - **Required**: Pool operations cannot begin without this ATA - The ATA will hold tokens for LockRelease pools or serve as the burn account for BurnMint pools ### Step 4: Transfer Mint Authority (BurnMint Pools Only) Choose one of two approaches: - **Direct Transfer**: Transfer `mint_authority` directly to the Pool Signer PDA (simple, pool-only minting) - **Multisig Setup (Recommended)**: Create an M-of-N [SPL token multisig](https://spl.solana.com/token#multisig-usage) with Pool Signer PDA appearing at least M times as a signer (flexible, multiple authorized minters) See [Mint Authority Management](/ccip/concepts/cross-chain-token/svm/token-pools#mint-authority-management) for detailed configuration options. ### Step 5: Create Address Lookup Table (ALT) - Create an ALT containing all required accounts for your pool operations - Must include required accounts in specific order as detailed in the [`set_pool` API reference](/ccip/api-reference/svm/v1.6.0/router#address-lookup-table-requirements) - See the complete [ALT requirements table](/ccip/api-reference/svm/v1.6.0/router#address-lookup-table-requirements) for exact account order and derivations ### Step 6: Link Pool to Token Registry - Call [`set_pool`](/ccip/api-reference/svm/v1.6.0/router#set_pool) instruction on the Router - Links your pool's ALT to your token in the TokenAdminRegistry - Enables CCIP to route cross-chain transfers to your pool ### Step 7: Configure Pool Settings - Complete [remote chain configuration](/ccip/concepts/cross-chain-token/svm/registration-administration#remote-chain-configuration) - Set up [rate limits and security settings](/ccip/concepts/cross-chain-token/svm/registration-administration#rate-limits-and-security) - **For LockRelease pools**: Configure [liquidity management](/ccip/concepts/cross-chain-token/svm/registration-administration#liquidity-management-lockrelease-pools-only) ## Next Steps After determining your integration path: 1. **Review Detailed Documentation**: - [Architecture](/ccip/concepts/cross-chain-token/svm/architecture): Understand component interactions - [Tokens](/ccip/concepts/cross-chain-token/svm/tokens): Token requirements and compatibility - [Token Pools](/ccip/concepts/cross-chain-token/svm/token-pools): Pool implementation details - [Registration & Administration](/ccip/concepts/cross-chain-token/svm/registration-administration): Complete registration and configuration procedures 2. **For Assisted Paths**: Submit your [registration request](https://chain.link/ccip-contact?v=Tokens:%20Token%20admin%20registration) with detailed information about your token and requirements 3. **For Self-Service Paths**: Begin with token registration following the appropriate workflow for your chosen deployment approach --- # Cross-Chain Token Standard - Registration & Administration (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm/registration-administration Last Updated: 2025-05-19 When token administrators want to register their SPL token for Cross‐Chain Token (CCT) operations, they interact with both the Router program and Token Pool programs. This guide covers both token registration/administration (Router side) and token pool configuration (Pool side). ## Token Registration & Administration (Router Side) This section covers all operations performed through the Router program to manage token administrator roles and basic token configuration. ### Router Access Control for Token Administration Understanding who can call which Router instructions is critical for secure token administration. This table shows the authorization model for all token admin registry instructions: | Instruction | Mint Authority | Router Upgrade Authority | Current Token Admin | Pending Token Admin | | ---------------------------------------------------------------------------------------------------------------------------- | :------------: | :----------------------: | :-----------------: | :-----------------: | | [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) | ✅ | ❌ | ❌ | ❌ | | [`owner_override_pending_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_override_pending_administrator) | ✅ | ❌ | ❌ | ❌ | | [`ccip_admin_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#ccip_admin_propose_administrator) | ❌ | ✅ | ❌ | ❌ | | [`accept_admin_role_token_admin_registry`](/ccip/api-reference/svm/v1.6.0/router#accept_admin_role_token_admin_registry) | ❌ | ❌ | ❌ | ✅ | | [`transfer_admin_role_token_admin_registry`](/ccip/api-reference/svm/v1.6.0/router#transfer_admin_role_token_admin_registry) | ❌ | ❌ | ✅ | ❌ | | [`set_pool`](/ccip/api-reference/svm/v1.6.0/router#set_pool) | ❌ | ❌ | ✅ | ❌ | **Notes:** - **Mint Authority**: The SPL token's `mint_authority` holder - **Router Upgrade Authority**: The Router program's upgrade authority - **Current Token Admin**: The active CCIP [token administrator](/ccip/concepts/cross-chain-token/overview#token-administrator) - **Pending Token Admin**: [Token administrator](/ccip/concepts/cross-chain-token/overview#token-administrator) proposed but not yet accepted **Focus on Self-Service Registration:** The following sections focus on the **self-service registration flow** where you control your token's mint authority. While the Router also supports `ccip_admin_propose_administrator` for cases where mint authority is not accessible, we detail the self-service approach as it's the primary registration method for most token developers. ### Self-Service Registration Flow If your SPL token supports an automatic way to determine an admin using onchain verifiable data (e.g., you hold the mint authority), you can complete self-registration in a permissionless manner by calling the Router's [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) instruction: 1. **Admin Initiates Registration:** The token admin calls the Router's [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) instruction, passing in the mint and the proposed administrator's public key. This instruction initializes a `TokenAdminRegistry` PDA for this mint. 2. **Determine Administrator:** The Router ensures that the caller is the SPL token's mint authority, confirming they have the right to set a CCIP admin. 3. **Propose Administrator:** The Router sets the proposed administrator as the pending administrator. The token admin is not fully recognized yet; they remain `pending`. The `TokenAdminRegistry` now stores this user as `pending_administrator`. The next step is to accept the role. **Self-Service Registration Requirements:** - **Caller Authorization**: Only the SPL token's `mint_authority` can call [`owner_propose_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_propose_administrator) - **Registry State**: TokenAdminRegistry PDA must not already exist for this mint - **Result**: Creates new TokenAdminRegistry PDA with proposed administrator in pending state ### Non-Self-Service Registration Flow For token programs that do not have a standard way to identify an administrator using onchain verifiable data (e.g., no direct mint authority check), the token developer can manually initiate the registration by submitting a request above. ### Administrator Role Management #### Proposing the Administrator The following sequence diagram illustrates the process of proposing the administrator. #### Overriding Pending Administrator If you need to change the pending administrator before they accept the role, you can use the [`owner_override_pending_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_override_pending_administrator) instruction. This is useful when you initially proposed the wrong administrator or need to update the pending administrator for other reasons. 1. **Override Pending Administrator:** The mint authority calls the Router's [`owner_override_pending_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_override_pending_administrator) instruction, passing in the mint and the new proposed administrator's public key. 2. **Validate Authority:** The Router ensures that the caller is the SPL token's mint authority and that no administrator has been accepted yet (the `administrator` field must be zero). 3. **Update Pending Administrator:** The Router replaces the existing `pending_administrator` with the new proposed administrator. The previous pending administrator can no longer accept the role. **Override Requirements:** - **Caller Authorization**: Only the SPL token's `mint_authority` can call [`owner_override_pending_administrator`](/ccip/api-reference/svm/v1.6.0/router#owner_override_pending_administrator) - **Registry State**: TokenAdminRegistry PDA must exist but have no accepted administrator yet (`administrator` field is zero) - **Result**: Updates the `pending_administrator` field with the new proposed administrator #### Accepting the Administrator Role Once the administrator has been proposed and is pending, they must accept the role to complete the registration process. This step finalizes the administrator's assignment. 1. **Pending Administrator Calls [`accept_admin_role_token_admin_registry`](/ccip/api-reference/svm/v1.6.0/router#accept_admin_role_token_admin_registry)** The pending admin invokes [`accept_admin_role_token_admin_registry()`](/ccip/api-reference/svm/v1.6.0/router#accept_admin_role_token_admin_registry) on the Router program, specifying the mint account in the transaction context. 2. **Finalize Registration** - The Router checks that the caller's public key matches the `pending_administrator` field in the `TokenAdminRegistry` PDA. - If authorized, the Router sets `administrator = pending_administrator` and clears `pending_administrator`. At this point, the role is fully active. Below is the sequence diagram illustrating how the pending administrator interacts with the Router program to complete registration. #### Transfer Administrator Role The [`transfer_admin_role_token_admin_registry`](/ccip/api-reference/svm/v1.6.0/router#transfer_admin_role_token_admin_registry) instruction allows the current token administrator to transfer their role to a new administrator. This transfer process is secure and involves two steps, requiring the new admin to accept it explicitly before finalization. 1. **Initiate Role Transfer** - The current admin calls [`transfer_admin_role_token_admin_registry(new_admin)`](/ccip/api-reference/svm/v1.6.0/router#transfer_admin_role_token_admin_registry) on the Router, passing the new administrator's public key. - The Router checks that the caller is the token's existing administrator. If so, it sets `pending_administrator = new_admin` in the `TokenAdminRegistry` (PDA). 2. **Pending Administrator** - The registry is now pending. The existing admin will still be active until the new admin accepts. 3. **Accept the Role** - The new administrator must call [`accept_admin_role_token_admin_registry()`](/ccip/api-reference/svm/v1.6.0/router#accept_admin_role_token_admin_registry) to finalize the transfer. - If `authority.key()` matches `pending_administrator`, the Router updates `administrator = pending_administrator`. Otherwise, it will fail. - Once accepted, the new administrator can set or modify the token pool. Below is a sequence diagram showing how the transfer is requested, followed by how the new admin must accept to complete the handover. ### Setting the Token Pool On SVM-based blockchains (e.g., Solana), the [`set_pool`](/ccip/api-reference/svm/v1.6.0/router#set_pool) instruction enables a token administrator to map a given mint to the pool lookup table that defines how that token is handled in cross-chain transfers. This instruction modifies the `TokenAdminRegistry` PDA so the Route program knows which token pool accounts can lock or burn tokens on the source chain (and release or mint them on the destination). 1. **Set Token Pool:** The current administrator calls the [`set_pool`](/ccip/api-reference/svm/v1.6.0/router#set_pool) instruction on the Router program, passing in: - The token mint (for which we are configuring cross-chain transfers). - The `pool_lookuptable` account (the Address Lookup Table containing the mandatory PDAs and program addresses for that token pool). - A list of "writable indexes" indicating which lookup table entries should be marked as writable during cross-chain transfers. These indexes specify positions in the ALT that require write permission for successful transaction execution. 2. **Reset Old Pool, Apply New Settings:** Under the hood, the Router updates the `TokenAdminRegistry` PDA: - Overwrites any previously stored Address Lookup Table reference. - Resets the old permission bits, then enables the specified "writable indexes." - This ensures the new token pool has the correct set of PDAs with the correct writable permissions. 3. **Validate or Delist:** - If the new `pool_lookuptable` is not the zero address, the Router checks that this lookup table has at least the minimal set of addresses required for cross-chain transfers. For the complete list of required accounts and their exact order, see the [`set_pool` ALT requirements](/ccip/api-reference/svm/v1.6.0/router#address-lookup-table-requirements). If valid, the token becomes enabled for cross-chain transfers. - If the `pool_lookuptable` is the zero address, the token is effectively delisted from CCIP, meaning no new cross-chain transfers can occur. The sequence diagram below explains how the [`set_pool`](/ccip/api-reference/svm/v1.6.0/router#set_pool) instruction updates the `TokenAdminRegistry` PDA and either enables or delists the token for cross-chain transfers. ## Token Pool Configuration (Pool Side) Once you have registered your token and set the token pool via the Router, you need to configure the specific pool parameters. These operations are performed directly on the Token Pool programs, not the Router. ### Pool Initialization Before any other configuration can occur, you must initialize your token pool: 1. **Initialize Token Pool** - **Instruction:** [`initialize`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) - **Use Case:** Create the pool state PDA for your specific mint. This is the first mandatory step that must be completed before any other pool configuration operations. - **Authorization:** Program upgrade authority can always initialize pools. When self-serve is enabled (`self_served_allowed: true`), the token's mint authority can also initialize pools. - **Requirements:** Pool owner must create the [Associated Token Account (ATA)](https://www.solana-program.com/docs/associated-token-account) for the Pool Signer PDA before pool operations can begin. - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#initialize) to learn more. ### Remote Chain Configuration Configure your token pool for specific destination blockchains: 1. **Initialize a Remote Configuration** - **Instruction:** [`init_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#init_chain_remote_config) - **Use Case:** Create a new onchain account (PDA derived from the chain selector and token pool program ID) holding configuration details for a specific remote blockchain (e.g., remote token address). - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#init_chain_remote_config) to learn more. 2. **Edit an Existing Remote Configuration** - **Instruction:** [`edit_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#edit_chain_remote_config) - **Use Case:** Update the entire stored configuration for a remote chain (`RemoteConfig`), including the remote token address and decimals. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#edit_chain_remote_config) to learn more. 3. **Add Remote Pool Address** - **Instruction:** [`append_remote_pool_addresses`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#append_remote_pool_addresses) - **Use Case:** Register one or more remote pool addresses for the same remote chain. This can happen when you deploy a new pool version on that blockchain but keep the old address functional until all in-flight messages are processed. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#append_remote_pool_addresses) to learn more. 4. **Remove Remote Pool Address** - **Instruction:** [`edit_chain_remote_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#edit_chain_remote_config) - **Use Case:** Remove one or more remote pool addresses from an existing remote chain configuration. Call this instruction with a new `RemoteConfig` that excludes the addresses you want to remove. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#edit_chain_remote_config) to learn more. 5. **Remove Chain Config** - **Instruction:** [`delete_chain_config`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#delete_chain_config) - **Use Case:** Remove the remote blockchain configuration PDA to stop supporting it permanently. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#delete_chain_config) to learn more. ### Rate Limits and Security Configure transfer limits and access controls for your token pool: 1. **Configure Rate Limits** - **Instruction:** [`set_chain_rate_limit`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#set_chain_rate_limit) - **Use Case:** Apply or modify the remote blockchain's inbound/outbound rate-limit configuration. This uses a token bucket algorithm in which you can configure the capacity (maximum tokens) and rate (tokens per second refill rate) for both inbound and outbound transfers. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#set_chain_rate_limit) to learn more. 2. **Optional Allowlist** - **Instructions:** [`configure_allow_list`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#configure_allow_list), [`remove_from_allow_list`](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#remove_from_allow_list) - **Use case:** You can add or remove addresses if the pool has an allowlist. When the allowlist is enabled, only addresses that appear on it can initiate cross‐chain transfers. This provides an additional layer of access control for who can initiate cross-chain token transfers. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/burn-mint-token-pool#configure_allow_list) to learn more. For detailed authorization requirements, see the access control tables for [BurnMint pools](/ccip/concepts/cross-chain-token/svm/token-pools#access-control) and [LockRelease pools](/ccip/concepts/cross-chain-token/svm/token-pools#access-control-1). ### Liquidity Management (Lock/Release Pools Only) Configure liquidity providers and settings for Lock/Release token pools: #### Liquidity Configuration 1. **Set Rebalancer** - **Instruction:** [`set_rebalancer`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_rebalancer) - **Use Case:** Configure which public key is authorized to add or withdraw liquidity from the pool. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_rebalancer) to learn more. 2. **Configure Liquidity Acceptance** - **Instruction:** [`set_can_accept_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_can_accept_liquidity) - **Use Case:** Enable or disable whether the pool can accept incoming liquidity via the [`provide_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#provide_liquidity) instruction. - **Authorization:** Pool Owner only - Read the [API reference](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#set_can_accept_liquidity) to learn more. #### Liquidity Operations 1. **Provide Liquidity** - **Instruction:** [`provide_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#provide_liquidity) - **Use Case:** Add tokens to the pool's reserves to enable cross-chain releases. The rebalancer transfers tokens from their account to the pool's Associated Token Account. - **Authorization:** Rebalancer only - **Requirements:** Pool must have `can_accept_liquidity` enabled - Read the [API reference](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#provide_liquidity) to learn more. 2. **Withdraw Liquidity** - **Instruction:** [`withdraw_liquidity`](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#withdraw_liquidity) - **Use Case:** Remove tokens from the pool's reserves. Can be used to transfer liquidity between pools by setting the destination as another pool's rebalancer. - **Authorization:** Rebalancer only - **Requirements:** Pool must have `can_accept_liquidity` enabled - Read the [API reference](/ccip/api-reference/svm/v1.6.0/lock-release-token-pool#withdraw_liquidity) to learn more. **Note:** Lock/Release token pools require active liquidity management to ensure sufficient tokens are available for releases. Insufficient liquidity will cause cross-chain transfer failures. For complete authorization details, see the [LockRelease Access Control table](/ccip/concepts/cross-chain-token/svm/token-pools#access-control-1). --- # Cross-Chain Token Standard - Upgradability (SVM) Source: https://docs.chain.link/ccip/concepts/cross-chain-token/svm/upgradability Last Updated: 2025-05-19 Token pool upgradability on SVM-based blockchains (e.g., Solana) depends entirely on which [deployment approach](/ccip/concepts/cross-chain-token/svm/token-pools#deployment-approaches) you choose. Your upgrade responsibilities and capabilities vary significantly based on whether you use self-serve mode, deploy standard pools yourself, or build custom pools. ## Upgrade Approaches by Deployment Type Your upgrade experience depends entirely on which deployment approach you chose when setting up your token pool: ### Approach 1: Self-Serve Mode (Recommended - No Action Required) **How it works:** This approach follows the same pattern as **SPL Token programs** on Solana: you create and manage your own token pools (like creating your own mints), but the underlying token pool programs are maintained by the program authority through established governance processes. **What it means for upgrades:** - **Governance-controlled upgrades**: The standard BurnMint and LockRelease programs are upgraded through CCIP's [governance process](/ccip/concepts/architecture/onchain/svm/upgradability) involving multisig approvals, timelock reviews, and node operator oversight - **Seamless updates**: Security fixes and feature updates are deployed through the governance process without breaking your pool configuration or mint authority - **No action required**: You don't manage program upgrades, similar to how you don't upgrade the SPL Token program when using standard Solana tokens - **Preserved functionality**: Updates happen at the program level while your individual pool state and configuration remain intact **Your responsibilities:** - **None** - program upgrades are handled through CCIP governance - Monitor CCIP announcements for new features or breaking changes - Test your integration after major protocol updates **Benefits:** - Always up-to-date with latest security patches through proven governance - No operational overhead for program upgrade management - Professional maintenance through decentralized governance - Immediate access to new CCIP features ### Approach 2: Self-Deployed Standard Pools (User-Controlled Upgrades) **What it means for upgrades:** - **You control the upgrade authority** for your deployed standard pool programs - **Manual upgrades**: You decide when to apply updates from the Chainlink repository - **Full control**: You can customize timing, test extensively, and coordinate with your governance **Your responsibilities:** - Monitor Chainlink repository for new releases - Test upgrades in development environments - Execute upgrade transactions using Solana CLI or Anchor tooling - Coordinate upgrades with your governance/operational processes ### Approach 3: Custom Token Pools (User-Controlled Upgrades) **What it means for upgrades:** - **You control the upgrade authority** for your custom pool program - **Custom development**: You develop your own upgrades and improvements - **Complete responsibility**: All upgrade planning, development, and execution is your responsibility **Your responsibilities:** - Develop custom upgrade code - Ensure CCIP compatibility after upgrades - Test thoroughly with CCIP Router integration - Execute upgrade transactions - Monitor for breaking changes in CCIP protocol updates ## In-Place Upgrades (Approaches 2 & 3 Only) When you control the upgrade authority, in-place upgrades provide significant benefits: ### Benefits of In-Place Upgrades 1. **Retain Mint Authority (Burn and Mint Pools):** SPL tokens have a single `mint_authority`. When you delegate this authority to a Burn and Mint token pool program, that program holds the exclusive rights to mint tokens. As detailed in the [Mint Authority Management](/ccip/concepts/cross-chain-token/svm/token-pools#mint-authority-management) section, in-place upgrades allow you to: - **Maintain Control:** Upgrade the pool code without reassigning the mint authority, which avoids the complexity of modifying the multisig membership or reconfiguring authorities. - **Reduce Overhead:** Avoid manual reassignments, such as adding a new pool to the multisig and removing the old one, thereby simplifying maintenance and reducing the risk of errors. 2. **Preserve User Workflow (All Pools):** On SVM-based blockchains, instructions require specifying all involved accounts, including the token pool program ID. If you redeploy your pool to a new Program ID, users and integrators must update their transaction code to reference it. In-place upgrades keep the original ID so that existing references and account parameters remain valid. ## Best Practices for User-Controlled Upgrades ### Governance and Security 1. **Secure Upgrade Authority Management** - Configure the upgrade authority to a trusted multisig or governance program - Never use single-key control for production pools - Implement time delays and approval processes for critical upgrades 2. **Plan Governance Processes** - Establish clear governance rules for when and how upgrades are approved - Consider community input for significant changes - Document upgrade procedures and emergency response plans ### Development and Testing 3. **Comprehensive Testing** - Develop and validate pool updates in test environments first - Test integration with CCIP Router and cross-chain operations - Verify all existing functionality works after upgrade - Test edge cases and error conditions 4. **Backward Compatibility** - Design upgrades to maintain compatibility with existing state accounts and PDAs - Ensure existing user configurations remain valid - Plan migration strategies for breaking changes ### Execution 5. **Upgrade Execution** - Use Solana CLI or Anchor tooling to execute upgrade transactions - Perform upgrades during low-traffic periods when possible - Have rollback plans ready in case of issues - Monitor system behavior immediately after upgrades 6. **Prefer Upgrades Over Redeployment** - In-place upgrades preserve all existing references and authorities - Redeployment forces re-registration, mint authority reassignment, and user workflow updates - Only redeploy when absolutely necessary (e.g., fundamental architecture changes) --- # CCIP Manual Execution Source: https://docs.chain.link/ccip/concepts/manual-execution Last Updated: 2025-07-21 In general, messages are successfully delivered and processed via CCIP as described in the [Architecture](/ccip/concepts/architecture) page. However, some exceptional conditions might require users to manually execute the transaction on the destination blockchain: - The receiver contract on the destination blockchain reverted due to an unhandled exception such as a logical error. - For token pools, if the combined execution of the required functions (`balanceOf` checks and `releaseOrMint`) exceeds the default gas limit of **90,000 gas** on the destination blockchain, CCIP execution will fail. Read the Token pools [common requirements](/ccip/concepts/cross-chain-token/evm/token-pools#common-requirements) to learn more. - The receiver contract on the destination blockchain reverted due to the gas limit being insufficient to execute the triggered function (Note: The gas limit value is set in the [extraArgs](/ccip/api-reference/evm/v1.6.2/client#genericextraargsv2) param of the message). The flowchart below displays the process of a cross-chain transaction, detailing the steps involved in the manual execution: ## CCIP execution 1. A sender contract or EOA initiates a CCIP message on the source blockchain. 2. [CCIP Committing DON](/ccip/concepts/architecture/offchain/overview#commit-ocr-process) awaits [finality](/ccip/concepts/architecture/key-concepts#blockchain-finality) on the source blockchain. 3. Post finality, the [CCIP Committing DON](/ccip/concepts/architecture/offchain/overview#commit-ocr-process) assembles a batch of eligible transactions, computes a Merkle root, and records it to the OffRamp contract on the destination blockchain. 4. After successful verification, the [Risk Management Network](/ccip/concepts/architecture/offchain/risk-management-network) blesses the committed Merkle root. 5. After the committed Merkle root is blessed, the [CCIP Executing DON](/ccip/concepts/architecture/offchain/overview#executing-ocr-process) proceeds with the execution on the destination blockchain. 6. The execution on the destination blockchain works as follows: 1. If the message involves token transfers, the tokens are first transferred to the receiver. **Important:** If the combined execution of the required functions (`balanceOf` checks of the token contract and `releaseOrMint` of the token pool contract) exceeds the default gas limit of **90,000 gas** on the destination blockchain, CCIP execution will fail, and the transaction will become eligible for manual execution. It is highly recommended to design your token pools to stay within this gas limit to avoid execution failure. Read the Token Pools [common requirements](/ccip/concepts/cross-chain-token/evm/token-pools#common-requirements) to learn more. - If the receiver is an EOA, then this transaction is considered complete with no further processing. - If the receiver is a smart contract, the [ccipReceive](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive) function is invoked after the token transfer. The ccipReceive function processes the CCIP message and any user-specified logic in the receiver contract. The execution of the CCIP message is atomic (all or none). If the ccipReceive function successfully executes, then all aspects of the transaction are complete, and there is no revert. If, however, there is an issue in the receiver execution due to insufficient gas limit or unhandled exceptions, the attempted token transfer will also revert. The transaction then becomes eligible for manual execution. 2. If the message does not involve token transfers, only arbitrary messaging, and the receiver execution fails due to gas limits or unhandled exceptions, the transaction becomes eligible for manual execution. ## Manual execution As described above, a CCIP message becomes eligible for manual execution for various reasons. Manual execution means that a user has to manually trigger the execution of the destination transaction after the issue that caused manual execution has been resolved. When a CCIP message is eligible for manual execution, the [CCIP explorer](https://ccip.chain.link/) shows the following information: - *Ready for manual execution* status - The possibility to override the gas limit and a *Trigger Manual Execution* button Depending on the situation, you can take one of the following steps: - Insufficient gas limit: The executor can connect their wallet, override the gas limit parameter, increase the gas limit for this particular execution, and trigger a manual execution. If this new gas limit override is sufficient, the transaction will go through successfully. **Note:** This gas limit override applies only to this manually executed transaction. - Unhandled exception (logical error) in the receiver contract: If the receiver contract is [upgradeable](https://blog.chain.link/upgradable-smart-contracts/), developers must correct the logic, re-deploy the logic contract, and then manually execute the same transaction. If the receiver contract is not upgradeable, developers must deploy a new receiver contract, and then users can send a new CCIP message. Non-upgradable contracts will not benefit from manual execution. **Note:** Always make sure to test your smart contracts thoroughly. As a best practice, implement fallback mechanisms in the CCIP receiver contracts to manage unhandled exceptions gracefully. Read the [Defensive example](/ccip/tutorials/evm/programmable-token-transfers-defensive) to learn more. When manual execution is initiated, a Merkle proof is generated for the message to be executed. During execution, the CCIP explorer submits the Merkle proof and the new gas limit (if the initial failure was due to a low gas limit). This Merkle proof is verified by the [OffRamp contract](/ccip/concepts/architecture/onchain/evm/components#offramp) against the Merkle root in the OffRamp contract, and that was blessed by the [Risk Management Network](/ccip/concepts/architecture/offchain/risk-management-network). This mirrors the automated execution performed by the [CCIP Executing DON](/ccip/concepts/architecture/offchain/overview#executing-ocr-process), with the addition that the execution is resubmitted using the gas limit you provide. ## Frequently asked questions 1. **Can anyone execute a transaction on the CCIP explorer even if they are not the initiator of the transaction?** Yes, any EOA can manually execute a CCIP message that is eligible for manual execution. However, the executing account must have sufficient native gas tokens (such as ETH on Ethereum or POL on Polygon) to cover the gas costs associated with the delivery of the CCIP message. 2. **If a user sends multiple messages and the first message isn't successfully delivered and goes into a *manual execution* mode, does that mean all subsequent messages from the user will also be stuck?** It depends. If a message goes into manual execution mode due to receiver errors (unhandled exceptions or gas limit issues), subsequent messages don't get automatically blocked, unless they would encounter the same error. However, suppose a message goes into manual execution mode after the Smart Execution time window expires (currently 8 hours). In that case, subsequent messages must wait for the first message to be processed to maintain the default sequence. 3. **If the maximum gas limit is 3M (3,000,000) on mainnet, but it turns out that the destination blockchain requires more than that, will an override of > 3M still work?** Yes, but only for this execution. This works because the gas limit for this execution instance isn't passing through the CCIP validation for the gas limit, for which the CCIP executing DON pays the gas. However, if you consistently need more than 3M for your use case, please reach out to us via this [contact form](https://chain.link/ccip-contact). 4. What should I do if my token pool's gas consumption exceeds the 90,000 gas limit on the destination blockchain? If your token pool's combined execution—including the `balanceOf` function calls before and after minting/releasing, and the `releaseOrMint` function—consumes more than **90,000 gas** on the destination blockchain, CCIP execution will fail. It's highly recommended to optimize your token and token pool contracts to stay within this limit. However, if you cannot optimize further and consistently require more gas, please [contact](https://chain.link/ccip-contact?v=Tokens:%20Gas%20limit%20update) Chainlink Labs to request assistance. 1. **Will Chainlink Labs reimburse us for manual execution fees?** Since most manual execution situations are due to insufficient gas limit or an unhandled exception in the receiver contract, Chainlink Labs does not reimburse these fees. If you are a dApp developer, please ensure you test thoroughly to avoid manual executions to the extent possible.
2. **Do I have to manually execute via the CCIP explorer? Are there any other ways to do this?** The CCIP explorer provides the easiest way to execute manually. It handles all the complexity of submitting the Merkle proof needed for successful transaction execution.
3. **How do I know if my receiver error is due to a gas limit issue or an unhandled exception?** If you see a *ReceiverError* with a revert reason as empty (0x), this is likely due to a gas limit issue. You can look at the transaction trace for the destination transaction on a tool such as [Tenderly](https://tenderly.co/), and you will likely see an *out of gas* reason mentioned in such cases. Determine the gas limit that would work for your transaction and manually override it. Read the [manual execution](/ccip/tutorials/evm/manual-execution) tutorial to analyze an example of an exception due to a low gas limit.
4. **How can I write contracts that avoid manual execution situations in the first place?** - Test thoroughly to ensure logical conditions for all paths are gracefully handled in your receiver contract. - Set a gas limit that works for complex execution paths, not just the simplest ones. Read the [best practices](/ccip/concepts/best-practices/evm#setting-gaslimit) for gas estimation. - Refer to the [Defensive example](/ccip/tutorials/evm/programmable-token-transfers-defensive) tutorial for an example of how to design a programmable token transfer that handles errors gracefully.
5. **My transaction meets one of the conditions to trigger manual execution, but I do not see this option on the CCIP explorer. How am I supposed to execute this manually?** This should not happen, but in the unlikely scenario that it does, please submit a support ticket as shown below. Include the CCIP Message ID, your preferred contact details, and a detailed description of the issue you faced. This will help us assist you more effectively. ![CCIP manual execution support ticket submission interface showing form fields and submission button](/images/ccip/support-manual-exec.gif) --- # CCIP Best Practices Source: https://docs.chain.link/ccip/concepts/best-practices Last Updated: 2025-05-19 This section outlines recommended practices for using Chainlink CCIP effectively and securely. - **[EVM Best Practices](/ccip/concepts/best-practices/evm)**: Recommended guidelines for interacting with CCIP on EVM-compatible chains. - **[SVM Best Practices](/ccip/concepts/best-practices/svm)**: Recommended guidelines for interacting with CCIP on SVM-based chains like Solana. - **[Aptos Best Practices](/ccip/concepts/best-practices/aptos)**: Recommended guidelines for interacting with CCIP on Aptos chain. --- # CCIP Best Practices (EVM) Source: https://docs.chain.link/ccip/concepts/best-practices/evm Last Updated: 2025-05-19 Before you deploy your cross-chain dApps to mainnet, make sure that your dApps follow the best practices in this document. You are responsible for thoroughly reviewing your code and applying best practices to ensure that your cross-chain dApps are secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. ## Verify destination chain Before calling the router's `ccipSend` [function](/ccip/api-reference/evm/v1.6.2/i-router-client#ccipsend), ensure that your code allows users to send CCIP messages to trusted destination chains. **Example**: For an example of how to verify the destination chain, refer to the [Transfer Tokens with Data - Defensive](/ccip/tutorials/evm/programmable-token-transfers-defensive#tutorial) example. ## Verify source chain When implementing the `ccipReceive` [method](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive) in a contract residing on the destination chain, ensure to verify the source chain of the incoming CCIP message. This verification ensures that CCIP messages can only be received from trusted source chains. **Example**: For an example of how to verify the source chain, refer to the [Transfer Tokens with Data - Defensive](/ccip/tutorials/evm/programmable-token-transfers-defensive#tutorial) example. ## Verify sender When implementing the `ccipReceive` [method](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive) in a contract residing on the destination chain, it's important to validate the sender of the incoming CCIP message. This check ensures that CCIP messages are received only from trusted sender addresses. **Note**: Depending on your use case, this verification might not always be necessary. **Example**: For an example of how to verify the sender of the incoming CCIP message, refer to the [Transfer Tokens with Data - Defensive](/ccip/tutorials/evm/programmable-token-transfers-defensive#tutorial) example. ## Verify router addresses When you implement the `ccipReceive` [method](/ccip/api-reference/evm/v1.6.2/ccip-receiver#ccipreceive) in the contract residing on the destination chain, validate that the `msg.sender` is the correct router address. This verification ensures that only the router contract can call the `ccipReceive` function on the receiver contract and is for developers that want to restrict which accounts are allowed to call `ccipReceive`. **Example**: For an example of how to verify the router, refer to the [Transfer Tokens with Data - Defensive](/ccip/tutorials/evm/programmable-token-transfers-defensive#tutorial) example. ## Using `extraArgs` The purpose of [`extraArgs`](/ccip/api-reference/evm/v1.6.2/client#genericextraargsv2) is to allow compatibility with future CCIP upgrades. To get this benefit, make sure that `extraArgs` is mutable in production deployments. This allows you to build it offchain and pass it in a call to a function or store it in a variable that you can update on-demand. If `extraArgs` are left empty, a default of *200000* `gasLimit` will be set. ### Setting `gasLimit` The `gasLimit` specifies the maximum amount of gas CCIP can consume to execute `ccipReceive()` on the contract located on the destination blockchain. It is the main factor in determining the fee to send a message. Unspent gas is not refunded. To transfer tokens directly to an EOA as a *receiver* on the destination blockchain, the `gasLimit` should be set to `0` since there is no `ccipReceive()` implementation to call. To estimate the accurate gas limit for your destination contract, consider the following options: - Leveraging Ethereum client RPC by applying `eth_estimateGas` on `receiver.ccipReceive()`. You can find more information on the [Ethereum API Documentation](https://ethereum.github.io/execution-apis/api-documentation/) and [Alchemy documentation](https://docs.alchemy.com/reference/eth-estimategas). - Conducting [Foundry gas tests](https://book.getfoundry.sh/forge/gas-tracking). - Using [Hardhat plugin for gas tests](https://github.com/cgewecke/eth-gas-reporter). - Using a blockchain explorer to look up the gas consumption of a particular internal transaction. **Example**: For an example of how to estimate the gas limit, refer to the [Optimizing Gas Limit Settings in CCIP Messages](/ccip/tutorials/evm/ccipreceive-gaslimit) guide. ### Setting `allowOutOfOrderExecution` The `allowOutOfOrderExecution` parameter enables you to control the execution order of your messages on the destination blockchain. This parameter is part of [`GenericExtraArgsV2`](/ccip/api-reference/evm/v1.6.2/client#genericextraargsv2) and is available only on lanes where the **Out of Order Execution** property is set to **Optional** or **Required**. Refer to the [CCIP Directory](/ccip/directory) to determine if your target lane supports this feature. #### Best Practices - **When `allowOutOfOrderExecution` is Optional:** - You can set `allowOutOfOrderExecution` to either `true` or `false`, depending on your application's requirements. - **`true`:** Messages can be executed in any order relative to other messages from the same sender. If a previous message has not yet been executed on the destination chain, it does not block the execution of subsequent messages. - **`false`:** Messages are executed in order. CCIP ensures that preceding messages are processed before executing the current message. - **When `allowOutOfOrderExecution` is Required:** - You **must** set `allowOutOfOrderExecution` to `true`. This setting acknowledges that messages may be executed out of order. If set to `false`, the message will revert and will not be processed. - This requirement is enforced on lanes where technical constraints necessitate out-of-order execution, such as mitigating issues related to zero-knowledge proof limitations. For more information, see the [proof overflow problem](https://community.scroll.io/t/the-proof-overflow-problem/841). ## Decoupling CCIP Message Reception and Business Logic As a best practice, separate the reception of CCIP messages from the core business logic of the contract. Implement 'escape hatches' or fallback mechanisms to gracefully manage situations where the business logic encounters issues. To explore this concept further, refer to the [Defensive Example](/ccip/tutorials/evm/programmable-token-transfers-defensive) guide. ## Evaluate the security and reliability of the networks that you use Although CCIP has been thoroughly reviewed and audited, inherent risks might still exist based on your use case, the blockchain networks where you deploy your contracts, and the network conditions on those blockchains. ## Review and audit your code Before securing value with contracts that implement CCIP interfaces and routers, ensure that your code is secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. ## Soak test your dApps Be aware of the [Service Limits and Rate Limits for Supported Networks](/ccip/directory). Before you provide access to end users or secure value, soak test your cross-chain dApps. Ensure that your dApps can operate within these limits and operate correctly during usage spikes or unfavorable network conditions. ## Monitor your dApps When you build applications that depend on CCIP, include monitoring and safeguards to protect against the negative impact of extreme market events, possible malicious activity on your dApp, potential delays, and outages. Create your own monitoring alerts based on deviations from normal activity. This will notify you when potential issues occur so you can respond to them. ## Best Practices for Cross-Chain Token (CCT) Administration When managing your tokens and token pools, it's critical to follow best practices to ensure the security and integrity of your cross-chain operations. This includes proper handling of admin roles and safeguarding against unauthorized access. ### Securely Manage Admin Roles The **token admin** is responsible for configuring token pools across blockchains and enabling cross-chain operations. This role allows the token admin to set the token pool for a token on each supported CCIP blockchain. To ensure security, follow these guidelines: - **Understand the Responsibilities of a Token Developer**: Review [Token Developer Responsibilities](/ccip/service-responsibility#token-developers-responsibilities). - **Assign Admin Roles with Caution**: Only trusted EOAs or smart accounts should be assigned the **token admin** role. - **Use Multi-Signature Smart Accounts**: For added security, consider assigning the token admin role to a **multi-signature smart account**. This ensures that multiple approvals are required for critical operations. ### Protect Against Unauthorized Admin Actions - **Monitor Admin Activity**: Implement monitoring systems to track any actions taken by **token admins**, **token pool owners**, and **rate limit admins**. This helps detect unauthorized attempts to modify configurations or execute cross-chain transfers. - **Limit Admin Privileges When Possible**: For example, instead of giving the full owner access to manage rate limits, consider assigning the **rate limit admin** role, which is specifically responsible for updating rate limits. - **Leverage Smart Contract Audits**: Ensure your tokens and token pools are audited and follow secure development practices. ### Best Practices for Token Pool Owners and Rate Limit Admins - **Token Pool Owner Responsibilities**: The **token pool owner** can enable remote chains, set remote pool addresses for a given chain selector, and configure rate limits. Ensure that this role is assigned to a trusted EOA or smart account, and monitor activity regularly. - **Set Rate Limits Appropriately**: Ensure that you set appropriate rate limits for outbound and inbound token transfers when configuring token pools. - \*\* Delegate Rate Limit Admin Role \*\*: The rate limit admin is an optional role that the token pool owner can assign to another trusted entity using the `setRateLimitAdmin ()` function. The rate limit admin can only manage rate limits, so this role provides a way to delegate responsibility without giving full access to the token pool configuration. ## Best Practices for Liquidity Management Effective liquidity management is crucial for ensuring the smooth operation of token pools, especially in [**Lock and Release**](https://github.com/smartcontractkit/ccip/blob/release/contracts-ccip-1.5.1/contracts/src/v0.8/ccip/pools/LockReleaseTokenPool.sol) token pools. The most critical aspect is ensuring that the token pool has enough liquidity available when it is acting in **reception mode** (on the destination blockchain), allowing tokens to be released to the receiver. Failure to manage liquidity will result in a degraded user experience and can result in user funds being 'stuck' in transit. ### Ensure Sufficient Liquidity When a **LockReleaseTokenPool** operates in **reception mode** (on the destination blockchain), it releases the tokens locked in the token pool. The pool ***must*** have sufficient liquidity to ensure that tokens can be released to the receiver. If the pool lacks liquidity, the release operation will fail and user funds will not be available on the destination blockchain until sufficient liquidity is replenished and manual execution is performed. - **Best Practice**: Estimate expected volume when preparing to add and manage liquidity to ensure sustainable operations. - **Best Practice**: Regularly monitor the liquidity available in your token pool and ensure that there is always enough liquidity to support the release of tokens to the receiver when the pool acts as the destination. ### Avoid Fragmented Liquidity with Multiple Issuing Blockchains Using a **Lock and Unlock** mechanism across multiple issuing blockchains can lead to fragmented liquidity, making it more difficult to maintain sufficient liquidity in each pool. - **Best Practice**: Where possible, avoid using the **Lock and Unlock** mechanism across multiple issuing blockchains. Fragmented liquidity increases operational overhead and complicates liquidity management. ### Monitor Liquidity Health and Automate Alerts Monitoring the health of your token pool's liquidity is essential for ensuring the liveness of cross-chain transfers. Automated alerts can notify you if liquidity falls below a certain threshold, allowing you to take action before transfers fail. - **Best Practice**: Calculate the amount of time required to deplete your token pool to various threshold levels (ie: Low: 50% [Warning], Very Low [Critical] 30%) and utilize automated alerting and pre-defined operational procedures to ensure adequate preparedness to respond by replenishing the pool before users are impacted. For this calculation, be sure to assume users will utilize max capacity transfers paired with token pool's refill rate. ### Use the `provideLiquidity` and `withdrawLiquidity` Functions Properly In pools like the **LockReleaseTokenPool**, liquidity providers can add and remove liquidity using the [`provideLiquidity`](/ccip/api-reference/evm/v1.6.2/lock-release-token-pool#provideliquidity) and [`withdrawLiquidity`](/ccip/api-reference/evm/v1.6.2/lock-release-token-pool#withdrawliquidity) functions. - **Best Practice**: Only trusted entities, such as a designated rebalancer, should be allowed to manage liquidity. Make sure to configure liquidity controls securely to prevent unauthorized liquidity manipulation. ### Set and Manage the Rebalancer Role The **rebalancer** is responsible for managing the liquidity of the pool and ensuring that there is always sufficient liquidity when needed. They can rebalance liquidity between different pools or pool versions if necessary. - **Best Practice**: Assign the rebalancer role to a trusted entity and ensure they understand the responsibilities, such as maintaining liquidity in the pool to support token releases. ## Multi-Signature Contracts Multi-signature contracts, such as [Safe Smart Accounts](https://github.com/safe-global/safe-smart-account), enhance security by requiring multiple signatures to authorize transactions. ### Threshold configuration Set an optimal threshold for signers based on the trust level of participants and the required security. ### Role-based access control Assign roles with specific permissions to different signers, limiting access to critical operations to trusted individuals. ### Hardware wallet integration Use hardware-backed keys for signers to safeguard private keys from online vulnerabilities. Ensure that these devices are secure and regularly updated. ### Regular audits and updates Conduct periodic audits of signer access and contract settings. Update the multisig setup as necessary, especially when personnel changes occur. ### Emergency recovery plans Implement procedures for recovering from lost keys or compromised accounts, such as a predefined recovery multisig or recovery key holders. ### Transaction review process Establish a standard process for reviewing and approving transactions, which can include a waiting period for large transfers to mitigate risks and verifying data on a hardware wallet before signing to protect against front-end compromises. ### Security tooling Tools such as [Tenderly](https://tenderly.co/) or [Hypernative](https://www.hypernative.io/) can provide additional layers of security related to transaction simulation, risk monitoring, and alerting. ### Documentation and training Maintain thorough documentation of multisig operations and provide training for all signers to ensure familiarity with processes and security protocols. ## Chain-Specific Considerations ### Hedera Fee Decimal Handling When using Chainlink CCIP with Hedera, you must be aware of a critical difference in decimal handling. Hedera's native HBAR token and wrapped WHBAR both use 8 decimals, while most EVM chains use 18 decimals for their native tokens. #### Impact on CCIP Fee Calculation When interacting with Hedera using HBAR or WHBAR as fee tokens: 1. HBAR and WHBAR natively use 8 decimals 2. Hedera's JSON-RPC conversion layer expects a value with 18 decimals for `msg.value` when sending transactions *Source: [Hedera Documentation on HBAR Decimal Places](https://docs.hedera.com/hedera/sdks-and-apis/sdks/hbars#hbar-decimal-places)* #### Required Fee Scaling For off-chain applications (like frontends or scripts) that: 1. Call `getFee()` to determine the fee amount 2. Then use that amount to send CCIP messages You **must scale the fee** by multiplying it by **10 decimals**: ``` Scaled Fee = getFee() * 10^10 ``` When using native HBAR as the fee token, you need to send this scaled value as `msg.value` during the `ccipSend()` call. When using WHBAR as the fee token, users must approve (ERC20) at least this scaled amount before calling `ccipSend()` with no msg.value. --- # CCIP Best Practices (SVM) Source: https://docs.chain.link/ccip/concepts/best-practices/svm Last Updated: 2025-05-19 Before you deploy your cross-chain dApps to mainnet, make sure that your dApps follow the best practices in this document. You are responsible for thoroughly reviewing your code and applying best practices to ensure that your cross-chain dApps are secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. ## Verify destination chain Before calling the router's `ccip_send` [instruction](/ccip/api-reference/svm/v1.6.0/router#ccip_send), ensure your code verifies that the destination chain is supported by the CCIP Router. Sending messages to unsupported chains will fail and potentially waste transaction fees. You can programmatically verify destination chain support using Solana PDAs (Program Derived Addresses). Here below is a JavaScript example of how to verify destination chain support: ```javascript import { Connection, PublicKey } from "@solana/web3.js" /** * Verifies if a destination chain is supported by the CCIP Router * * @param {Connection} connection - Solana connection object * @param {string} routerProgramId - The CCIP Router program ID * @param {BigInt} destinationChainSelector - Chain selector to verify * @returns {Promise} - Whether the chain is supported */ async function isDestinationChainSupported(connection, routerProgramId, destinationChainSelector) { // Convert chain selector to little-endian buffer (Solana standard) const chainSelectorBuffer = Buffer.alloc(8) chainSelectorBuffer.writeBigUInt64LE(BigInt(destinationChainSelector)) // Derive the PDA for this destination chain // The Router stores chain state in PDAs with seed ["dest_chain_state", chainSelector] const [destChainPda] = PublicKey.findProgramAddressSync( [Buffer.from("dest_chain_state"), chainSelectorBuffer], new PublicKey(routerProgramId) ) // If the account exists, the chain is supported const accountInfo = await connection.getAccountInfo(destChainPda) return accountInfo !== null } ``` ## Verify source chain When implementing the `ccip_receive` [method](/ccip/api-reference/svm/v1.6.0/receiver#ccip_receive) in a program residing on the destination chain, ensure to verify the source chain of the incoming CCIP message. This verification ensures that CCIP messages can only be received from trusted source chains. ## Verify sender When implementing the [`ccip_receive`](/ccip/api-reference/svm/v1.6.0/receiver#ccip_receive) instruction in a program residing on the destination chain, it's important to validate the sender of the incoming CCIP message. This check ensures that CCIP messages are received only from trusted sender addresses. **Note**: Depending on your use case, this verification might not always be necessary. ## Verify authority and allowed offramp When you implement the [`ccip_receive`](/ccip/api-reference/svm/v1.6.0/receiver#ccip_receive) instruction in the program residing on the destination chain, validate that the `authority` account is the correct Offramp CPI signer PDA and that `allowed_offramp` is the correct PDA owned by the router program. This verification ensures that only the authorized CCIP Offramp program can call the `ccip_receive` function. **Example in Rust**: ```rust #[derive(Accounts)] #[instruction(message: Any2SVMMessage)] pub struct CcipReceive<'info> { // Offramp CPI signer PDA must be first #[account( seeds = [EXTERNAL_EXECUTION_CONFIG_SEED, crate::ID.as_ref()], bump, seeds::program = offramp_program.key(), )] pub authority: Signer<'info>, /// CHECK: Offramp program exists only to derive the allowed offramp PDA pub offramp_program: UncheckedAccount<'info>, /// CHECK: PDA owned by the router program verifying this is an allowed offramp #[account( owner = state.router @ CcipReceiverError::InvalidCaller, seeds = [ ALLOWED_OFFRAMP, message.source_chain_selector.to_le_bytes().as_ref(), offramp_program.key().as_ref() ], bump, seeds::program = state.router, )] pub allowed_offramp: UncheckedAccount<'info>, // Your state account containing the router address #[account(seeds = [STATE_SEED], bump)] pub state: Account<'info, ProgramState>, // Additional accounts as needed // ... } ``` ## Using `extra_args` The `extra_args` parameter provides chain-specific configuration for cross-chain messaging. It controls execution parameters on the destination chain, including resource allocation and message ordering guarantees. ### Parameter Selection When sending a CCIP message, you must select the appropriate `extra_args` structure based on your destination chain: - `SVMExtraArgsV1`: For Solana and other SVM-based destinations - `EVMExtraArgsV2`: For Ethereum and other EVM-based destinations For the full parameter specification, refer to the [CCIP API Reference](/ccip/api-reference/svm/v1.6.0/messages#extra-args). ### Setting `compute_units` (SVM destinations) The `compute_units` parameter specifies the maximum Solana compute budget (in units) that the CCIP OffRamp can use when executing the `ccip_receive()` instruction on the destination program. This parameter directly affects fee calculation since higher compute budgets require more resources. **Best Practices:** - **For Program Receivers**: Set sufficient compute units for your program logic execution; any unused units are not refunded. - **For Wallet Receivers**: When transferring tokens directly to a wallet with no `ccip_receive()` implementation, set `compute_units` to `0` since no program execution is needed. - **For Compute Unit Estimation**: Test your receiver program under varying conditions to determine optimal values. Consider: - Message size and complexity - Token transfer operations - Program execution paths - Additional accounts referenced ### Setting `accountIsWritableBitmap` When using the `SVMExtraArgsV1` structure, the `accountIsWritableBitmap` field specifies which additional accounts in your message should be marked as writable: - This is a 64-bit bitmap where each bit position corresponds to an account in the `accounts` array. - Set the corresponding bit to `1` to mark an account as writable (bit 0 for the first account, bit 1 for the second, etc.). - Must be provided in little-endian format for Solana compatibility. ### Setting `tokenReceiver` The `tokenReceiver` parameter in `SVMExtraArgsV1` specifies which Solana account will receive the tokens: #### When Receiving at Wallet Addresses When sending tokens to an end-user wallet: - Set `tokenReceiver` to the user's wallet address (base58 encoded) - Do NOT use an Associated Token Account (ATA) - use the wallet address directly - The CCIP infrastructure will automatically derive the proper ATA on the recipient's behalf ```javascript // Example: Setting tokenReceiver to a user's wallet tokenReceiver: "EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB" // Recipient wallet ``` #### When Receiving at Program Addresses When sending tokens to a Solana program: - Set `tokenReceiver` to a Program Derived Address (PDA) that the program has authority over - The PDA must be derived using seeds that the program recognizes - The program must include logic to handle and manage the received tokens ```javascript // Example: Setting tokenReceiver to a PDA the program controls tokenReceiver: "57y3NXjkiAzP5Gw9WuUwzJMJbJQAHH6jUYBQfZdTE5zJ" // PDA with program authority ``` In Solana's security model, programs cannot directly control tokens unless they have authority over the token account: 1. **Program Derived Addresses (PDAs)** must be derived from the program's ID using specified seeds 2. Only the program that created the PDA can sign as that PDA 3. Without proper authority, the program cannot transfer, burn, or otherwise manipulate the tokens #### For Data-Only Messages When sending only data (no tokens): - Set `tokenReceiver` to the default Solana PublicKey (`11111111111111111111111111111111`) - This is required even though no tokens are being transferred ### Setting `allowOutOfOrderExecution` The `allowOutOfOrderExecution` parameter controls message ordering guarantees: - `true`: Messages may be processed out of sequence relative to other messages from the same sender - `false`: Messages are processed in the exact sequence they were sent ## Evaluate the security and reliability of the networks that you use Although CCIP has been thoroughly reviewed and audited, inherent risks might still exist based on your use case, the blockchain networks where you deploy your programs, and the network conditions on those blockchains. ## Review and audit your code Before securing value with programs that implement CCIP interfaces and routers, ensure that your code is secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. ## Soak test your dApps Be aware of the [Service Limits and Rate Limits for Supported Networks](/ccip/directory). Before you provide access to end users or secure value, soak test your cross-chain dApps. Ensure that your dApps can operate within these limits and operate correctly during usage spikes or unfavorable network conditions. ## Monitor your dApps When you build applications that depend on CCIP, include monitoring and safeguards to protect against the negative impact of extreme market events, possible malicious activity on your dApp, potential delays, and outages. Create your own monitoring alerts based on deviations from normal activity. This will notify you when potential issues occur so you can respond to them. ## Multi-Signature Authorities Multi-signature authorities enhance security by requiring multiple signatures to authorize transactions. ### Threshold configuration Set an optimal threshold for signers based on the trust level of participants and the required security. ### Role-based access control Assign roles with specific permissions to different signers, limiting access to critical operations to trusted individuals. ### Hardware wallet integration Use hardware wallets for signers to safeguard private keys from online vulnerabilities. Ensure that these devices are secure and regularly updated. ### Regular audits and updates Conduct periodic audits of signer access and authority settings. Update the multisig setup as necessary, especially when personnel changes occur. ### Emergency recovery plans Implement procedures for recovering from lost keys or compromised accounts, such as a predefined recovery multisig or recovery key holders. ### Transaction review process Establish a standard process for reviewing and approving transactions, which can include a waiting period for large transfers to mitigate risks. ### Documentation and training Maintain thorough documentation of multisig operations and provide training for all signers to ensure familiarity with processes and security protocols. --- # CCIP Best Practices (Aptos) Source: https://docs.chain.link/ccip/concepts/best-practices/aptos Last Updated: 2025-09-03 Before you deploy your cross-chain dApps to mainnet, make sure that your dApps follow the best practices in this document. You are responsible for thoroughly reviewing your code and applying best practices to ensure that your cross-chain dApps are secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. ## Verify destination chain Before calling the `router::ccip_send` entry function, your application should verify that the destination chain is supported. Sending messages to unsupported chains will fail and waste transaction fees. **Example**: You can programmatically check for support by calling the `onramp::is_chain_supported` view function. Here is a TypeScript example: ```ts import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk" async function isDestinationChainSupported( aptos: Aptos, onRampAddress: string, destinationChainSelector: string ): Promise { const result = await aptos.view({ payload: { function: `${onRampAddress}::onramp::is_chain_supported`, functionArguments: [destinationChainSelector], }, }) return result[0] as boolean } ``` ## Verify source chain When implementing the `ccip_receive` entry function in your custom module, you should verify the `source_chain_selector` from the incoming `Any2AptosMessage`. This ensures your module only accepts messages from blockchains you trust. ```rust use ccip::client; fun ccip_receive( _proof: ProofType, ) { let message = receiver_registry::get_receiver_input(module_address, _proof); let source_chain = client::get_source_chain_selector(&message); // Your allowlist logic assert!(is_allowed_source_chain(source_chain), E_UNTRUSTED_SOURCE_CHAIN); // ... rest of your logic } ``` ## Verify sender Your `ccip_receive` implementation should also validate the sender address in the `Any2AptosMessage` if your application logic depends on messages coming from specific source addresses. **Note**: This verification may not be necessary for all use cases, such as an application that accepts messages from any sender. ```rust // Inside your ccip_receive function let sender_bytes = client::get_sender(&message); assert!(is_trusted_sender(sender_bytes), E_UNTRUSTED_SENDER); ``` ## Using `extraArgs` The `extra_args` parameter provides chain-specific configuration for cross-chain messaging. It controls execution parameters on the destination chain, including resource allocation and message ordering guarantees. ### Setting `gasLimit` When sending a message from Aptos to an EVM chain, the `gasLimit` in `extraArgs` specifies the gas for the `ccipReceive` execution on the destination. - To transfer tokens directly to an EVM wallet (EOA), set the `gasLimit` to `0` because no contract execution is required. - To call a receiver contract on EVM, you must estimate the required gas and set an appropriate `gasLimit`. Unused gas is not refunded. - When sending a message from Aptos to another Aptos module, the `gasLimit` can be used to allocate a specific amount of gas for your `ccip_receive` function's execution. ### Message Ordering (`allowOutOfOrderExecution` flag) - When sending a message **from EVM to Aptos**, the `extraArgs` on the EVM side contains an `allowOutOfOrderExecution: bool` parameter. - When sending a message **from Aptos**, the `extra_args` contains an `allow_out_of_order_execution: bool` parameter. ## Decoupling CCIP Message Reception and Business Logic As a best practice, separate the logic for receiving a CCIP message from your core application logic. Your `ccip_receive` function should be lightweight, focusing on: - Verifying the caller and payload. - Storing the message data in an onchain resource. - Emitting an event. A separate function can then be called by a user or another process to consume the stored data and execute the main business logic. This pattern provides more control and allows for "escape hatches" to manage situations where the business logic encounters issues. ## Key Concepts for Aptos Receivers When implementing a custom receiver module on Aptos to interact with CCIP, there are several key architectural patterns and constraints to understand. ### Why must `ccip_receive` fetch its own data payload? The `ccip_receive` function in your module acts as a secure callback, triggered by the CCIP Off-Ramp, but it does not receive the message payload directly in its function arguments. Instead, the receiver module is responsible for actively retrieving the payload. - **Mechanism**: When a message arrives, the CCIP protocol temporarily stores the payload (the `Any2AptosMessage` struct) within the `ReceiverRegistry` module. Your `ccip_receive` function must then call `receiver_registry::get_receiver_input` to securely fetch this data within the same transaction. - **Rationale**: This design pattern ensures security. It confirms that only the correctly registered module at the designated receiver address can access the message payload, and only during the context of a valid CCIP execution initiated by the Off-Ramp. This prevents unauthorized access to message data. ### When must a receiver module be deployed under a Resource Account? The choice between a **resource account** and a **user account** / **code object account** for deploying your receiver module depends entirely on whether the module will ever need to programmatically control assets. - **Modules That Handle Tokens**: If your module will receive tokens via CCIP and later needs to transfer them, it **must** be deployed under a **resource account**. This is because a resource account allows the module to generate a signer for its own address onchain, which is required to authorize the withdrawal or transfer of those assets. Without this signer capability, any tokens the module receives would be locked. - **Data-Only Modules**: Conversely, if your receiver module is designed **only to process arbitrary data** — for example, to update its own internal state or trigger an event—and will never hold or transfer assets, it can be deployed under a regular **user account** or **code object account** (the code object account is recommended). In this scenario, the module doesn't need to sign for any transactions on its own behalf, so the signer capability of a resource account is not necessary. ### Why must each `ccip_receive` module be deployed under a unique account? The CCIP `ReceiverRegistry` is designed to map a single account address to a single, unique `ccip_receive` function. - **Constraint**: When a CCIP message arrives, it targets a specific receiver address. The protocol requires a deterministic way to find and invoke the correct function. Registering multiple `ccip_receive` functions at the same address would create an ambiguity that the protocol cannot resolve. - **Recommended Design Pattern**: While you are limited to one registered entry point per account, this does not limit your application's complexity. The recommended approach is to use your single `ccip_receive` function as a **dispatcher**. - Your application can encode additional routing information inside the data payload of the CCIP message (e.g., using a function name or an action ID). - Your single `ccip_receive` function then parses this data and calls the appropriate internal functions within your module to handle different logic paths. This pattern maintains a single, secure entry point for CCIP while allowing for flexible and sophisticated application logic. ## Evaluate the security and reliability of the networks that you use Although CCIP has been thoroughly reviewed and audited, inherent risks might still exist based on your use case, the blockchain networks where you deploy your programs, and the network conditions on those blockchains. ## Review and audit your code Before securing value with programs that implement CCIP interfaces and routers, ensure that your code is secure and reliable. If you have a unique use case for CCIP that might involve additional cross-chain risk, [contact the Chainlink Labs Team](https://chain.link/ccip-contact) before deploying your application to mainnet. ## Soak test your dApps Be aware of the [Service Limits and Rate Limits for Supported Networks](/ccip/directory). Before you provide access to end users or secure value, soak test your cross-chain dApps. Ensure that your dApps can operate within these limits and operate correctly during usage spikes or unfavorable network conditions. ## Monitor your dApps When you build applications that depend on CCIP, include monitoring and safeguards to protect against the negative impact of extreme market events, possible malicious activity on your dApp, potential delays, and outages. Create your own monitoring alerts based on deviations from normal activity. This will notify you when potential issues occur so you can respond to them. ## Multi-Signature Authorities Multi-signature authorities enhance security by requiring multiple signatures to authorize transactions. ### Threshold configuration Set an optimal threshold for signers based on the trust level of participants and the required security. ### Role-based access control Assign roles with specific permissions to different signers, limiting access to critical operations to trusted individuals. ### Hardware wallet integration Use hardware wallets for signers to safeguard private keys from online vulnerabilities. Ensure that these devices are secure and regularly updated. ### Regular audits and updates Conduct periodic audits of signer access and authority settings. Update the multisig setup as necessary, especially when personnel changes occur. ### Emergency recovery plans Implement procedures for recovering from lost keys or compromised accounts, such as a predefined recovery multisig or recovery key holders. ### Transaction review process Establish a standard process for reviewing and approving transactions, which can include a waiting period for large transfers to mitigate risks. ### Documentation and training Maintain thorough documentation of multisig operations and provide training for all signers to ensure familiarity with processes and security protocols. --- # CCIP Test Tokens - Faucets for EVM and Solana Source: https://docs.chain.link/ccip/test-tokens Last Updated: 2025-08-19 import { SVMTestTokensClient } from "@features/ccip/components/faucet" import CcipCommon from "@features/ccip/CcipCommon.astro" CCIP provides test tokens that you can mint on testnets for development and testing. ## Quick Start ### EVM Chains Call the `drip` function directly on token contracts using the interface below or through block explorers: ### Solana Devnet Use the dedicated faucet interface for CCIP-BnM tokens: #### Amount and rate limits - The faucet mints **1 CCIP‑BnM** per request. - Requests are **rate‑limited to about once every 3 hours per wallet**. #### Signature requirement The faucet requires wallet signature verification. When you request tokens, your wallet will prompt you to sign a message. - Message signing is free (no SOL required) and grants no spending permissions. - After signature verification, the server executes the token mint transaction. ## About CCIP Test Tokens CCIP supports specialized test tokens designed for cross-chain testing. These tokens are available on all CCIP-supported testnets. | Token | Type | Availability | Description | | ------------ | ----------- | ------------------------------------------------------------------- | -------------------------------------------------------------- | | **CCIP-BnM** | Burn & Mint | All testnets | Burned on source chain, minted on destination chain | | **CCIP-LnM** | Lock & Mint | Ethereum Sepolia (native)
Other chains (wrapped as clCCIP-LnM) | Locked on Ethereum Sepolia, minted as wrapped tokens elsewhere | On EVM chains, tokens are minted by calling the `drip` function directly on contracts. On Solana Devnet, CCIP-BnM tokens are available through a dedicated faucet interface. ## LINK Token Faucets For CCIP operations, you also need LINK tokens to pay transaction fees. Use the official [Chainlink faucets](https://faucets.chain.link/) to obtain LINK tokens on supported testnets by connecting your wallet. ## Block Explorer Method (EVM Chains) For EVM chains, you call the `drip` function directly on token contracts. You can use block explorers instead of the interface above: ```solidity function drip(address to) external { _mint(to, 1e18); } ``` ### General Process Navigate to the [CCIP Directory](/ccip/directory/testnet) to find token contract addresses, open the contract on the appropriate block explorer, connect your wallet, and call the `drip` function with your wallet address. ### Example: Ethereum Sepolia CCIP-BnM Locate the CCIP-BnM contract address in the [CCIP Directory](/ccip/directory/testnet) under [Ethereum Sepolia](/ccip/directory/testnet/chain/ethereum-testnet-sepolia). Open the [contract on Etherscan](https://sepolia.etherscan.io/address/0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05), navigate to the Contract tab, select Write Contract, and connect your wallet. Call the `drip` function with your wallet address to mint 1 CCIP-BnM token. Import the token in your wallet using this [MetaMask guide](https://support.metamask.io/hc/en-us/articles/360015489031#h_01FWH492CHY60HWPC28RW0872H). --- # CCIP Tutorials (EVM) Source: https://docs.chain.link/ccip/tutorials/evm Last Updated: 2025-05-19 You can explore several comprehensive guides to learn about cross-chain interoperability using CCIP. These tutorials provide step-by-step instructions to help you understand different patterns that you can incorporate into your blockchain projects. ## Guides - [Transfer Tokens](/ccip/tutorials/evm/transfer-tokens-from-contract) - [Transfer Tokens with Data](/ccip/tutorials/evm/programmable-token-transfers) - [Transfer Tokens with Data - Defensive Example](/ccip/tutorials/evm/programmable-token-transfers-defensive) - [Cross-Chain Token (CCT)](/ccip/tutorials/evm/cross-chain-tokens) - [Test CCIP Locally](/ccip/tutorials/evm/test-ccip-locally) - [Offchain](/ccip/tutorials/evm/offchain) - [Transfer Tokens between EOAs](/ccip/tutorials/evm/offchain/transfer-tokens-from-eoa) - [Checking CCIP Message Status](/ccip/tutorials/evm/offchain/get-status-offchain) - [Using CCIP CLI](/ccip/tutorials/evm/offchain/ccip-tools) - [Transfer Tokens between EOAs](/ccip/tutorials/evm/offchain/ccip-tools/transfer-tokens-from-eoa) - [Checking CCIP Message Status](/ccip/tutorials/evm/offchain/ccip-tools/get-status-offchain) - [Get Supported Tokens](/ccip/tutorials/evm/offchain/ccip-tools/get-supported-tokens) - [Transfer USDC with Data](/ccip/tutorials/evm/usdc) - [Send Arbitrary Data](/ccip/tutorials/evm/send-arbitrary-data) - [Send Arbitrary Data and Receive Transfer Confirmation: A -> B -> A](/ccip/tutorials/evm/send-arbitrary-data-receipt-acknowledgment) - [Manual Execution](/ccip/tutorials/evm/manual-execution) - [Optimizing Gas Limit Settings in CCIP Messages](/ccip/tutorials/evm/ccipreceive-gaslimit) - [Acquire Test Tokens](/ccip/test-tokens) --- # Transfer Tokens Between Chains from Smart Contracts Source: https://docs.chain.link/ccip/tutorials/evm/transfer-tokens-from-contract Last Updated: 2025-05-19 In this tutorial, you will use Chainlink CCIP to transfer tokens from a smart contract to an account on a different blockchain. First, you will pay for the CCIP fees on the source blockchain using LINK. Then, you will use the same contract to pay CCIP fees in native gas tokens. For example, you would use ETH on Ethereum or AVAX on Avalanche. ## Before you begin 1. You should understand how to write, compile, deploy, and fund a smart contract. If you need to brush up on the basics, read this [tutorial](/quickstarts/deploy-your-first-contract), which will guide you through using the [Solidity programming language](https://soliditylang.org/), interacting with the [MetaMask wallet](https://metamask.io) and working within the [Remix Development Environment](https://remix.ethereum.org/). 2. Your account must have some AVAX and LINK tokens on *Avalanche Fuji*. Learn how to [Acquire testnet LINK](/resources/acquire-link). 3. Check the [CCIP Directory](/ccip/directory) to confirm that the tokens you will transfer are supported for your lane. In this example, you will transfer tokens from *Avalanche Fuji* to *Ethereum Sepolia* so check the list of supported tokens [here](/ccip/directory/testnet/chain/avalanche-fuji-testnet). 4. Learn how to [acquire CCIP test tokens](/ccip/test-tokens#evm-chains). Following this guide, you should have CCIP-BnM tokens, and CCIP-BnM should appear in the list of your tokens in MetaMask. 5. Learn how to [fund your contract](/resources/fund-your-contract). This guide shows how to fund your contract in LINK, but you can use the same guide to fund your contract with any ERC20 tokens as long as they appear in the list of tokens in MetaMask. ## Tutorial In this tutorial, you will transfer [CCIP-BnM](/ccip/test-tokens#about-ccip-test-tokens) tokens from a contract on Avalanche Fuji to an account on Ethereum Sepolia. First, you will pay [CCIP fees in LINK](#transfer-tokens-and-pay-in-link), then you will pay [CCIP fees in native gas](#transfer-tokens-and-pay-in-native). The destination account can be an [EOA (Externally Owned Account)](https://ethereum.org/en/developers/docs/accounts/#types-of-account) or a smart contract. Moreover, the example shows how to transfer CCIP-BnM tokens, but you can re-use the same example to transfer other tokens as long as they are supported for your [lane](/ccip/concepts/architecture/key-concepts#lane). ### Deploy your contracts To use this contract: 1. [Open the contract in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/CCIP/TokenTransferor.sol). 2. Compile your contract. 3. Deploy and fund your sender contract on *Avalanche Fuji*: 1. Open MetaMask and select the *Avalanche Fuji* network. 2. In Remix IDE, click *Deploy & Run Transactions* and select *Injected Provider - MetaMask* from the environment list. Remix will then interact with your MetaMask wallet to communicate with *Avalanche Fuji*. 3. Fill in your blockchain's router and LINK contract addresses. The router address can be found on the [CCIP Directory](/ccip/directory) and the LINK contract address on the [LINK token contracts page](/resources/link-token-contracts). For *Avalanche Fuji*: - The router address is 0xF694E193200268f9a4868e4Aa017A0118C9a8177, - The LINK contract address is 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846. 4. Click the **transact** button. After you confirm the transaction, the contract address appears on the *Deployed Contracts* list. Note your contract address. 5. Open MetaMask and fund your contract with CCIP-BnM tokens. You can transfer 0.002 *CCIP-BnM* to your contract. 4. Enable your contract to transfer tokens to *Ethereum Sepolia*: 1. In Remix IDE, under *Deploy & Run Transactions*, open the list of functions for your smart contract deployed on *Avalanche Fuji*. 2. Call the `allowlistDestinationChain` function with 16015286601757825753 as the destination chain selector, and true as allowed. Each chain selector is found on the [CCIP Directory](/ccip/directory). ### Transfer tokens and pay in LINK You will transfer *0.001 CCIP-BnM*. The CCIP fees for using CCIP will be paid in LINK. Read this [explanation](#transferring-tokens-and-pay-in-link) for a detailed description of the code example. 1. Open MetaMask and connect to *Avalanche Fuji*. Fund your contract with LINK tokens. You can transfer 70 *LINK* to your contract. **Note**: The LINK tokens are used to pay for CCIP fees. **Note:** This transaction fee is significantly higher than normal due to gas spikes on Sepolia. To run this example, you can get additional testnet LINK from [faucets.chain.link](https://faucets.chain.link) or use a supported testnet other than Sepolia. 2. Transfer CCIP-BnM from *Avalanche Fuji*: 1. Open MetaMask and select the network *Avalanche Fuji*. 2. In Remix IDE, under *Deploy & Run Transactions*, open the list of functions for your smart contract deployed on *Avalanche Fuji*. 3. Fill in the arguments of the ***transferTokensPayLINK*** function: | Argument | Value and Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | _destinationChainSelector |
CCIP Chain identifier of the destination blockchain (*Ethereum Sepolia* in this example). You can find each chain selector on the [CCIP Directory](/ccip/directory). | | _receiver | Your account address on *Ethereum Sepolia*.
The destination account address. It could be a smart contract or an EOA. | | _token |
The *CCIP-BnM* contract address at the source chain (*Avalanche Fuji* in this example). You can find all the addresses for each supported blockchain on the [CCIP Directory](/ccip/directory). | | _amount |
The token amount (*0.001 CCIP-BnM*). | 4. Click the **transact** button and confirm the transaction on MetaMask. 5. Once the transaction is successful, note the transaction hash. Here is an [example](https://testnet.snowtrace.io/tx/0x62ca604240fc30133646ff94dcedac5375c5e42b109f3339c85e4fa29541d42b) of a transaction on *Avalanche Fuji*. 1. Open the [CCIP explorer](https://ccip.chain.link/) and search your cross-chain transaction using the transaction hash. 2. The CCIP transaction is completed once the status is marked as "Success". The data field is empty because you are only transferring tokens. 3. Check the receiver account on the destination chain: 1. Note the destination transaction hash from the CCIP explorer. `0x083fc1a79ffcfd617426fd71dff87ca16db2e4333e62a28cdd13d4bec0926bcb` in this example. 2. Open the block explorer for your destination chain. For *Ethereum Sepolia*, open [etherscan](https://sepolia.etherscan.io). 3. Search the [transaction hash](https://sepolia.etherscan.io/tx/0x083fc1a79ffcfd617426fd71dff87ca16db2e4333e62a28cdd13d4bec0926bcb). 4. Notice in the *Tokens Transferred* section that CCIP-BnM tokens have been transferred to your account (0.001 CCIP-BnM). ### Transfer tokens and pay in native You will transfer *0.001 CCIP-BnM*. The CCIP fees for using CCIP will be paid in Avalanche Fuji's native AVAX. Read this [explanation](#transferring-tokens-and-pay-in-native) for a detailed description of the code example. 1. Open MetaMask and connect to *Avalanche Fuji*. Fund your contract with native gas tokens. You can transfer 0.2 *AVAX* to your contract. **Note**: The native gas tokens are used to pay for CCIP fees. 2. Transfer CCIP-BnM from *Avalanche Fuji*: 1. Open MetaMask and select the network *Avalanche Fuji*. 2. In Remix IDE, under *Deploy & Run Transactions*, open the list of transactions of your smart contract deployed on *Avalanche Fuji*. 3. Fill in the arguments of the ***transferTokensPayNative*** function: | Argument | Value and Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | _destinationChainSelector |
CCIP Chain identifier of the destination blockchain (*Ethereum Sepolia* in this example). You can find each chain selector on the [CCIP Directory](/ccip/directory). | | _receiver | Your account address on *Ethereum Sepolia*.
The destination account address. It could be a smart contract or an EOA. | | _token |
The *CCIP-BnM* contract address at the source chain (*Avalanche Fuji* in this example). You can find all the addresses for each supported blockchain on the [CCIP Directory](/ccip/directory).. | | _amount |
The token amount (*0.001 CCIP-BnM*). | 4. Click the **transact** button and confirm the transaction on MetaMask. 5. Once the transaction is successful, note the transaction hash. Here is an [example](https://testnet.snowtrace.io/tx/0x186e5767d65dffe685c24d5ee881201e2b39fd684220a68943b0b861178ddf64) of a transaction on *Avalanche Fuji*. 1. Open the [CCIP explorer](https://ccip.chain.link/) and search your cross-chain transaction using the transaction hash. 2. The CCIP transaction is completed once the status is marked as "Success". The data field is empty because you only transfer tokens. Note that CCIP fees are denominated in LINK. Even if CCIP fees are paid using native gas tokens, node operators will be paid in LINK. 3. Check the receiver account on the destination chain: 1. Note the destination transaction hash from the CCIP explorer. `0xf403d828fa377d657af67f12e99ff435974299c27ba2d57c53494d29bbbfc938` in this example. 2. Open the block explorer for your destination chain. For *Ethereum Sepolia*, open [etherscan](https://sepolia.etherscan.io). 3. Search the [transaction hash](https://sepolia.etherscan.io/tx/0xf403d828fa377d657af67f12e99ff435974299c27ba2d57c53494d29bbbfc938). 4. Notice in the *Tokens Transferred* section that CCIP-BnM tokens have been transferred to your account (0.001 CCIP-BnM). ## Explanation