1/5
# Receiving cross-chain messages In the previous lesson: - We created and deployed a `Sender` contract to send a CCIP message on Sepolia. - We created and deployed a `Vault` contract on Base Sepolia. This contract will be called using the data in the cross-chain message. Now, we need to create the `Receiver` contract and deploy it to the destination chain (Base Sepolia, in our case). This contract address will be passed in the cross-chain message on the source chain (Speolia, in our case) to receive the cross-chain message. ## Receiver contract For contracts to be able to receive CCIP messages, they need to inherit the [`CCIPReceiver`](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/ccip/applications/CCIPReceiver.sol) contract. This is an abstract contract and enforces inheriting contracts to implement a function called `_ccipReceive`. This function is called by CCIP when sending the cross-chain message and will contain the logic to handle the data sent cross-chain. In our example, we encoded a target contract and function call. This was the `deposit` function on the `Vault` contract, which automatically deposits our USDC into a vault after sending the tokens cross-chain. Create a file inside the `contracts` folder called `Receiver.sol` and paste the `Receiver.sol` code from the [course code repo](https://github.com/Cyfrin/chainlink-fundamentals-cu/blob/main/chainlink-course-code/ccip/Receiver.sol). ### Code explainer As always, let's walk through this code. If you are not a developer or aspiring developer and are not interested in the technical details of implementing receiving CCIP messages, feel free to skip to the deployment section. #### Imports The contract imports several Chainlink CCIP-related dependencies: - `IRouterClient`: Interface for the CCIP router. - `Client`: Library containing CCIP message struct. - `CCIPReceiver`: Abstract contract that needs to be inherited to receive cross-chain messages. - `IERC20` & `SafeERC20`: For handling token transfers safely. - `Ownable`: To implement ownership. ```solidity import {IRouterClient} from "@chainlink/contracts-ccip@1.5.0/src/v0.8/ccip/interfaces/IRouterClient.sol"; import {Client} from "@chainlink/contracts-ccip@1.5.0/src/v0.8/ccip/libraries/Client.sol"; import {CCIPReceiver} from "@chainlink/contracts-ccip@1.5.0/src/v0.8/ccip/applications/CCIPReceiver.sol"; import {IERC20} from "@chainlink/contracts-ccip@1.5.0/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@chainlink/contracts-ccip@1.5.0/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts@5.2.0/access/Ownable.sol"; ``` #### Safe Transfers This line implements the SafeERC20 library for all IERC20 interactions. This library adds protection against failed token transfers by requiring success confirmation. ```solidity using SafeERC20 for IERC20; ``` #### State variables, events, and errors - `MessageReceived`: Event emitted when a cross-chain message is received. - `s_sender`: State variable for the allowed sender address. - `SOURCE_CHAIN_SELECTOR`: Hardcoded chain ID for Sepolia testnet. This has been hard-coded for clarity. - Custom errors for various failure conditions. ```solidity event MessageReceived( bytes32 indexed messageId, uint64 indexed sourceChainSelector, address sender, bytes data, address token, uint256 tokenAmount ); address private s_sender; uint64 private constant SOURCE_CHAIN_SELECTOR = 16015286601757825753; // only allow messages from Sepolia error Receiver__NothingToWithdraw(); error Receiver__NotAllowedForSourceChainOrSenderAddress(uint64 sourceChainSelector, address sender); error Receiver__FunctionCallFail(); error Receiver__SenderNotSet(); ``` #### Constructor The constructor: - Initializes the `CCIPReceiver` with the Base Sepolia router address (`0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93`) - Initializes the `Ownable` constructor with the deployer as the initial owner. ```solidity constructor() CCIPReceiver(0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93) Ownable(msg.sender) {} ``` #### onlyAllowlisted modifier This modifier restricts message processing to: 1. Verify the sender address has been set. 2. Only allow messages from the configured source chain (Sepolia) and the allowlisted sender address. ```solidity modifier onlyAllowlisted(uint64 _sourceChainSelector, address _sender) { if (s_sender == address(0)) { revert Receiver__SenderNotSet(); } if (_sourceChainSelector != SOURCE_CHAIN_SELECTOR || _sender != s_sender) { revert Receiver__NotAllowedForSourceChainOrSenderAddress(_sourceChainSelector, _sender); } _; } ``` #### Setting the sender address Only the contract owner can set the trusted sender address that can send CCIP messages to this contract. ```solidity function setSender(address _sender) external onlyOwner { s_sender = _sender; } ``` #### _ccipReceive This is the core function that processes incoming CCIP messages: - It implements the `_ccipReceive` function from the `CCIPReceiver` abstract contract and uses the `onlyAllowlisted` modifier. - Decodes the message data to extract a target address (the `Vault` contract) and function calldata (the `deposit` function signature and arguments). - Makes a low-level call to the target contract with the function data. - Reverts if the call fails. - Emits a `MessageReceived` event with details about the message. ```solidity function _ccipReceive( Client.Any2EVMMessage memory any2EvmMessage ) internal override onlyAllowlisted( any2EvmMessage.sourceChainSelector, abi.decode(any2EvmMessage.sender, (address)) ) { (address target, bytes memory functionCallData) = abi.decode(any2EvmMessage.data, (address, bytes)); (bool success, ) = target.call(functionCallData); if (!success) { revert Receiver__FunctionCallFail(); } emit MessageReceived( any2EvmMessage.messageId, any2EvmMessage.sourceChainSelector, abi.decode(any2EvmMessage.sender, (address)), any2EvmMessage.data, any2EvmMessage.destTokenAmounts[0].token, any2EvmMessage.destTokenAmounts[0].amount ); } ``` #### withdrawToken This function allows the contract owner to withdraw any ERC-20 tokens in the contract: - Checks the contract's balance of the specified token. - Reverts if there are no tokens to withdraw. - Uses `SafeERC20`'s `safeTransfer` function to safely send all tokens to the owner. ```solidity function withdrawToken(address _token) public onlyOwner { uint256 amount = IERC20(_token).balanceOf(address(this)); if (amount == 0) revert Receiver__NothingToWithdraw(); IERC20(_token).safeTransfer(msg.sender, amount); } ``` The contract is designed to receive cross-chain messages from a specific chain (Sepolia) and a specifically allowed-sender address, providing security. ### Compile and deploy the contract Compile the `Receiver` contract and deploy it to Base Sepolia. Make sure you are still connected to Base Sepolia in MetaMask. Click **Deploy**: ::image{src='/chainlink-fundamentals/6-chainlink-ccip-messages/assets/receiver.png' style='width: 100%; height: auto;' alt='receiver'} Once it has been deployed, remember to pin it to your workspace! Also, note down the address again, as you did with the `Sender` and `Vault` addresses, so you can copy it without having to connect to Base Sepolia. ### Setting the sender Before we move on, let's set the `s_sender` address. - In the **Deployed Contracts** section, expand the `Receiver` contract instance. - Next to the `setSender` function, paste the `Sender` address (from Sepolia that we noted in the previous lesson), click **setSender**, and confirm the transaction in MetaMask to allowlist the `Sender` contract to send the `Receiver` cross-chain messages. You are now ready to send your cross-chain message to bridge USDC and automatically deposit it into a vault!
A hands-on tutorial to Receiving cross-chain messages - Learn to build, deploy, and configure a `Receiver` smart contract on the destination chain, enabling it to securely process incoming Chainlink CCIP messages.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Smart contract and Solidity fundamentals
Chainlink’s decentralized oracle network (DON)
Chainlink Data Feeds
Chainlink Data Streams
Chainlink Automation
Chainlink CCIP
Chainlink Functions
Verifiable Random Function (VRF)
Chainlink Proof of Reserve
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Blockchain Financial Analyst
$100,000 - $150,000 (avg. salary)
Last updated on May 19, 2025
Solidity Developer
Chainlink FundamentalsDuration: 9min
Duration: 1h 16min
Duration: 41min
Duration: 30min
Duration: 1h 02min
Duration: 48min
Duration: 30min
Duration: 19min
Duration: 36min
Duration: 30min
Certification: Chainlink Fundamentals
The Chainlink Fundamentals proficiency exam covers is designed to confirm your grasp of all key concepts and learnings presented in the course material. Exam takers will have 75 minutes to complete 50 questions and must score 30/50 to pass and earn a Certificate of Completion. Because course material is continually updated, The Chainlink Fundamentals Certificate of Completions expires after 1 year. To remain current, holders must re-take the exam and pass to confirm their current knowledge.
Course Overview
About the course
Smart contract and Solidity fundamentals
Chainlink’s decentralized oracle network (DON)
Chainlink Data Feeds
Chainlink Data Streams
Chainlink Automation
Chainlink CCIP
Chainlink Functions
Verifiable Random Function (VRF)
Chainlink Proof of Reserve
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Blockchain Financial Analyst
$100,000 - $150,000 (avg. salary)
Last updated on May 19, 2025