Skip to content

CCIP offers a standardized framework for creating secure dApps across blockchains, while ICM utilizes the native interoperability of Avalanche 9000. This repository combines both technologies to facilitate cross-chain transactions that start on an EVM, are processed on C-Chain, and are received on an L1.

License

Notifications You must be signed in to change notification settings

smartcontractkit/ccip-icm

Repository files navigation

runme
id version
01JAHNYDRKXDD9PSPBQ3BKWFAX
v3

This repository represents an example of using a Chainlink product or service. It is provided to help you understand how to interact with Chainlink’s systems so that you can integrate them into your own. This template is provided "AS IS" without warranties of any kind, has not been audited, and may be missing key checks or error handling to make the usage of the product more clear. Take everything in this repository as an example and not something to be copy pasted into a production ready service.

ccip-avalanche

Open in Gitpod

Demonstrating how to use Chainlink Cross-Chain Interoperobility Protocol (CCIP) and Avalanche Interchain Messaging (ICM) to send a message from Ethereum Sepolia to Avalanche Fuji (via CCIP), then forwarding that message from Avalanche Fuji to Dispatch L1 (via ICM).

Getting Started

In the next section you can see how to send data from one chain to another. But before that, you need to set up some environment variables, install dependencies, setup environment variables, and compile contracts.

1. Install Dependencies

yarn && make

2. Setup Environment

Run the command below, then update the .env PRIVATE_KEY and ETHERSCAN_API_KEY variables.

if [ -f .env ]; then
    echo "We will use the .env your have already created."
    else
    if [ -z "${DOTENV}" ]; then
        echo "Creating and setting .env"
        cp .env.example .env && source .env
        echo "Set your PRIVATE_KEY and ETHERSCAN_API_KEY in .env"
    fi
fi

3. Create Wallet

To create a new wallet that is stored in a keystore, issue the following command, which will prompt you to secure the private key with a password.

# Grabs the PRIVATE_KEY from the .env file.
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)

if [ -f keystore/secret ]; then
    echo "Found keystore in workspace"
    else
    if [ -z "${DOTENV}" ]; then
        echo "Creating and setting keystore"
        mkdir keystore
        cast wallet import --private-key $PRIVATE_KEY -k keystore secret
        echo "keystore/secret created"
    fi
fi

For ease use of the keystore we already configured a environment variable called KEYSTORE pointing to the keystore file in the working directory.

You can use the wallet stored in the keystore by adding the --keystore flag instead of the --private-key flag. Run the command below to confirm your wallet address is stored accurately.

KEYSTORE=$(grep KEYSTORE .env | cut -d '=' -f2)

cast wallet address --keystore $KEYSTORE

4. Prepare Smart Contracts

Smart Contract Design

messaging-process

Before we proceed with deployment, it is best practice to run tests, which can be executed as follows:

forge test --match-contract SenderTest -vv
forge test --match-contract BrokerTest -vv
forge test --match-contract ReceiverTest -vv

In order to interact with our contracts, we first need to deploy them, which is simplified in the script/Deploy.s.sol smart contract, so let's deploy each contract applying the deployment script for each of the following commands.

forge script ./script/Deploy.s.sol:DeploySender -vvv --broadcast --rpc-url ethereumSepolia

Update MESSAGE_SENDER_ADDRESS stored in your .env, then make sure to verify the deployment by running the following command:

export MESSAGE_SENDER_ADDRESS=$(grep MESSAGE_SENDER_ADDRESS .env | cut -d '=' -f2)

forge verify-contract $MESSAGE_SENDER_ADDRESS src/MessageSender.sol:MessageSender \
--rpc-url 'https://eth-sepolia.public.blastapi.io' \
--verifier blockscout \
--verifier-url 'https://eth-sepolia.blockscout.com/api/'

echo "Verified MessageSender contract may be found here: https://eth-sepolia.blockscout.com/address/$MESSAGE_SENDER_ADDRESS?tab=contract"

Finally, since the MessageSender requires funds to pay for fees, we will load up the contract programatically, as follows with 0.05 ETH:

KEYSTORE=$(grep KEYSTORE .env | cut -d '=' -f2)

cast send $MESSAGE_SENDER_ADDRESS --rpc-url ethereumSepolia --value 0.05ether --keystore $KEYSTORE
forge script ./script/Deploy.s.sol:DeployBroker -vvv --broadcast --rpc-url avalancheFuji

Update MESSAGE_BROKER_ADDRESS stored in your .env, then make sure to verify the deployment by running the following command:

export MESSAGE_BROKER_ADDRESS=$(grep MESSAGE_BROKER_ADDRESS .env | cut -d '=' -f2)

forge verify-contract $MESSAGE_BROKER_ADDRESS src/MessageBroker.sol:MessageBroker \
--rpc-url 'https://api.avax-test.network/ext/bc/C/rpc' \
--verifier-url 'https://api.routescan.io/v2/network/testnet/evm/43113/etherscan' \
--etherscan-api-key "verifyContract"

echo "Verified MessageBroker contract may be found here: https://testnet.snowtrace.io/address/$MESSAGE_BROKER_ADDRESS/contract/43113/code"
forge script ./script/Deploy.s.sol:DeployReceiver -vvv --broadcast --rpc-url dispatchTestnet

Update MESSAGE_RECEIVER_ADDRESS stored in your .env, then make sure to verify the deployment by running the following command:

export MESSAGE_RECEIVER_ADDRESS=$(grep MESSAGE_RECEIVER_ADDRESS .env | cut -d '=' -f2)

forge verify-contract $MESSAGE_RECEIVER_ADDRESS src/MessageReceiver.sol:MessageReceiver --etherscan-api-key 'verifyContract' \
&& orge verify-contract $MESSAGE_RECEIVER_ADDRESS src/MessageReceiver.sol:MessageReceiver \
--rpc-url 'https://subnets.avax.network/dispatch/testnet/rpc' \
--verifier-url 'https://api.routescan.io/v2/network/testnet/evm/779672/etherscan' \

echo "Verified MessageReceiver contract may be found here: https://779672.testnet.snowtrace.io/address/$MESSAGE_RECEIVER_ADDRESS/contract/779672/code"

Messaging Cross-Chain

Before proceeding, please ensure you have completed the steps outlined in the Setup Messaging Scenario section above.

1. Ethereum Sepolia → Avalanche Fuji

Sending Message (Sepolia → Fuji)

Run the following to send a message to Fuji from Sepolia via the SendMessage functionality coded in Send.s.sol:

export CUSTOM_MESSAGE=""
if [ -z "${CUSTOM_MESSAGE}" ]; then
    echo "No custom message provided"
    else
    echo "Sending \`$CUSTOM_MESSAGE\` to Avalanche"
fi

forge script ./script/Send.s.sol:SendMessage -vvv --broadcast --rpc-url ethereumSepolia --sig "run(string)" -- "$CUSTOM_MESSAGE"

# https://ccip.chain.link/#/side-drawer/msg/0x30917345c2214ca9a26631f24f30e67b0f7d3aef2285c4ec108a124d944886f1

2. Avalanche Fuji → Dispatch Testnet

Brokering Message (Fuji → Dispatch)

Once the message is finalized on the broker chain (Fuji), you may see the details about the latest message via the BrokerMessage functionality coded in Broker.s.sol. After you have confirmed the latest message you received looks good, you may proceed with running the following script to broker the message to Dispatch:

MESSAGE_BROKER_ADDRESS=$(grep MESSAGE_BROKER_ADDRESS .env | cut -d '=' -f2)
MESSAGE_RECEIVER_ADDRESS=$(grep MESSAGE_RECEIVER_ADDRESS .env | cut -d '=' -f2)
KEYSTORE=$(grep KEYSTORE .env | cut -d '=' -f2)

cast send $MESSAGE_BROKER_ADDRESS --rpc-url avalancheFuji --keystore $KEYSTORE "brokerMessage(address)" $MESSAGE_RECEIVER_ADDRESS

3. Dispatch Testnet

Receiving Message (Dispatch)

After running the script above to broker the message from Fuji to Dispatch, you may confirm the message was received by running the following script:

forge script ./script/Receive.s.sol:ReceiveMessage -vvv --broadcast --rpc-url dispatchTestnet

About

CCIP offers a standardized framework for creating secure dApps across blockchains, while ICM utilizes the native interoperability of Avalanche 9000. This repository combines both technologies to facilitate cross-chain transactions that start on an EVM, are processed on C-Chain, and are received on an L1.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published