---
description: The minimal code needed to send and receive CCIP Messages
---

# How to use Chainlink CCIP

## The minimal CCIP architecture

To recap, with Chainlink CCIP, one can:

* Transfer (supported) tokens
* Send any kind of data&#x20;
* Send both tokens and data

CCIP receiver can be:

* EOA
* Any smart contract that implements `CCIPReceiver.sol`

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FeBXc5q78HsM8SNOYHUMS%2Fbasic-architecture.png?alt=media&#x26;token=bae48b0f-ff55-49b3-857b-d8f35166266e" alt=""><figcaption><p>Basic CCIP Architecture</p></figcaption></figure>

**Note**: If you send a message and token(s) to EOA, only tokens will arrive.

For now, you can consider CCIP as a "black-box" component and be aware of the Router contract only. We will explain the Chainlink CCIP architecture in the following chapters.

## Getting started

You can use Chainlink CCIP with any blockchain development framework. For this Masterclass, we prepared the steps for Hardhat, Foundry, and Remix IDE.

Let's create a new project

{% tabs %}
{% tab title="Hardhat" %}
Make sure you have [Node.js](https://nodejs.org/en/download) and [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) installed. To check, run the following command:

```sh
node -v
```

```sh
npm -v
```

Create a new folder and name it `ccip-masterclass`

```sh
mkdir ccip-masterclass
```

Navigate to it

```sh
cd ccip-masterclass
```

Create a hew Hardhat project by running:

```shell
npx hardhat@2.14.1 init
```

And then select either "Create a JavaScript project" or "Create a TypeScript project".&#x20;
{% endtab %}

{% tab title="Foundry" %}
Make sure you have [Foundry](https://book.getfoundry.sh/getting-started/installation) installed. To check, run the following command:

```sh
forge --version
```

Create a new folder and name it `ccip-masterclass`

```sh
mkdir ccip-masterclass
```

Navigate to it

```sh
cd ccip-masterclass
```

Create a hew Foundry project by running:

```sh
forge init
```
{% endtab %}

{% tab title="Remix" %}
Navigate to [https://remix.ethereum.org/](https://remix.ethereum.org/) and click the "Create new Workspace" button.
{% endtab %}
{% endtabs %}

Alternatively, you can clone:

* [CCIP Starter Kit (Hardhat version)](https://github.com/smartcontractkit/ccip-starter-kit-hardhat)
* [CCIP Starter Kit (Foundry version)](https://github.com/smartcontractkit/ccip-starter-kit-foundry)

## The @chainlink/contracts-ccip NPM package

To use Chainlink CCIP, you need to interact with Chainlink CCIP-specific contracts from the [@chainlink/contracts-ccip](https://www.npmjs.com/package/@chainlink/contracts-ccip) NPM package.

{% embed url="https://www.npmjs.com/package/@chainlink/contracts-ccip" %}

To install it, follow steps specific to the development environment you will use for this Masterclass.

{% tabs %}
{% tab title="Hardhat" %}
```bash
npm i @chainlink/contracts-ccip --save-dev
```
{% endtab %}

{% tab title="Foundry" %}
We cannot use git submodules to install `@chainlink/contracts-ccip` because the content of this package is not available as a separate GitHub repo. This essentially means that we cannot run a `forge install` command.\
\
Here's the workaround:

Add the following line to the `.gitignore` file

```gitignore
# Node modules
node_modules/
```

Then run the following command in your Terminal:

```sh
npm i @chainlink/contracts-ccip --save-dev
```

Finally, add the following lines to the `foundry.toml` file:

```toml
libs = ['node_modules', 'lib']
remappings = [
    '@chainlink/contracts-ccip/=node_modules/@chainlink/contracts-ccip'
]
```
{% endtab %}

{% tab title="Remix" %}
Create a new Solidity file, and paste the following content. It is an empty contract that just imports one of the contracts from the `@chainlink/contracts-ccip` package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract Empty {}
```

Compile it. If compiled successfully and new `.deps/npm/@chainlink/contracts-ccip` folders are generated, that means we imported the `@chainlink/contracts-ccip` package into the Remix IDE Workspace.

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FMsZsCI2zV43p9D926cSd%2Fcompile.png?alt=media&#x26;token=a6e817db-1784-4d9f-bd52-70fed12b12f9" alt=""><figcaption><p>Remix IDE compile</p></figcaption></figure>
{% endtab %}
{% endtabs %}

## Basic interface

Although, as being said, CCIP sender and receiver can be EOA and smart contract, and all combinations are possible, we are going to cover the most complex use-case where both CCIP sender and receiver are smart contracts on different blockchains.

### Source blockchain

To send CCIP Messages, the smart contract on the source blockchain must call the `ccipSend()` function, which is defined the `IRouterClient.sol` interface.

```solidity
// SOURCE BLOCKCHAIN 

interface IRouterClient {
    /// @notice Request a CCIP message to be sent to the destination chain
    /// @param destinationChainSelector The destination chain selector
    /// @param message The cross-chain CCIP message including data and/or tokens
    /// @return messageId The message ID
    function ccipSend(
        uint64 destinationChainSelector,
        Client.EVM2AnyMessage calldata message
    ) external payable returns(bytes32 messageId);
}
```

The CCIP Message which is being sent is a type of `EVM2AnyMessage` Solidity struct from the `Client` library.

```solidity
// SOURCE BLOCKCHAIN

library Client {
    struct EVM2AnyMessage {
        bytes receiver; // abi.encode(receiver address) for dest EVM chains
        bytes data; // data payload
        EVMTokenAmount[] tokenAmounts; // token transfers
        address feeToken; // fee token address; address(0) means you are sending msg.value
        bytes extraArgs; // populate this with _argsToBytes(EVMExtraArgsV1)
    }
    
    struct EVMTokenAmount {
        address token; // token address on local blockchain
        uint256 amount;
    }
    
    struct EVMExtraArgsV1 {
        uint256 gasLimit;
        bool strict;
    }
}
```

Let's now understand what each property of the `EVM2AnyMessage` struct we are sending represents and how to use it.

#### receiver

Receiver address. It can be a smart contract or an EOA. Use `abi.encode(receiver)` to encode the address to the `bytes` Solidity data-type.

#### data

Payload sent within the CCIP message. This is that "any type of data" one can send as a CCIP Message we are referring to from the start. It can be anything from simple text like "Hello, world!" to Solidity structs or function selectors.

#### tokenAmounts

Tokens and their amounts in the source chain representation. Here we are specifying which tokens (out of supported ones) we are sending and how much of it. This is the array of a `EVMTokenAmount` struct, which consists of two properties only:

* `token` - Address of a token we are sending on the local (source) blockchain
* `amount` The amount of tokens we are sending. The sender must approve the CCIP router to spend this amount on behalf of the sender, otherwise the call to the `ccipSend` function will revert.

Currently, the maximum number of tokens one can send in a single CCIP send transaction is five.

#### feeToken

Address of feeToken. CCIP supports fee payments in LINK and in alternative assets, which currently include native blockchain gas coins and their ERC20 wrapped versions. For developers, this means you can simply pay on the source chain, and CCIP will take care of execution on the destination chain. Set `address(0)` to pay in native gas coins such as ETH on Ethereum or MATIC on Polygon. Keep in mind that even if you are paying for fees in the native asset, nodes in the Chainlink DON will be rewarded in LINK only.

#### extraArgs

Users fill in the `EVMExtraArgsV1` struct and then encode it to bytes using the `_argsToBytes` function. The struct consists of two properties:

* `gasLimit` - The maximum amount of gas CCIP can consume to execute `ccipReceive()` on the contract located on the destination blockchain. **Unspent gas is not refunded.** This means that if you are sending tokens to EOA, for example, you should put 0 as a `gasLimit` value because EOAs can't implement the `ccipReceive()` (or any other) function. To estimate the accurate gas limit for your destination contract, consider Leveraging Ethereum client RPC by applying `eth_estimateGas` on `receiver.ccipReceive()` function, or use the [Hardhat plugin for gas tests](https://github.com/cgewecke/eth-gas-reporter), or conduct [Foundry gas tests](https://book.getfoundry.sh/forge/gas-tracking).
* `strict` - Used for strict sequencing. **You should set it to `false`**. CCIP will always process messages sent from a specific sender to a specific destination blockchain in the order they were sent. If you set `strict: true` in the `extraArgs` part of the message, and if the `ccipReceive` fails (reverts), it will _**prevent any following messages from the same sender from being processed until the current message is successfully executed**_. You should be very careful when using this feature to avoid unintentionally stopping messages from the sender from being processed. The strict sequencing feature is currently experimental, and there is no guarantee of its maintenance or further development in the future.

If `extraArgs` are left empty, a.k.a `extraArgs: ""`, a default of _200\_000_ `gasLimit` will be set with no strict sequencing. For production deployments, make sure that `extraArgs` is mutable. This allows you to build it off-chain and pass it in a call to a function or store it in a variable that you can update on demand. This makes `extraArgs` compatible with future CCIP upgrades.

### Destination blockchain

To receive CCIP Messages, the smart contract on the destination blockchain must implement the `IAny2EVMMessageReceiver` interface. The [@chainlink/contracts-ccip](https://www.npmjs.com/package/@chainlink/contracts-ccip) NPM package comes up with the contract which implements it in the right way, called `CCIPReceiver.sol`, but we are going to talk more about it in the next chapter. For now, let's understand which functions from the `IAny2EVMMessageReceiver` interface must be implemented in the general-case scenario.

```solidity
// DESTINATION BLOCKCHAIN

/// @notice Application contracts that intend to receive messages from 
/// the router should implement this interface.
interface IAny2EVMMessageReceiver {
    /// @notice Router calls this to deliver a message
    /// @param message CCIP Message
    /// @dev Note ensure you check that msg.sender is the Router
    function ccipReceive(Client.Any2EVMMessage calldata message) external;
}
```

As you can see, the `ccipReceive()` function from the `IAny2EVMMessageReceiver` interface accepts object of the `Any2EVMMessage`  struct from the `Client` library. This struct is the Solidity representation of the received CCIP Message. Please note that this struct, `Any2EVMMessage` is **different** than the one we used to send on the source blockchain - `EVM2AnyMessage`. They are not the same.

```solidity
// DESTINATION BLOCKCHAIN

library Client {
    struct Any2EVMMessage {
        bytes32 messageId; // MessageId corresponding to ccipSend on source
        uint64 sourceChainSelector; // Source chain selector
        bytes sender; // abi.decode(sender) if coming from an EVM chain
        bytes data; // payload sent in original message
        EVMTokenAmount[] tokenAmounts; // Tokens and their amounts at destination
    }
    
    struct EVMTokenAmount {
        address token; // token address on local blockchain
        uint256 amount;
    }
}
```

Let's now understand what each property of the `Any2EVMMessage` struct we are receiving represents and how to use it.

* `messageId` - CCIP Message Id, generated on the source chain.
* `sourceChainSelector` - Source chain selector.
* `sender` - Sender address. `abi.decode(sender, (address))` if the source chain is an EVM chain.
* `data` - Payload sent within the CCIP message. For example, "Hello, world!"
* `tokenAmounts` - Received tokens and their amounts in their destination chain representation.

To recap, here's the diagram with the minimal architecture needed to send & receive the Chainlink CCIP Message:

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FNpAx9dA1zjw61OYapFFE%2Finterfaces.png?alt=media&#x26;token=dddb85b6-d903-43e3-8e86-ea1ff3aff54b" alt=""><figcaption><p>Developer Interfaces</p></figcaption></figure>



***

## Coding time 🎉&#x20;

Now that we understand what basic CCIP architecture looks like and how to use it let's write our first CCIP Sender & CCIP Receiver contracts. Keep in mind that this is the minimal code needed to send & receive CCIP Messages and that it is very unsafe for production usage, but we will cover that in the following chapters.

We are going to use Avalanche Fuji -> Ethereum Sepolia lane because it is the fastest one. The idea is to send a simple text message as a `data` payload.&#x20;

### Develop CCIP Sender contract

Follow the steps to create a basic CCIP Sender contract.

{% tabs %}
{% tab title="Hardhat" %}
Create a new file inside the `contracts` folder and name it `CCIPSender_Unsafe.sol`

Start with the development by setting the Solidity compiler version and importing necessary contracts from the `@chainlink/contracts-ccip` NPM package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {}
```

Since we are importing the `LinkTokenInterface` interface from the `@chainlink/contracts` package, which we haven't installed yet, the compilation will fail. To solve the potential issue, let's install the `@chainlink/contracts` NPM package:

```bash
npm i @chainlink/contracts --save-dev
```

Now let's add storage variables for the `Router.sol` smart contract address and the`LINK` token address which we will use for fees. Also, we will approve the `Router.sol` to spend the maximum possible amount of `LINK` tokens this contract poses. This is an extremely bad practice, and in the next chapter, you will see how to approve the exact amount needed for fees, but for the sake of simplicity and better code readability, we are choosing the current path. Our goal for this lecture is to understand minimal principles when it comes to sending & receiving CCIP Messages.

{% code fullWidth="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {
    address link;
    address router;

    constructor(address _link, address _router) {
        link = _link;
        router = _router;
        LinkTokenInterface(link).approve(router, type(uint256).max);
    }
}
```
{% endcode %}

Finally, let's write a function to send a CCIP Message. We will pass the address of the Receiver contract (to be developed & deployed) as a function argument alongside the destination chain selector (although we know that it is going to be the Ethereum Sepolia's one) and the simple text we want to send.

We are not sending any tokens, so we are assigning the empty `Client.EVMTokenAmount` array to the `tokenAmounts` field. For the sake of simplicity and code readability, we will not set the `extraArgs` field either, which will then default to 200\_000 for the `gasLimit` and `false` for sequencing. Finally, we will use `LINK` tokens to pay for CCIP fees.

{% code title="CCIPSender_Unsafe.sol" lineNumbers="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {
    address link;
    address router;

    constructor(address _link, address _router) {
        link = _link;
        router = _router;
        LinkTokenInterface(link).approve(router, type(uint256).max);
    }
    
    function send(address receiver, string memory someText, uint64 destinationChainSelector) external {
        Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
            receiver: abi.encode(receiver),
            data: abi.encode(someText),
            tokenAmounts: new Client.EVMTokenAmount[](0),
            extraArgs: "",
            feeToken: link
        });

        IRouterClient(router).ccipSend(destinationChainSelector, message);
    }
}
```
{% endcode %}

Now try to compile the contract by running the following command inside your Terminal (make sure that `solidity` version inside the `hardhat.config` file is set to at least 0.8.19 or higher:

```bash
npx hardhat compile
```
{% endtab %}

{% tab title="Foundry" %}
Create a new file inside the `src` folder and name it `CCIPSender_Unsafe.sol`

Start with the development by setting the Solidity compiler version and importing necessary contracts from the `@chainlink/contracts-ccip` NPM package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {}
```

Since we are importing the `LinkTokenInterface` interface from the `@chainlink/contracts` package, which we haven't installed yet, the compilation will fail. To solve the potential issue, let's install the `@chainlink/contracts` NPM package. We can run `forge install smartcontractkit/chainlink`, but since we already installed the `@chainlink/contracts-ccip` package using NPM let's continue with the same path:

```bash
npm i @chainlink/contracts --save-dev
```

And expand the `foundry.toml` file like this:

```toml
libs = ['node_modules', 'lib']
remappings = [
    '@chainlink/contracts/=node_modules/@chainlink/contracts',
    '@chainlink/contracts-ccip/=node_modules/@chainlink/contracts-ccip',
]
```

Now let's add storage variables for the `Router.sol` smart contract address and the`LINK` token address which we will use for fees. Also, we will approve the `Router.sol` to spend the maximum possible amount of `LINK` tokens this contract poses. This is an extremely bad practice, and in the next chapter, you will see how to approve the exact amount needed for fees, but for the sake of simplicity and better code readability, we are choosing the current path. Our goal for this lecture is to understand minimal principles when it comes to sending & receiving CCIP Messages.

{% code fullWidth="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {
    address link;
    address router;

    constructor(address _link, address _router) {
        link = _link;
        router = _router;
        LinkTokenInterface(link).approve(router, type(uint256).max);
    }
}
```
{% endcode %}

Finally, let's write a function to send a CCIP Message. We will pass the address of the Receiver contract (to be developed & deployed) as a function argument alongside the destination chain selector (although we know that it is going to be the Ethereum Sepolia's one) and the simple text we want to send.

We are not sending any tokens, so we are assigning the empty `Client.EVMTokenAmount` array to the `tokenAmounts` field. For the sake of simplicity and code readability, we will not set the `extraArgs` field either, which will then default to 200\_000 for the `gasLimit` and `false` for sequencing. Finally, we will use `LINK` tokens to pay for CCIP fees.

{% code title="CCIPSender_Unsafe.sol" lineNumbers="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {
    address link;
    address router;

    constructor(address _link, address _router) {
        link = _link;
        router = _router;
        LinkTokenInterface(link).approve(router, type(uint256).max);
    }
    
    function send(address receiver, string memory someText, uint64 destinationChainSelector) external {
        Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
            receiver: abi.encode(receiver),
            data: abi.encode(someText),
            tokenAmounts: new Client.EVMTokenAmount[](0),
            extraArgs: "",
            feeToken: link
        });

        IRouterClient(router).ccipSend(destinationChainSelector, message);
    }
}
```
{% endcode %}

Now try to compile the contract by running the following command inside your Terminal (make sure your `solc` version inside the `foundry.toml` file is set to at least 0.8.19 version or higher:

```bash
forge build
```
{% endtab %}

{% tab title="Remix" %}
Create a new Solidity file by clicking on the "Create new file" button and name it `CCIPSender_Unsafe.sol`

Start with the development by setting the Solidity compiler version and importing necessary contracts from the `@chainlink/contracts-ccip` NPM package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {}
```

Now let's add storage variables for the `Router.sol` smart contract address and the`LINK` token address which we will use for fees. Also, we will approve the `Router.sol` to spend the maximum possible amount of `LINK` tokens this contract poses. This is an extremely bad practice, and in the next chapter, you will see how to approve the exact amount needed for fees, but for the sake of simplicity and better code readability, we are choosing the current path. Our goal for this lecture is to understand minimal principles when it comes to sending & receiving CCIP Messages.

{% code fullWidth="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {
    address link;
    address router;

    constructor(address _link, address _router) {
        link = _link;
        router = _router;
        LinkTokenInterface(link).approve(router, type(uint256).max);
    }
}
```
{% endcode %}

Finally, let's write a function to send a CCIP Message. We will pass the address of the Receiver contract (to be developed & deployed) as a function argument alongside the destination chain selector (although we know that it is going to be the Ethereum Sepolia's one) and the simple text we want to send.

We are not sending any tokens, so we are assigning the empty `Client.EVMTokenAmount` array to the `tokenAmounts` field. For the sake of simplicity and code readability, we will not set the `extraArgs` field either, which will then default to 200\_000 for the `gasLimit` and `false` for sequencing. Finally, we will use `LINK` tokens to pay for CCIP fees.

{% code title="CCIPSender_Unsafe.sol" lineNumbers="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract CCIPSender_Unsafe {
    address link;
    address router;

    constructor(address _link, address _router) {
        link = _link;
        router = _router;
        LinkTokenInterface(link).approve(router, type(uint256).max);
    }
    
    function send(address receiver, string memory someText, uint64 destinationChainSelector) external {
        Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
            receiver: abi.encode(receiver),
            data: abi.encode(someText),
            tokenAmounts: new Client.EVMTokenAmount[](0),
            extraArgs: "",
            feeToken: link
        });

        IRouterClient(router).ccipSend(destinationChainSelector, message);
    }
}
```
{% endcode %}

Now try to compile the contract by going to the "Solidity compiler" tab and clicking the "Compile CCIPSender\_Unsafe.sol" button. Make sure that your compiler version is set to at least 0.8.19 or higher (check the "Compiler" dropdown).

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FI0vdECNI4l4HSUxa4h6x%2Fcompile1.png?alt=media&#x26;token=0171a227-0aa3-41bd-a488-f5ec9f936a9b" alt=""><figcaption><p>Compile CCIPSender_Unsafe.sol</p></figcaption></figure>
{% endtab %}
{% endtabs %}

### Develop CCIP Receiver contract

Follow the steps to create a basic CCIP Receiver contract.

{% tabs %}
{% tab title="Hardhat" %}
Create a new file inside the `contracts` folder and name it `CCIPReceiver_Unsafe.sol`

Start with the development by setting the Solidity compiler version and importing necessary contracts from the `@chainlink/contracts-ccip` NPM package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {}
```

Now let's add storage variables to track the latest received CCIP Message data and sender. We will mark them intentionally as `public` because Solidity will automatically develop getter functions for them, thus making our codebase smaller and more readable. We want to call these getter functions to confirm that CCIP Message has been successfully received.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {
    address public latestSender;
    string public latestMessage;

    constructor(address router) CCIPReceiver(router) {}
}
```

Finally, let's implement the `_ccipReceive()` function from the `CCIPReceiver.sol` contract. Note that while we were explaining, in the previous chapter, how to develop the basic CCIP receiver contract by implementing the `IAny2EVMMessageReceiver` interface, we mentioned that there is something called the `CCIPReceiver.sol` smart contract. The `CCIPReceiver.sol` smart contract from the `@chainlink/contracts-ccip` NPM package is the smart contract that properly implements the following interface, following the major best practices, and therefore we are going to use it because it will make our code much more readable and easier to understand.

Once you receive the CCIP Message, you can do whatever you want with it. For the purpose of this very first example, we are just going to assign two storage variables we previously defined.

{% code title="CCIPReceiver_Unsafe.sol" lineNumbers="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {
    address public latestSender;
    string public latestMessage;

    constructor(address router) CCIPReceiver(router) {}
    
    function _ccipReceive(Client.Any2EVMMessage memory message) internal override {
        latestSender = abi.decode(message.sender, (address));
        latestMessage = abi.decode(message.data , (string));
    }
}
```
{% endcode %}

Now try to compile the contract by running the following command inside your Terminal (make sure that `solidity` version inside the `hardhat.config` file is set to at least 0.8.19 or higher:

```bash
npx hardhat compile
```
{% endtab %}

{% tab title="Foundry" %}
Create a new file inside the `src` folder and name it `CCIPReceiver_Unsafe.sol`

Start with the development by setting the Solidity compiler version and importing necessary contracts from the `@chainlink/contracts-ccip` NPM package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {}
```

Now let's add storage variables to track the latest received CCIP Message data and sender. We will mark them intentionally as `public` because Solidity will automatically develop getter functions for them, thus making our codebase smaller and more readable. We want to call these getter functions to confirm that CCIP Message has been successfully received.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {
    address public latestSender;
    string public latestMessage;

    constructor(address router) CCIPReceiver(router) {}
}
```

Finally, let's implement the `_ccipReceive()` function from the `CCIPReceiver.sol` contract. Note that while we were explaining, in the previous chapter, how to develop the basic CCIP receiver contract by implementing the `IAny2EVMMessageReceiver` interface, we mentioned that there is something called the `CCIPReceiver.sol` smart contract. The `CCIPReceiver.sol` smart contract from the `@chainlink/contracts-ccip` NPM package is the smart contract that properly implements the following interface, following the major best practices, and therefore we are going to use it because it will make our code much more readable and easier to understand.

Once you receive the CCIP Message, you can do whatever you want with it. For the purpose of this very first example, we are just going to assign two storage variables we previously defined.

{% code title="CCIPReceiver_Unsafe.sol" lineNumbers="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {
    address public latestSender;
    string public latestMessage;

    constructor(address router) CCIPReceiver(router) {}
    
    function _ccipReceive(Client.Any2EVMMessage memory message) internal override {
        latestSender = abi.decode(message.sender, (address));
        latestMessage = abi.decode(message.data , (string));
    }
}
```
{% endcode %}

Now try to compile the contract by running the following command inside your Terminal (make sure your `solc` version inside the `foundry.toml` file is set to at least 0.8.19 version or higher:

```bash
forge build
```
{% endtab %}

{% tab title="Remix" %}
Create a new Solidity file by clicking on the "Create new file" button, and name it  `CCIPReceiver_Unsafe.sol`

Start with the development by setting the Solidity compiler version and importing necessary contracts from the `@chainlink/contracts-ccip` NPM package.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {}
```

Now let's add storage variables to track the latest received CCIP Message data and sender. We will mark them intentionally as `public` because Solidity will automatically develop getter functions for them, thus making our codebase smaller and more readable. We want to call these getter functions to confirm that CCIP Message has been successfully received.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {
    address public latestSender;
    string public latestMessage;

    constructor(address router) CCIPReceiver(router) {}
}
```

Finally, let's implement the `_ccipReceive()` function from the `CCIPReceiver.sol` contract. Note that while we were explaining, in the previous chapter, how to develop the basic CCIP receiver contract by implementing the `IAny2EVMMessageReceiver` interface, we mentioned that there is something called the `CCIPReceiver.sol` smart contract. The `CCIPReceiver.sol` smart contract from the `@chainlink/contracts-ccip` NPM package is the smart contract that properly implements the following interface, following the major best practices, and therefore we are going to use it because it will make our code much more readable and easier to understand.

Once you receive the CCIP Message, you can do whatever you want with it. For the purpose of this very first example, we are just going to assign two storage variables we previously defined.

{% code title="CCIPReceiver_Unsafe.sol" lineNumbers="true" %}
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract CCIPReceiver_Unsafe is CCIPReceiver {
    address public latestSender;
    string public latestMessage;

    constructor(address router) CCIPReceiver(router) {}
    
    function _ccipReceive(Client.Any2EVMMessage memory message) internal override {
        latestSender = abi.decode(message.sender, (address));
        latestMessage = abi.decode(message.data , (string));
    }
}
```
{% endcode %}

Now try to compile the contract by going to the "Solidity compiler" tab and clicking the "Compile CCIPReceiver\_Unsafe.sol" button. Make sure that your compiler version is set to at least 0.8.19 or higher (check the "Compiler" dropdown).

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FoYsJigoWQMI8eGm1T0LU%2Fcompile2.png?alt=media&#x26;token=55499630-6229-4035-a339-4d86b052037a" alt=""><figcaption><p>Compile CCIPReceiver_Unsafe.sol</p></figcaption></figure>
{% endtab %}
{% endtabs %}

### Prepare for deployment

Follow the steps to add the necessary environment variables for deploying these contracts and sending your first CCIP Message.

{% tabs %}
{% tab title="Hardhat" %}
We are going to use the [`@chainlink/env-enc`](https://www.npmjs.com/package/@chainlink/env-enc) package for extra security. It encrypts sensitive data instead of storing them as plain text in the `.env` file by creating a new `.env.enc` file. Although it's not recommended to push this file online, if that accidentally happens, your secrets will still be encrypted.

Install the package by running the following command:

```bash
npm i @chainlink/env-enc --save-dev
```

Set a password for encrypting and decrypting the environment variable file. You can change it later by typing the same command.

```bash
npx env-enc set-pw
```

Now set the following environment variables: `PRIVATE_KEY`, Source Blockchain RPC URL, Destination Blockchain RPC URL. For this example, we are going to use Avalanche Fuji and Ethereum Sepolia.

```bash
PRIVATE_KEY=""
ETHEREUM_SEPOLIA_RPC_URL=""
AVALANCHE_FUJI_RPC_URL=""
```

To set these variables, type the following command and follow the instructions in the terminal:

```bash
npx env-enc set
```

After you are done, the `.env.enc` file will be automatically generated. If you want to validate your inputs, you can always run the next command:

```bash
npx env-enc view
```

Finally, expand the `hardhat.config` to support these two networks:

```typescript
import * as dotenvenc from '@chainlink/env-enc'
dotenvenc.config();

import { HardhatUserConfig } from 'hardhat/config';
import '@nomicfoundation/hardhat-toolbox';

const PRIVATE_KEY = process.env.PRIVATE_KEY;
const ETHEREUM_SEPOLIA_RPC_URL = process.env.ETHEREUM_SEPOLIA_RPC_URL;
const AVALANCHE_FUJI_RPC_URL = process.env.AVALANCHE_FUJI_RPC_URL;

const config: HardhatUserConfig = {
  solidity: '0.8.19',
  networks: {
    hardhat: {
      chainId: 31337
    },
    ethereumSepolia: {
      url: ETHEREUM_SEPOLIA_RPC_URL !== undefined ? ETHEREUM_SEPOLIA_RPC_URL : '',
      accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
      chainId: 11155111
    },
    avalancheFuji: {
      url: AVALANCHE_FUJI_RPC_URL !== undefined ? AVALANCHE_FUJI_RPC_URL : '',
      accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
      chainId: 43113
    }
  }
};

export default config;
```
{% endtab %}

{% tab title="Foundry" %}
Create a new file and name it `.env`. Fill in your wallet's PRIVATE\_KEY and RPC URLs for at least two blockchains. For this example, we are going to use Avalanche Fuji and Ethereum Sepolia.

```bash
PRIVATE_KEY=""
ETHEREUM_SEPOLIA_RPC_URL=""
AVALANCHE_FUJI_RPC_URL=""
```

Once that is done, to load the variables in the `.env` file, run the following command:

```bash
source .env
```

Finally, expand the `foundry.toml` to support these two networks:

```toml
[profile.default]
src = 'src'
out = 'out'
libs = ['node_modules', 'lib']
remappings = [
    '@chainlink/contracts/=node_modules/@chainlink/contracts',
    '@chainlink/contracts-ccip/=node_modules/@chainlink/contracts-ccip',
]
solc = '0.8.19'

[rpc_endpoints]
ethereumSepolia = "${ETHEREUM_SEPOLIA_RPC_URL}"
avalancheFuji = "${AVALANCHE_FUJI_RPC_URL}"

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
```
{% endtab %}

{% tab title="Remix" %}
Navigate to the "Deploy & run transactions" tab and select the "Injected Provider - Metamask" option from the "Environment" dropdown menu.

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FL1Ml5S7Ws15x1jRqok3Q%2Fconnect.png?alt=media&#x26;token=30ae413f-6b4b-4d91-85ef-a01f6889c592" alt=""><figcaption><p>Connect your wallet to Remix IDE</p></figcaption></figure>

If you are using Metamask wallet, the Ethereum Sepolia network should already came preinstalled. Make sure you added the Avalanche Fuji C-Chain network.

Go to [Chainlist.org](https://chainlist.org/?testnets=true\&search=avalanche+fuji) and search for "avalanche fuji". Once you see the network with Chain ID 43113, click the "Add to Metamask" button.

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FfWVIZJqwc9XFiRJ29PaH%2Fchainlist.png?alt=media&#x26;token=db07af73-754b-45a8-82c1-bb91c3c7c7f2" alt=""><figcaption><p>Add Avalanche Fuji network to Metamask</p></figcaption></figure>
{% endtab %}
{% endtabs %}

### Deploy CCIP Receiver to Ethereum Sepolia

Follow the steps to deploy the `CCIPRecevier_Unsafe` smart contract to the Ethereum Sepolia network.

{% tabs %}
{% tab title="Hardhat" %}
Create a new file under the `scripts` folder and name it `deployReceiver.ts` or `deployReceiver.js` depends on whether you work with TypeScript or JavaScript Hardhat projects.

Note that deployment of the `CCIPReceiver_Unsafe` smart contract is hard coded to Ethereum Sepolia for this example, but feel free to refactor the following deployment script to support other networks or even make it fully customizable by rewriting it to Hardhat task with (optional) parameters. You can check [CCIP Starter Kit (Hardhat version](https://github.com/smartcontractkit/ccip-starter-kit-hardhat) for reference.

```typescript
// scripts/deployReceiver.ts

import { ethers, network, run } from "hardhat";

async function main() {
  if(network.name !== `ethereumSepolia`) {
    console.error(`❌ Receiver must be deployed to Ethereum Sepolia`);
    return 1;
  }

  const sepoliaRouterAddress = `0xD0daae2231E9CB96b94C8512223533293C3693Bf`;
  
  await run("compile");

  const ccipReceiverFactory = await ethers.getContractFactory("CCIPReceiver_Unsafe");
  const ccipReceiver = await ccipReceiverFactory.deploy(sepoliaRouterAddress);

  await ccipReceiver.deployed();

  console.log(`CCIPReceiver_Unsafe deployed to ${ccipReceiver.address}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

Deploy CCIPReceiver\_Unsafe smart contract by running:

```sh
npx hardhat run ./scripts/deployReceiver.ts --network ethereumSepolia
```

or for JavaScript:

```sh
npx hardhat run ./scripts/deployReceiver.js --network ethereumSepolia
```
{% endtab %}

{% tab title="Foundry" %}
### Option 1)

Deploy CCIPReceiver\_Unsafe smart contract by running:

```sh
forge create --rpc-url ethereumSepolia --private-key=$PRIVATE_KEY src/CCIPReceiver_Unsafe.sol:CCIPReceiver_Unsafe --constructor-args 0xD0daae2231E9CB96b94C8512223533293C3693Bf
```

### Option 2)

Create a new smart contract under the `script` folder and name it `CCIPReceiver_Unsafe.s.sol`&#x20;

Note that deployment of the `CCIPReceiver_Unsafe` smart contract is hard coded to Ethereum Sepolia for this example, but feel free to refactor the following deployment script to support other networks. You can check [CCIP Starter Kit (Foundry version)](https://github.com/smartcontractkit/ccip-starter-kit-foundry) for reference.

```solidity
// script/CCIPReceiver_Unsafe.s.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "forge-std/Script.sol";
import {CCIPReceiver_Unsafe} from "../src/CCIPReceiver_Unsafe.sol";

contract DeployCCIPReceiver_Unsafe is Script {
    function run() public {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        address sepoliaRouter = 0xD0daae2231E9CB96b94C8512223533293C3693Bf;

        CCIPReceiver_Unsafe receiver = new CCIPReceiver_Unsafe(
            sepoliaRouter
        );

        console.log(
            "CCIPReceiver_Unsafe deployed to ",
            address(receiver)
        );

        vm.stopBroadcast();
    }
}
```

Deploy CCIPReceiver\_Unsafe smart contract by running:

```sh
forge script ./script/CCIPReceiver_Unsafe.s.sol:DeployCCIPReceiver_Unsafe -vvv --broadcast --rpc-url ethereumSepolia
```
{% endtab %}

{% tab title="Remix" %}
Open your Metamask wallet and switch to the Ethereum Sepolia network.

Navigate to the "Solidity Compiler" tab and once again click the "Compile CCIPReceiver\_Unsafe.sol" button.

Navigate to the "Deploy & run transactions" tab and select the "Injected Provider - Metamask" option from the "Environment" dropdown menu.

Under the "Contract" dropdown menu, make sure that the "CCIPReceiver\_Unsafe - CCIPReceiver\_Unsafe.sol" is selected.

Locate the orange "Deploy" button. Provide `0xD0daae2231E9CB96b94C8512223533293C3693Bf` as the `router` address.&#x20;

Click the orange "Deploy"/"Transact" button.

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FABHI2gCMsKgAYfyqIzEO%2FdeployReceiverUnsafe.png?alt=media&#x26;token=1b0440fa-599f-471a-b60e-00896b2e9b47" alt=""><figcaption><p>Deploy CCIPReceiver_Unsafe.sol</p></figcaption></figure>

Metamask notification will pop up. Sign the transaction.
{% endtab %}
{% endtabs %}

### Deploy CCIP Sender to Avalanche Fuji

Follow the steps to deploy the `CCIPSender_Unsafe` smart contract to the Avalanche Fuji network.

{% tabs %}
{% tab title="Hardhat" %}
Create a new file under the `scripts` folder and name it `deploySender.ts` or `deploySender.js` depends on whether you work with TypeScript or JavaScript Hardhat projects.

Note that deployment of the `CCIPSender_Unsafe` smart contract is hard coded to Avalanche Fuji for this example, but feel free to refactor the following deployment script to support other networks or even make it fully customizable by rewriting it to Hardhat task with (optional) parameters. You can check [CCIP Starter Kit (Hardhat version](https://github.com/smartcontractkit/ccip-starter-kit-hardhat) for reference.

```typescript
// scripts/deploySender.ts

import { ethers, network, run } from "hardhat";

async function main() {
  if(network.name !== `avalancheFuji`) {
    console.error(`❌ Sender must be deployed to Avalanche Fuji`);
    return 1;
  }

  const fujiLinkAddress = `0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846`;
  const fujiRouterAddress = `0x554472a2720E5E7D5D3C817529aBA05EEd5F82D8`;
  
  await run("compile");

  const ccipSenderFactory = await ethers.getContractFactory("CCIPSender_Unsafe");
  const ccipSender = await ccipSenderFactory.deploy(fujiLinkAddress, fujiRouterAddress);

  await ccipSender.deployed();

  console.log(`CCIPSender_Unsafe deployed to ${ccipSender.address}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

Deploy CCIPSender\_Unsafe smart contract by running:

```sh
npx hardhat run ./scripts/deploySender.ts --network avalancheFuji
```

or for JavaScript:

```sh
npx hardhat run ./scripts/deploySender.js --network avalancheFuji
```
{% endtab %}

{% tab title="Foundry" %}
### Option 1)

Deploy CCIPSender\_Unsafe smart contract by running:

```sh
forge create --rpc-url avalancheFuji --private-key=$PRIVATE_KEY src/CCIPSender_Unsafe.sol:CCIPSender_Unsafe --constructor-args 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846 0x554472a2720E5E7D5D3C817529aBA05EEd5F82D8
```

### Option 2)

Create a new smart contract under the `script` folder and name it `CCIPSender_Unsafe.s.sol`&#x20;

Note that deployment of the `CCIPSender_Unsafe` smart contract is hard coded to Avalanche Fuji for this example, but feel free to refactor the following deployment script to support other networks. You can check [CCIP Starter Kit (Foundry version)](https://github.com/smartcontractkit/ccip-starter-kit-foundry) for reference.

```solidity
// script/CCIPSender_Unsafe.s.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "forge-std/Script.sol";
import {CCIPSender_Unsafe} from "../src/CCIPSender_Unsafe.sol";

contract DeployCCIPSender_Unsafe is Script {
    function run() public {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        address fujiLink = 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846;
        address fujiRouter = 0x554472a2720E5E7D5D3C817529aBA05EEd5F82D8;

        CCIPSender_Unsafe sender = new CCIPSender_Unsafe(
            fujiLink,
            fujiRouter
        );

        console.log(
            "CCIPSender_Unsafe deployed to ",
            address(sender)
        );

        vm.stopBroadcast();
    }
}
```

Deploy CCIPSender\_Unsafe smart contract by running:

```sh
forge script ./script/CCIPSender_Unsafe.s.sol:DeployCCIPSender_Unsafe -vvv --broadcast --rpc-url avalancheFuji
```
{% endtab %}

{% tab title="Remix" %}
Open your Metamask wallet and switch to the Avalanche Fuji network.

Open the CCIPSender\_Unsafer.sol file.

Navigate to the "Solidity Compiler" tab and click the "Compile CCIPSender\_Unsafe.sol" button.

Navigate to the "Deploy & run transactions" tab and select the "Injected Provider - Metamask" option from the "Environment" dropdown menu. Make sure that `chainId` is switched to 43113 (if not, you may need to refresh the Remix IDE page in your browser).

Under the "Contract" dropdown menu, make sure that the "CCIPSender\_Unsafe - CCIPSender\_Unsafe.sol" is selected.

Locate the orange "Deploy" button. Provide `0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846` as the `link` address and `0x554472a2720E5E7D5D3C817529aBA05EEd5F82D8` as the `router` address.&#x20;

Click the orange "Deploy"/"Transact" button.

Metamask notification will pop up. Sign the transaction.
{% endtab %}
{% endtabs %}

### Send your first CCIP Message

Follow the steps to send the CCIP Message from the `CCIPSender_Unsafe` smart contract on the Avalanche Fuji network to the `CCIPReceiver_Unsafe` smart contract on the Ethereum Sepolia network.

First of all, you will need to fund your `CCIPSender_Unsafe` smart contract with 1 LINK. To get it, navigate to the [https://faucets.chain.link/fuji](https://faucets.chain.link/fuji)&#x20;

{% embed url="https://faucets.chain.link/fuji" %}

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FunAM9EBvRRHZVDRNR07Q%2Ffaucet.png?alt=media&#x26;token=b89da94e-e517-4567-a553-ad08c07a6993" alt="" width="563"><figcaption><p>Chainlink Faucet</p></figcaption></figure>

Now fund the `CCIPSender_Unsafe` smart contract by sending 1 LINK from your wallet to it.

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2FFiFORTrR8W1X4nhtiJe9%2Ffund.png?alt=media&#x26;token=9086319b-7edd-43cd-b5a2-42b0b53f5671" alt="" width="351"><figcaption><p>Fund CCIPSender_Unsafe.sol with 1 LINK</p></figcaption></figure>

And finally, send your first CCIP Cross-Chain Message:

{% tabs %}
{% tab title="Hardhat" %}
Prepare:

* The address of the address of the `CCIPReceiver_Unsafe.sol` smart contract you previously deployed to Ethereum Sepolia, as the `receiver` parameter;
* The Text Message you want to send, for example "CCIP Masterclass", as the `someText` parameter;
* 16015286601757825753, which is the CCIP Chain Selector for the Ethereum Sepolia network, as the `destinationChainSelector` parameter.

Create a new JavaScript/TypeScript file under the `scripts` folder and name it `sendMessage.js`/`sendMessage.ts`

```typescript
// scripts/sendMessage.ts

import { ethers, network } from "hardhat";

async function main() {
  if(network.name !== `avalancheFuji`) {
    console.error(`❌ Must be called from Avalanche Fuji`);
    return 1;
  }

  const ccipSenderAddress = `PUT CCIP_SENDER_UNSAFE ADDRESS HERE`;
  const ccipReceiverAddress = `PUT CCIP_RECEIVER_UNSAFE ADDRESS HERE`;
  const someText = `CCIP Masterclass`;
  const destinationChainSelector = 16015286601757825753;

  const ccipSenderFactory = await ethers.getContractFactory("CCIPSender_Unsafe");
  const ccipSender = await ccipSenderFactory.connect(ccipSenderAddress, ethers.provider);

  const tx = await ccipSender.send(
      ccipReceiverAddress, 
      someText,
      desinationChainSelector
  );

  console.log(`Transaction hash: ${tx.hash}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

Send your first CCIP Message by running the following command:

```sh
npx hardhat run ./scripts/sendMessage.ts --network avalancheFuji
```

Or for JavaScript:

```sh
npx hardhat run ./scripts/sendMessage.js --network avalancheFuji
```
{% endtab %}

{% tab title="Foundry" %}
Prepare:

* The address of the address of the `CCIPReceiver_Unsafe.sol` smart contract you previously deployed to Ethereum Sepolia, as the `receiver` parameter;
* The Text Message you want to send, for example "CCIP Masterclass", as the `someText` parameter;
* 16015286601757825753, which is the CCIP Chain Selector for the Ethereum Sepolia network, as the `destinationChainSelector` parameter.

Run:

```sh
cast send <CCIP_SENDER_UNSAFE_ADDRESS> --rpc-url avalancheFuji --private-key=$PRIVATE_KEY "send(address,string,uint64)" <CCIP_RECEIVER_UNSAFE_ADDRESS> "CCIP Masterclass" 16015286601757825753
```
{% endtab %}

{% tab title="Remix" %}
Under the "Deployed Contracts" section, you should find the `CCIPSender_Unsafe.sol` contract you previously deployed to Avalanche Fuji. Find the `send` function and provide:

* The address of the address of the `CCIPReceiver_Unsafe.sol` smart contract you previously deployed to Ethereum Sepolia, as the `receiver` parameter;
* The Text Message you want to send, for example "CCIP Masterclass", as the `someText` parameter;
* 16015286601757825753, which is the CCIP Chain Selector for the Ethereum Sepolia network, as the `destinationChainSelector` parameter.

Hit the "Transact" orange button.
{% endtab %}
{% endtabs %}

You can now monitor live the status of your CCIP Cross-Chain Message via [CCIP Explorer](https://ccip.chain.link/). Just paste the transaction hash into the search bar and open the message details.

<figure><img src="https://2639739539-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEm7Dwh3rUCIyHWLvZwRo%2Fuploads%2Fdyd38qXTzvb9JveR0QVy%2Fexplorer.png?alt=media&#x26;token=b43d338b-3dbf-4e72-b944-30224a3d88b4" alt="" width="563"><figcaption><p>CCIP Explorer</p></figcaption></figure>

## Recap

To build using Chainlink CCIP, one needs to use the [@chainlink/contracts-ccip](https://www.npmjs.com/package/@chainlink/contracts-ccip) NPM package.

To send a CCIP Message, one needs to call the `ccipSend()` function from the `IRouterClient` interface, on the CCIP `Router.sol` smart contract by passing the `EVM2AnyMessage` struct from the `Client` library.

To receive a CCIP Message, one needs to implement the `IAny2EVMMessageReceiver` interface, which consists of the `ccipReceive()` function that needs to be overridden, which receives the `Any2EVMMessage` struct from the `Client` library as a function argument.&#x20;

The [@chainlink/contracts-ccip](https://www.npmjs.com/package/@chainlink/contracts-ccip) NPM package comes up with the already implemented receiver smart contract called `CCIPReceiver`.
