CCIP Architecture in Depth
How Chainlink CCIP works under the hood
CCIP concepts
Before you explore how Chainlink CCIP works in depth, it is best to understand the core concepts.
Interoperability
Interoperability is the ability to exchange information between different systems or networks, even if they are incompatible. Shared concepts on different networks ensure that each party understands and trusts the exchanged information. It also considers the concept of finality to establish trust in the exchanged information by validating its accuracy and integrity.
Finality
Finality refers to the state of irreversibility and permanent record of a transaction on the blockchain. If the parameters for finality are properly set, the likelihood of irreversibility is extremely low. For CCIP, source chain finality is the main factor that determines the end-to-end elapsed time for CCIP to send a message from one chain to another.
Finality varies across different networks. Some networks offer instant finality and others require multiple confirmations. These time differences are set to ensure the security of CCIP and its users. Finality is crucial for token transfers because funds are locked and not reorganized once they are released onto the destination chain. In this scenario, finality ensures that funds on the destination chain are available only after they have been successfully committed on the source chain.
Lane
To recap, a Chainlink CCIP lane is a distinct pathway between a source and a destination blockchain. Lanes are unidirectional. For instance, Ethereum Sepolia => Polygon Mumbai and Polygon Mumbai => Ethereum Sepolia are two different lanes.
Decentralized Oracle Network (DON)
Chainlink Decentralized Oracle Networks, or DONs, run Chainlink OCR2. The protocol runs in rounds during which an observed data value might be agreed upon. The output of this process results in a report which is attested to by a quorum of participants. The report is then transmitted on-chain by one of the participants. No single participant is responsible for transmitting on every round, and all of them will attempt to do so in a round-robin fashion until a transmission has taken place.
In the context of CCIP, a lane contains two OCR DON committees that monitor transactions between a source and destination blockchain: the Committing DON and Executing DON. More on that in the next subchapter.
High-Level Architecture
As mentioned in the Getting Started chapter with Chainlink CCIP, one can:
Transfer (supported) tokens
Send any kind of data
Send both tokens and data
CCIP receiver can be:
EOA
Any smart contract that implements
CCIPReceiver.sol
Note: If you send a message and token(s) to EOA, only tokens will arrive.
Going down the rabbit hole
The figure below outlines the different components involved in a cross-chain transaction:
Cross-Chain dApps are user-specific. A smart contract or an EOA (Externally Owned Account) interacts with the CCIP Router to send arbitrary data and/or transfer tokens cross-chain.
The contracts in dark blue are the CCIP interface (
Router.sol
). As we already learned, to use CCIP, users need only to understand how to interact with the router; they don't need to understand the whole CCIP architecture. Note: The CCIP interface is static and remains consistent over time to provide reliability and stability to the users.The contracts in light blue are internal to the CCIP protocol and subject to change.
Now let's explain each of the components from the above diagram.
On-chain components
1) Router
The Router is the primary contract CCIP users interface with. This contract is responsible for initiating cross-chain interactions. One router contract exists per chain. When transferring tokens, callers have to approve tokens for the router contract.
The router contract routes the instruction to the destination-specific OnRamp
.
When a message is received on the destination chain, the router is the contract that “delivers” tokens to the user's account or the message to the receiver's smart contract.
2) OnRamp
As mentioned in the previous paragraph, the router contract routes the instruction to the destination-specific OnRamp
. One OnRamp
contract per lane exists.
This contract performs the following tasks:
Checks destination-blockchain-specific validity, such as validating account address syntax
Verifies the message size limit and gas limits
Keeps track of sequence numbers to preserve the sequence of messages for the receiver
Manages billing
Interacts with the
TokenPool
if the message includes a token transfer.Emits an event monitored by the committing DON
3) Token Pool
Each token has its own token pool, an abstraction layer over ERC-20 tokens that facilitates OnRamp
and OffRamp
token-related operations.
Token pools are configurable to lock
or burn
at the source blockchain and unlock
or mint
at the destination blockchain. The mechanism for handling tokens depends on the characteristics of the token in question. Token handling mechanisms are described in the Token handling mechanisms paragraph.
Token pools provide rate limiting, which is a security feature enabling token issuers to set a maximum rate at which their token can be transferred. Rate limits are described in the Rate Limits paragraph.
4) Risk Management Network contract
The Risk Management Network contract maintains the list of Risk Management Network nodes' addresses allowed to bless/curse and holds the quorum logic for blessing a committed Merkle Root and cursing CCIP on a destination blockchain.
Risk Management Network is described in the Risk Management Network chapter.
5) Commit Store
The Committing DON interacts with the CommitStore
contract on the destination blockchain to store the Merkle root of the finalized messages on the source blockchain. This Merkle root must be blessed by the Risk Management Network before Executing DON can execute them on the destination blockchain. The CommitStore
ensures the message is blessed by the Risk Management Network and only one CommitStore
exists per lane.
6) OffRamp
One OffRamp contract per lane exists.
This contract performs the following tasks:
Ensures the message is authentic by verifying the proof provided by the Executing DON against a committed and blessed Merkle root
Makes sure transactions are executed only once
After validation, the OffRamp contract transmits any received message to the Router contract. If the CCIP transaction includes token transfers, the OffRamp contract calls the TokenPool to transfer the correct assets to the receiver.
Off-chain components
1) Committing DON
The Committing DON has several jobs where each job monitors cross-chain transactions between a given source blockchain and destination blockchain:
Each job monitors events from a given
OnRamp
contract on the source blockchain.The job waits for finality, which depends on the source blockchain.
The job bundles transactions and creates a Merkle root. This Merkle root is signed by a quorum of oracles nodes part of the Committing DON.
Finally, the job writes the Merkle root to the
CommitStore
contract on the given destination blockchain.
2) Executing DON
Like the Committing DON, the Executing DON has several jobs where each executes cross-chain transactions between a source blockchain and a destination blockchain:
Each job monitors events from a given
OnRamp
contract on the source blockchain.The job checks whether the transaction is part of the relayed Merkle root in the
CommitStore
contract.The job waits for the Risk Management Network to bless the message.
Finally, the job creates a valid Merkle proof, which is verified by the
OffRamp
contract against the Merkle root in theCommitStore
contract. After these check pass, the job calls theOffRamp
contract to complete the CCIP transactions on the destination blockchain.
Separating commitment and execution permits the Risk Management Network to have enough time to check the commitment of messages before executing them. The delay between commitment and execution also permits additional checks such as abnormal reorg depth, potential simulation, and slashing.
Saving a commitment is compact and has a fixed gas cost, whereas executing user callbacks can be highly gas intensive. Separating commitment and execution permits execution by end users in various cases, such as retrying failed executions.
3) Risk Management Network
The Risk Management Network is a set of independent nodes that monitor the Merkle roots committed by the Committing DON into the Commit Store.
Each node compares the committed Merkle roots with the transactions received by the OnRamp contract. After the verification succeeds, it calls the Risk Management Network contract to "bless" the committed Merkle root. When there are enough blessing votes, the root becomes available for execution. In case of anomalies, each Risk Management Network node calls the Risk Management Network contract to "curse" the system. If the cursed quorum is reached, the Risk Management Network contract is paused to prevent any CCIP transaction from being executed.
Risk Management Network is described in the Risk Management Network chapter.
Processing the CCIP Message
We will now see what the workflow of a CCIP Message looks like in a couple of steps.
Step 1: Prepare
The Sender prepares a CCIP Message (
EVM2AnyMessage
) for their cross-chain transaction to a destination blockchain (chainSelector
) of choice. A CCIP message includes the following information:Receiver
Data payload
Tokens and amounts
Fee token
Additional parameters (
gasLimit
,strict
)
The Sender calls
Router.getFee()
to receive the total fees (gas + premium) to pay CCIP and approves the requested fee amount.The Sender calls
Router.ccipSend()
, providing the CCIP Message they want to send along with their desired destinationchainSelector
. In the case of token transfers, the amount to be transferred must be approved to the Router.
Step 2: Send
The Router validates the received Message (e.g., valid and supported destination
chainId
and supported tokens at the destination chain).The Router receives and transfers fees to the
OnRamp
.The Router receives and transfers tokens to its corresponding Token Pool. If the sender has not approved the tokens to the Router, this will fail.
The Router forwards the Message to the correct
OnRamp
(based on destinationchainSelector
) for processing:Validate the message (number of tokens,
gasLimit
, data length …)[For token transfers] Ensure that the transferred value does not hit the aggregate rate limit of the lane.
Sequence the message with a sequence number.
For each Token included in the Message: instruct the token pool to lock/burn the tokens. This will also validate the token pool rate limit for this lane.
The
OnRamp
emits an event containing the sequenced message. This triggers the DONs to process the message.A
messageId
is generated and returned to the Sender.
Step 3: Committing DON
Nodes in the Committing DON listen for events of Messages that are ready to be sent
Messages must be finalized to be considered secure against reorg attacks.
Triggered by time or number of messages queued in the
OnRamp
, the Committing DON creates a Report with a commitment of all messages ready to be sent, in a batch. This commitment takes the form of a Merkle RootUpon consensus in the Committing DON, the Report containing the Merkle Root is transmitted to the
CommitStore
contract on the destination chainThe Risk Management Network “blesses” the Merkle Root in the
CommitStore
, to make sure it is a correct representation of the queued messages at theOnRamp
.
Merkle Root & Merkle Proof
Merkle Root: the root hash of the Merkle Tree. It is a commitment to all the leaves (Messages
M1
-M4
) in the tree. Each node in the tree is the hash of the nodes below it.Merkle Proof: to prove that Message M1 is included in the Merkle Root (commitment), a Prover provides the following elements as proof to a Verifier (who only has the root hash):
M1
H(M2)
H(H(M3),H(M4))
Using this Merkle Proof, a Verifier can easily verify that, indeed,
M1
is included in the commitment (root hash) that it possesses.
Step 4: Executing DON
Nodes in the Executing DON listen for events of Messages that are ready to be sent, similar to the Committing DON
Messages must be finalized to be considered secure against reorg attacks.
In addition to the time or number of messages queued in the
OnRamp
, the Executing DON also monitors theCommitStore
to make sure the messages are ready to be executed at the destination chain, i.e., are the messages included in a blessed on-chain commitment?If conditions are met, the Executing DON creates a Report with all messages ready to be sent, in a batch. It accounts for the
gasLimit
of each message in its batching logic. It also calculates a relevant Merkle Proof for each message to prove that the message is included in the Merkle Root submitted by the Committing DON in theCommitStore
. Note that Executing DON batches can be any subset of a Committing DON batch.Upon consensus, the Report is transmitted to the
OffRamp
contract on the destination chain
Step 5: Execute
For each message in the batch received, the
OffRamp
verifies using the provided Merkle Proof whether the transaction is included in the blessed commitment in theCommitStore
.If tokens are included in the transaction, the
OffRamp
validates the aggregate rate limit of the lane and identifies the matching Token Pool(s).OffRamp
calls theTokenPool
’sunlock
/mint
function. This will validate the token pool rate limit, unlock or mint the token and transfer them to the specified receiver.If the receiver is not an EOA and has the correct interface implemented, the
OffRamp
uses the Router to call the receiver’sccipReceive()
functionThe receiver processes the message and is informed where the message comes from (blockchain + sender), the tokens transferred, and the data payload (with relevant instructions).
Based on the data payload, the receiver might transfer the tokens to the final recipient (end-user)
Token handling mechanisms
To transfer tokens using CCIP, token pools on both blockchains must exist. That's why, if you remember the first chapter of this Masterclass, we said that you can transfer only supported tokens, not all of them.
Technically, tokens are not transferred. Instead, they are locked or burned on the source chain and then unlocked or minted on the destination chain
Token handling mechanisms are a key aspect of how token transfers work. They each have different characteristics with trade-offs for issuers, holders, and DeFi applications.
Burn & Mint
Tokens are burned on the source chain and minted natively on the destination chain
Use cases:
Tokens that are natively minted on multiple blockchains and have a variable total supply.
Examples: stablecoins, synthetic/derivative tokens, wrapped tokens (from Lock & Mint)
Lock & Mint (Reverse: Burn & Unlock)
Tokens are locked on the source chain (in Token Pools), and wrapped/synthetic/derivative tokens that represent the locked tokens are minted on the destination chain.
Use cases:
Tokens minted on a single chain (e.g., LINK)
Tokens with encoded constraints (supply/burn/mint)
Secure minting function with Proof-of-Reserve
Lock & Unlock [ON THE ROADMAP]
Transferred tokens are locked on the source chain (in Token Pools) and unlocked from Token Pools on the destination chain. This feature is not live yet.
Use cases:
Canonical/dominant wrapped tokens (eg: WETH, LINK …)
Rate Limits
The main objective of Rate Limits is to manage risk by limiting the value flowing through CCIP. A rate limit always applies to a lane.
Each available lane has two rate limits:
Network rate limit: Each network has a limit on the total USD value that can be transferred from one network across all available lanes. CCIP uses Chainlink Data Feeds to calculate the total USD value of tokens transferred on one network.
Token rate limits: Each individual lane on a network might have a limit on the total number of tokens that it can transfer. This limit is independent of the USD value of the tokens.
Both limits have the following concepts:
Bucket: holds the value that can be transferred at any given moment.
Capacity: the capacity of the bucket represents the maximum value that can be transferred at once via a single transaction.
Refill Rate: the rate at which the bucket is refilled, denominated per second.
Availability: the current amount of value in the bucket to be transferred.
Let's see how these Rate Limits work in an example:
Capacity = $100,000
Refill rate = $100 per second
Simulation
t=0s: the bucket is empty
t=300s: availability of $30,000
t=300s: user tries to send $40,000 → has to wait until t=400s when bucket has $40,000 available
t=400s: user submits $40,000 transfer again → transfer is executed, availability is $0
t=500s: availability of $10,000
t=1400s: bucket has maxed at $100,000 capacity
You can check these limits for each lane on the Official Chainlink Documentation. Here are the parameters for the real-world example, Ethereum Sepolia => Optimism Goerli lane:
If a rate limit is hit, the following errors can be returned. The user can handle these gracefully in their front end so end-users are optimally informed to decide their next step. In the case of multiple tokens in a single token transfer, the error is returned for the first hit. That is why tokenAddress
is included in the error as a parameter.
TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress);
- User requests to transfer more of a token than the capacity of the bucket.TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress);
- User requests to transfer more of a token than is currently available in the bucket. The User might have to wait at least minWaitInSeconds for enough availability or transfer the currently available amount.AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested);
- User requests to transfer more value than the capacity of the aggregate rate limit bucket.AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available);
- User requests to transfer more value than currently available in the bucket. The User might have to wait at least minWaitInSeconds for enough availability or transfer the currently available amount.
Risk Management Network
The Risk Management Network is built using off-chain and on-chain components:
Off-chain: Several Risk Management Network nodes continually monitor all supported chains against abnormal activities
On-chain: One Risk Management Network contract per supported CCIP chain
Off-chain (Risk Management Network nodes)
The Risk Management Network is a secondary validation service parallel to the primary CCIP system. It doesn't run the same codebase as the DON to mitigate against security vulnerabilities that might affect the DON's codebase. The Risk Management Network has two main modes of operation:
Blessing: Each Risk Management Network node monitors all Merkle roots of messages committed on each destination chain. The Committing DON commits these Merkle roots. The Risk Management Network node independently reconstructs the Merkle tree by fetching all messages on the source chain. Then, it checks for a match between the Merkle root committed by the Committing DON and the root of the reconstructed Merkle tree. If both Merkle roots match, the Risk Management Network node blesses the root to the Risk Management Network contract on the destination chain. The Risk Management Network contract tracks the votes. When a quorum is met, the Risk Management Network contract dubs the Merkle root blessed.
Cursing: If a Risk Management Network node detects an anomaly, the Risk Management Network node will curse the CCIP system. After a quorum of votes has been met, the Risk Management Network contract dubs the CCIP system cursed. CCIP will automatically pause on that chain and wait until the contract owner assesses the situation before potentially lifting the curse. There are two cases where Risk Management Network nodes pause CCIP:
Finality violation: A deep reorganization that violates the safety parameters set by the Risk Management Network configuration occurs on a CCIP chain.
Execution safety violation: A message is executed on the destination chain without any matching transaction being on the source chain. Double executions fall into this category since the executing DON can only execute a message once.
On-chain (Risk Management Network contract)
There is one Risk Management Network contract for each supported destination chain. The Risk Management Network contract maintains a group of nodes authorized to participate in the Risk Management Network blessing/cursing. Each Risk Management Network node has five components:
an address for voting to curse
an address for voting to bless
an address for withdrawing a vote to curse
a curse weight
a blessing weight
The contract also maintains two thresholds to determine the quorum for blessing and cursing. There are two different voting logics depending on the mode:
Blessing voting procedure: every time a Risk Management Network node blesses a Merkle root, the Risk Management Networkcontract adds the blessing weight for that node. If the sum exceeds the blessing threshold, the Risk Management Network contract considers the contract blessed.
Cursing voting procedure: a Risk Management Network node that sends a vote to curse assigns the vote a random 32-byte ID. The node may have multiple active votes to curse at any time. However, if there is at least one active cursing vote, the Risk Management Network contract considers the node to have voted to curse. The Risk Management Network contract adds the cursing weight for that node. If the sum of the weights of votes to curse exceeds the curse threshold, the Risk Management Network contract considers the contract cursed.
If the Risk Management Network contract is cursed, then the owner of the original contract must resolve any underlying issues the original contract might have. If the owner is satisfied that these issues have been resolved, they can revoke the cursing on behalf of Risk Management Network nodes.
If you made it this far, congratulations! You were amazing!
It was an intense ride, but this is the end of our CCIP Masterclass. If you are still curious about what you can build with CCIP, there is a Going Beyond Masterclass section.
Last updated