{"id":19107337,"url":"https://github.com/smartcontractkit/ccip-starter-kit-foundry","last_synced_at":"2025-04-05T17:04:30.458Z","repository":{"id":182284097,"uuid":"623180190","full_name":"smartcontractkit/ccip-starter-kit-foundry","owner":"smartcontractkit","description":"This project demonstrates a couple of basic Chainlink CCIP use cases","archived":false,"fork":false,"pushed_at":"2025-02-04T07:52:17.000Z","size":1737,"stargazers_count":87,"open_issues_count":6,"forks_count":62,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-29T16:07:00.146Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smartcontractkit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-03T21:24:08.000Z","updated_at":"2025-03-14T21:40:14.000Z","dependencies_parsed_at":"2024-09-10T07:37:52.723Z","dependency_job_id":"86e1d4d6-61ea-4bc1-b02b-91f4f3a2343d","html_url":"https://github.com/smartcontractkit/ccip-starter-kit-foundry","commit_stats":null,"previous_names":["smartcontractkit/ccip-starter-kit-foundry"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fccip-starter-kit-foundry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fccip-starter-kit-foundry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fccip-starter-kit-foundry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fccip-starter-kit-foundry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smartcontractkit","download_url":"https://codeload.github.com/smartcontractkit/ccip-starter-kit-foundry/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369953,"owners_count":20927928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T04:12:11.710Z","updated_at":"2025-04-05T17:04:30.425Z","avatar_url":"https://github.com/smartcontractkit.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Chainlink CCIP Starter Kit\n\n\u003e **Note**\n\u003e\n\u003e _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._\n\nThis project demonstrates a couple of basic Chainlink CCIP use cases.\n\n## Table of Contents\n\n1. [Chainlink CCIP Starter Kit](#chainlink-ccip-starter-kit)\n   - [Note](#note)\n2. [Prerequisites](#prerequisites)\n3. [Getting Started](#getting-started)\n   - [Install packages](#install-packages)\n   - [Compile contracts](#compile-contracts)\n4. [What is Chainlink CCIP?](#what-is-chainlink-ccip)\n5. [Usage](#usage)\n   - [Set a password for encrypting and decrypting the environment variable file](#set-a-password-for-encrypting-and-decrypting-the-environment-variable-file)\n   - [Set environment variables](#set-environment-variables)\n   - [Validate your inputs](#validate-your-inputs)\n6. [Testing](#local-testing)\n   [Faucet](#faucet)\n\n7. [Production Best Practice](#production-best-practice)\n8. [Example 1 - Transfer CCIP Test Tokens from EOA to EOA](#example-1---transfer-tokens-from-eoa-to-eoa)\n9. [Example 2 - Transfer Tokens from EOA to Smart Contract](#example-2---transfer-tokens-from-eoa-to-smart-contract)\n10. [Example 3 - Transfer Token(s) from Smart Contract to any destination](#example-3---transfer-tokens-from-smart-contract-to-any-destination)\n11. [Example 4 - Send \u0026 Receive Tokens and Data](#example-4---send--receive-tokens-and-data)\n12. [Example 5 - Send \u0026 Receive Cross-Chain Messages and Pay with Native Coins](#example-5---send--receive-cross-chain-messages-and-pay-with-native-coins)\n13. [Example 6 - Send \u0026 Receive Cross-Chain Messages and Pay with LINK Tokens](#example-6---send--receive-cross-chain-messages-and-pay-with-link-tokens)\n14. [Example 7 - Execute Received Message as a Function Call](#example-7---execute-received-message-as-a-function-call)\n\n## Prerequisites\n\n- [Foundry](https://book.getfoundry.sh/getting-started/installation)\n\n## Getting Started\n\n1. Install packages\n\n```\nforge install\n```\n\nand\n\n```\nnpm install\n```\n\n2. Compile contracts\n\n```\nforge build\n```\n\n## What is Chainlink CCIP?\n\n**Chainlink Cross-Chain Interoperability Protocol (CCIP)** provides a single, simple, and elegant interface through which dApps and web3 entrepreneurs can securely meet all their cross-chain needs, including token transfers and arbitrary messaging.\n\n![basic-architecture](./img/basic-architecture.png)\n\nWith Chainlink CCIP, one can:\n\n- Transfer supported tokens\n- Send messages (any data)\n- Send messages and tokens\n\nCCIP receiver can be:\n\n- Smart contract that implements `CCIPReceiver.sol`\n- EOA\n\n**Note**: If you send a message and token(s) to EOA, only tokens will arrive\n\nTo use this project, you can consider CCIP as a \"black-box\" component and be aware of the Router contract only. If you want to dive deep into it, check the [Official Chainlink Documentation](https://docs.chain.link/ccip).\n\n## Usage\n\nIn the next section you can see a couple of basic Chainlink CCIP use case examples. But before that, you need to set up some environment variables.\n\nCreate a new file by copying the `.env.example` file, and name it `.env`. Fill in your wallet's PRIVATE_KEY, and RPC URLs for at least two blockchains\n\n```shell\nPRIVATE_KEY=\"\"\nETHEREUM_SEPOLIA_RPC_URL=\"\"\nARBITRUM_SEPOLIA_RPC_URL=\"\"\nAVALANCHE_FUJI_RPC_URL=\"\"\nPOLYGON_MUMBAI_RPC_URL=\"\"\nBNB_CHAIN_TESTNET_RPC_URL=\"\"\nWEMIX_TESTNET_RPC_URL=\"\"\nKROMA_SEPOLIA_TESTNET_RPC_URL=\"\"\nMETIS_SEPOLIA_RPC_URL=\"\"\nZKSYNC_SEPOLIA_RPC_URL=\"\"\nSCROLL_SEPOLIA_RPC_URL=\"\"\nZIRCUIT_SEPOLIA_RPC_URL=\"\"\nXLAYER_SEPOLIA_RPC_URL=\"\"\nPOLYGON_ZKEVM_SEPOLIA_RPC_URL=\"\"\nPOLKADOT_ASTAR_SHIBUYA_RPC_URL=\"\"\nMANTLE_SEPOLIA_RPC_URL=\"\"\nSONEIUM_MINATO_SEPOLIA_RPC_URL=\"\"\nBSQUARED_TESTNET_RPC_URL=\"\"\nBOB_SEPOLIA_RPC_URL=\"\"\nWORLDCHAIN_SEPOLIA_RPC_URL=\"\"\nSHIBARIUM_TESTNET_RPC_URL=\"\"\nBITLAYER_TESTNET_RPC_URL=\"\"\nFANTOM_SONIC_TESTNET_RPC_URL=\"\"\nCORN_TESTNET_RPC_URL=\"\"\nHASHKEY_SEPOLIA_RPC_URL=\"\"\nINK_SEPOLIA_RPC_URL=\"\"\n```\n\nOnce that is done, to load the variables in the `.env` file, run the following command:\n\n```shell\nsource .env\n```\n\nMake yourself familiar with the [`Helper.sol`](./script/Helper.sol) smart contract. It contains all the necessary Chainlink CCIP config. If you ever need to adjust any of those parameters, go to the Helper contract.\n\nThis contract also contains some enums, like `SupportedNetworks`:\n\n```solidity\nenum SupportedNetworks {\n    ETHEREUM_SEPOLIA, // 0\n    AVALANCHE_FUJI, // 1\n    ARBITRUM_SEPOLIA, // 2\n    POLYGON_MUMBAI, // 3\n    BNB_CHAIN_TESTNET, // 4\n    OPTIMISM_SEPOLIA, // 5\n    BASE_SEPOLIA, // 6\n    WEMIX_TESTNET, // 7\n    KROMA_SEPOLIA_TESTNET, // 8\n    METIS_SEPOLIA, // 9\n    ZKSYNC_SEPOLIA // 10\n    SCROLL_SEPOLIA, // 11\n    ZIRCUIT_SEPOLIA, // 12\n    XLAYER_SEPOLIA, // 13\n    POLYGON_ZKEVM_SEPOLIA, // 14\n    POLKADOT_ASTAR_SHIBUYA, // 15\n    MANTLE_SEPOLIA, // 16\n    SONEIUM_MINATO_SEPOLIA, // 17\n    BSQUARED_TESTNET, // 18\n    BOB_SEPOLIA, // 19\n    WORLDCHAIN_SEPOLIA,  // 20\n    SHIBARIUM_TESTNET, // 21\n    BITLAYER_TESTNET, // 22\n    FANTOM_SONIC_TESTNET, // 23\n    CORN_TESTNET, // 24\n    HASHKEY_SEPOLIA, // 25\n    INK_SEPOLIA // 26\n}\n```\n\nThis means that if you want to perform some action from `AVALANCHE_FUJI` blockchain to `ETHEREUM_SEPOLIA` blockchain, for example, you will need to pass `2 (uint8)` as a source blockchain flag and `0 (uint8)` as a destination blockchain flag.\n\nSimilarly, there is an `PayFeesIn` enum:\n\n```solidity\nenum PayFeesIn {\n    Native,  // 0\n    LINK     // 1\n}\n```\n\nSo, if you want to pay for Chainlink CCIP fees in LINK token, you will pass `1 (uint8)` as a function argument.\n\n## Local testing\n\nThe test files are located in the `test` folder.\n\n\u003e **Note**  \n\u003e Tests in the test folder have names like Example1.spec.ts, where `Example1` maps to the [#example-1](#example-1---transfer-tokens-from-eoa-to-eoa) scenario below.\n\nThere are two types of tests:\n\n- **Test with [CCIPLocalSimulator](https://github.com/smartcontractkit/chainlink-local/blob/main/src/ccip/CCIPLocalSimulator.sol)**: These tests are used to test the CCIP functionality in your local environment. They are located in the `test/no-fork` folder. To run these tests, run the following command:\n\n  ```shell\n  forge test --no-match-contract \".*ForkTest$\"\n  ```\n\n- **Test with [CCIPLocalSimulatorFork](https://github.com/smartcontractkit/chainlink-local/blob/main/src/ccip/CCIPLocalSimulatorFork.sol)**: These tests are used to test the CCIP functionality in a forked environment. They are located in the test/fork folder. To run these tests, run the following command:\n\n  ```shell\n  forge test --match-contract \".*ForkTest$\"\n  ```\n\n  **Note**: The fork tests send CCIP messages from Arbitrum Sepolia to Ethereum Sepolia, so make sure you have the _ETHEREUM_SEPOLIA_RPC_URL_ and _ARBITRUM_SEPOLIA_RPC_URL_ set in your .env file.\n\n## Faucet\n\nYou will need test tokens for some of the examples in this Starter Kit. Public faucets sometimes limit how many tokens a user can create and token pools might not have enough liquidity. To resolve these issues, CCIP supports two test tokens that you can mint permissionlessly so you don't run out of tokens while testing different scenarios.\n\nTo get 10\\*\\*18 units of each of these tokens, use the `script/Faucet.s.sol` smart contract. Keep in mind that the `CCIP-BnM` test token you can mint on all testnets, while `CCIP-LnM` you can mint only on Ethereum Sepolia. On other testnets, the `CCIP-LnM` token representation is a wrapped/synthetic asset called `clCCIP-LnM`.\n\n```solidity\nfunction run(SupportedNetworks network) external;\n```\n\nFor example, to mint 10\\*\\*18 units of both `CCIP-BnM` and `CCIP-LnM` test tokens on Ethereum Sepolia, run:\n\n```shell\nforge script ./script/Faucet.s.sol -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\nOr if you want to mint 10\\*\\*18 units of `CCIP-BnM` test token on Avalanche Fuji, run:\n\n```shell\nforge script ./script/Faucet.s.sol -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8)\" -- 1\n```\n\n## Production Best Practice\n\nMost of these examples are simplified for educational purposes. For production code, please adhere to the following best practices (Refer to the [Best Practices](https://docs.chain.link/ccip/best-practices) guide from the Official Chainlink Documentation for more information.):\n\n- **Do Not Hardcode `extraArgs`**: In these examples, `extraArgs` are hardcoded within contracts for simplicity. It is recommended to make `extraArgs` mutable. For instance, you can construct `extraArgs` off-chain and pass them into your function calls, or store them in a storage variable that can be updated as needed. This approach ensures that `extraArgs` remain backward compatible with future CCIP upgrades.\n- **Validate the Destination Chain**: Always ensure that the destination chain is valid and supported before sending messages.\n- **Understand `allowOutOfOrderExecution` Usage**: This parameter is available only on lanes where the **Out of Order Execution** property is set to **Optional** or **Required**. Refer to the [CCIP Directory](https://docs.chain.link/ccip/directory) to determine if your target lane supports this feature. For lanes where this parameter is absent, you must use `extraArgsV1` instead.\n\nTo help you with the off-chain encoding of `extraArgs`, we included the [`script/EncodeExtraArgsOffchain.s.sol`](script/EncodeExtraArgsOffchain.s.sol) helper script. To encode `extraArgs` as `EVMExtraArgsV1` run:\n\n```shell\nforge script ./script/EncodeExtraArgsOffchain.s.sol -vvv --sig \"encodeV1(uint256)\" -- \u003cGAS_LIMIT\u003e\n```\n\nTo encode `extraArgs` as `EVMExtraArgsV2` run:\n\n```shell\nforge script ./script/EncodeExtraArgsOffchain.s.sol -vvv --sig \"encodeV2(uint256,bool)\" -- \u003cGAS_LIMIT\u003e \u003cALLOW_OUT_OF_ORDER_EXECUTION\u003e\n```\n\n### Example 1 - Transfer Tokens from EOA to EOA\n\nTo transfer tokens from one EOA on one blockchain to another EOA on another blockchain you can use the `script/Example01.s.sol` smart contract:\n\n```solidity\nfunction run(\n    SupportedNetworks source,\n    SupportedNetworks destination,\n    address receiver,\n    address tokenToSend,\n    uint256 amount,\n    PayFeesIn payFeesIn\n) external returns (bytes32 messageId);\n```\n\nFor example, if you want to send 0.0000000000000001 CCIP-BnM from Avalanche Fuji to Ethereum Sepolia and to pay for CCIP fees in LINK, run:\n\n```shell\nforge script ./script/Example01.s.sol -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8,uint8,address,address,uint256,uint8)\" -- 1 0 \u003cRECEIVER_ADDRESS\u003e 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 100 1\n```\n\n### Example 2 - Transfer Tokens from EOA to Smart Contract\n\nTo transfer tokens from EOA from the source blockchain to the smart contract on the destination blockchain, follow the next steps:\n\n1. Deploy [`BasicMessageReceiver.sol`](./src/BasicMessageReceiver.sol) to the **destination blockchain**, using the `script/Example02.s.sol:DeployBasicMessageReceiver` smart contract:\n\n```solidity\nfunction run(SupportedNetworks destination) external;\n```\n\nFor example, to deploy it to Ethereum Sepolia, run:\n\n```shell\nforge script ./script/Example02.s.sol:DeployBasicMessageReceiver -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\n2. Transfer tokens, from the **source blockchain** to the deployed BasicMessageReceiver smart contract using the `script/Example02.s.sol:CCIPTokenTransfer` smart contract:\n\n```solidity\nfunction run(\n    SupportedNetworks source,\n    SupportedNetworks destination,\n    address basicMessageReceiver,\n    address tokenToSend,\n    uint256 amount,\n    PayFeesIn payFeesIn\n) external returns (bytes32 messageId);\n```\n\nFor example, if you want to send 0.0000000000000001 CCIP-BnM from Avalanche Fuji to Ethereum Sepolia and to pay for CCIP fees in native coin (Test AVAX), run:\n\n```shell\nforge script ./script/Example02.s.sol:CCIPTokenTransfer -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8,uint8,address,address,uint256,uint8)\" -- 1 0 \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 100 0\n```\n\n3. Once the CCIP message is finalized on the destination blockchain, you can see the details about the latest message using the `script/Example02.s.sol:GetLatestMessageDetails` smart contract:\n\n```solidity\nfunction run(address basicMessageReceiver) external view;\n```\n\nFor example,\n\n```shell\nforge script ./script/Example02.s.sol:GetLatestMessageDetails -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(address)\" -- \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e\n```\n\n4. Finally, you can always withdraw received tokens from the [`BasicMessageReceiver.sol`](./src/BasicMessageReceiver.sol) smart contract using the `cast send` command.\n\nFor example, to withdraw 100 units of CCIP-BnM previously sent, run:\n\n```shell\ncast send \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e --rpc-url ethereumSepolia --private-key=$PRIVATE_KEY \"withdrawToken(address,address)\" \u003cBENEFICIARY_ADDRESS\u003e 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05\n```\n\n### Example 3 - Transfer Token(s) from Smart Contract to any destination\n\nTo transfer a token or batch of tokens from a single, universal, smart contract to any address on the destination blockchain follow the next steps:\n\n1. Deploy [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) to the **source blockchain**, using the `script/Example03.s.sol:DeployBasicTokenSender` smart contract:\n\n```solidity\nfunction run(SupportedNetworks source) external;\n```\n\nFor example, if you want to send tokens from Avalanche Fuji to Ethereum Sepolia, run:\n\n```shell\nforge script ./script/Example03.s.sol:DeployBasicTokenSender -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8)\" -- 1\n```\n\n2. [OPTIONAL] If you want to send tokens to the smart contract, instead of EOA, you will need to deploy [`BasicMessageReceiver.sol`](./src/BasicMessageReceiver.sol) to the **destination blockchain**. For this purpose, you can reuse the `script/Example02.s.sol:DeployBasicMessageReceiver` smart contract from the previous example:\n\n```solidity\nfunction run(SupportedNetworks destination) external;\n```\n\nFor example, to deploy it to Ethereum Sepolia, run:\n\n```shell\nforge script ./script/Example02.s.sol:DeployBasicMessageReceiver -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\n3. Fill the [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) with tokens/coins for fees (you can always withdraw it later). You can do it manually from your wallet or by using the `cast send` command.\n\nFor example, if you want to pay for Chainlink CCIP Fees in LINK tokens, you can fill the [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) smart contract with 1 Avalanche Fuji LINK by running:\n\n```shell\ncast send 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846 \"transfer(address,uint256)\" \u003cBASIC_TOKEN_SENDER_ADDRESS\u003e 5000000000000000000 --rpc-url avalancheFuji --private-key=$PRIVATE_KEY\n```\n\nOr, if you want to pay for Chainlink CCIP Fees in Native coins, you can fill the [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) smart contract with 0.1 Avalanche Fuji AVAX by running:\n\n```shell\ncast send \u003cBASIC_TOKEN_SENDER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY --value 0.1ether\n```\n\n4. For each token you want to send, you will need to approve the [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) to spend it on your behalf, by using the `cast send` command.\n\nFor example, if you want to send 0.0000000000000001 CCIP-BnM using the [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) you will first need to approve that amount:\n\n```shell\ncast send 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 \"approve(address,uint256)\" \u003cBASIC_TOKEN_SENDER_ADDRESS\u003e 100 --rpc-url avalancheFuji --private-key=$PRIVATE_KEY\n```\n\n5. Finally, send tokens by providing the array of `Client.EVMTokenAmount {address token; uint256 amount;}` objects, using the `script/Example03.s.sol:SendBatch` smart contract:\n\n```solidity\nfunction run(\n    SupportedNetworks destination,\n    address payable basicTokenSenderAddres,\n    address receiver,\n    Client.EVMTokenAmount[] memory tokensToSendDetails,\n    BasicTokenSender.PayFeesIn payFeesIn\n) external;\n```\n\nFor example, to send CCIP-BnM token amounts you previously approved from Avalanche Fuji to Ethereum Sepolia, and pay for Chainlink CCIP fees in LINK tokens, run:\n\n```shell\nforge script ./script/Example03.s.sol:SendBatch -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8,address,address,(address,uint256)[],uint8)\" -- 0 \u003cBASIC_TOKEN_SENDER_ADDRESS\u003e \u003cRECEIVER\u003e \"[(0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4,100)]\" 1\n```\n\n6. Of course, you can always withdraw tokens you sent to the [`BasicTokenSender.sol`](./src/BasicTokenSender.sol) for fees, or from [`BasicMessageReceiver.sol`](./src/BasicMessageReceiver.sol) if you received them there.\n\nFor example, to withdraw ERC20 tokens, run:\n\n```shell\ncast send \u003cCONTRACT_WITH_FUNDS_ADDRESS\u003e --rpc-url \u003cRPC_ENDPOINT\u003e --private-key=$PRIVATE_KEY \"withdrawToken(address,address)\" \u003cBENEFICIARY_ADDRESS\u003e \u003cTOKEN_TO_WITHDRAW_ADDRESS\u003e\n```\n\nAnd to withdraw Native coins, run:\n\n```shell\ncast send \u003cCONTRACT_WITH_FUNDS_ADDRESS\u003e --rpc-url \u003cRPC_ENDPOINT\u003e --private-key=$PRIVATE_KEY \"withdraw(address)\" \u003cBENEFICIARY_ADDRESS\u003e\n```\n\n### Example 4 - Send \u0026 Receive Tokens and Data\n\nTo transfer tokens and data across multiple chains, follow the next steps:\n\n1. Deploy the [`ProgrammableTokenTransfers.sol`](./src/ProgrammableTokenTransfers.sol) smart contract to the **source blockchain**, using the `script/Example04.s.sol:DeployProgrammableTokenTransfers` smart contract:\n\n```solidity\nfunction run(SupportedNetworks network) external;\n```\n\nFor example, if you want to send a message from Avalanche Fuji to Ethereum Sepolia type:\n\n```shell\nforge script ./script/Example04.s.sol:DeployProgrammableTokenTransfers -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8)\" -- 1\n```\n\n2. Open Metamask and fund your contract with Native tokens. For example, if you want to send a message from Avalanche Fuji to Ethereum Sepolia, you can send 1 Fuji AVAX to your contract. You can also do the same thing using the `cast send` command:\n\n```shell\ncast send \u003cPROGRAMMABLE_TOKEN_TRANSFERS_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY --value 1ether\n```\n\n3. Open Metamask and fund your contract with LINK tokens. For example, if you want to send a message from Avalanche Fuji to Ethereum Sepolia, you can send a 1 Fuji BnM to your contract. You can also do the same thing using the `cast send` command:\n\n```shell\ncast send 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 \"transfer(address,uint256)\" \u003cPROGRAMMABLE_TOKEN_TRANSFERS_ADDRESS\u003e 1000000000000000000 --rpc-url avalancheFuji --private-key=$PRIVATE_KEY\n```\n\n4. Deploy the [`ProgrammableTokenTransfers.sol`](./src/ProgrammableTokenTransfers.sol) smart contract to the **destination blockchain**, using the `script/Example04.s.sol:DeployProgrammableTokenTransfers` smart contract, as you did in step number one.\n\nFor example, if you want to receive a message from Avalanche Fuji on Ethereum Sepolia type:\n\n```shell\nforge script ./script/Example04.s.sol:DeployProgrammableTokenTransfers -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\nAt this point, you have one **sender** contract on the source blockchain, and one **receiver** contract on the destination blockchain. Please note that [`ProgrammableTokenTransfers.sol`](./src/ProgrammableTokenTransfers.sol) can both send \u0026 receive tokens and data, hence we have two identical instances on both source and destination blockchains.\n\n5. Send a message, using the `script/Example04.s.sol:SendTokensAndData` smart contract:\n\n```solidity\nfunction run(\n    address payable sender,\n    SupportedNetworks destination,\n    address receiver,\n    string memory message,\n    address token,\n    uint256 amount\n) external;\n```\n\nFor example, if you want to send a \"Hello World\" message alongside 100 units of CCIP-BnM from Avalanche Fuji to Ethereum Sepolia, type:\n\n```shell\nforge script ./script/Example04.s.sol:SendTokensAndData -vvv --broadcast --rpc-url avalancheFuji --sig \"run(address,uint8,address,string,address,uint256)\" -- \u003cPROGRAMMABLE_TOKEN_TRANSFERS_ADDRESS_ON_SOURCE_BLOCKCHAIN\u003e 0 \u003cPROGRAMMABLE_TOKEN_TRANSFERS_ADDRESS_ON_DESTINATION_BLOCKCHAIN\u003e \"Hello World\" 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4 100\n```\n\n6. Once the CCIP message is finalized on the destination blockchain, you can see the details of the latest CCIP message received, by running the following command:\n\n```shell\ncast call \u003cPROGRAMMABLE_TOKEN_TRANSFERS_ADDRESS_ON_DESTINATION_BLOCKCHAIN\u003e \"getLastReceivedMessageDetails()\" --rpc-url ethereumSepolia\n```\n\n### Example 5 - Send \u0026 Receive Cross-Chain Messages and Pay with Native Coins\n\nTo send simple Text Cross-Chain Messages and pay for CCIP fees in Native Tokens, follow the next steps:\n\n1. Deploy the [`BasicMessageSender.sol`](./src/BasicMessageSender.sol) smart contract on the **source blockchain**, using the `script/Example05.s.sol:DeployBasicMessageSender` smart contract:\n\n```solidity\nfunction run(SupportedNetworks source) external;\n```\n\nFor example, if you want to send a simple cross-chain message from Avalanche Fuji, run:\n\n```shell\nforge script ./script/Example05.s.sol:DeployBasicMessageSender -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8)\" -- 1\n```\n\n2. Fund the [`BasicMessageSender.sol`](./src/BasicMessageSender.sol) smart contract with Native Coins, either manually using your wallet or by using the `cast send` command. For example, if you want to send 1 Fuji AVAX, run:\n\n```shell\ncast send \u003cBASIC_MESSAGE_SENDER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY --value 1ether\n```\n\n3. Deploy the [`BasicMessageReceiver.sol`](./src/BasicMessageReceiver.sol) smart contract to the **destination blockchain**. For this purpose, you can reuse the `script/Example02.s.sol:DeployBasicMessageReceiver` smart contract from the second example:\n\n```solidity\nfunction run(SupportedNetworks destination) external;\n```\n\nFor example, to deploy it to Ethereum Sepolia, run:\n\n```shell\nforge script ./script/Example02.s.sol:DeployBasicMessageReceiver -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\n4. Finally, send a cross-chain message using the `script/Example05.s.sol:SendMessage` smart contract:\n\n```solidity\nfunction run(\n    address payable sender,\n    SupportedNetworks destination,\n    address receiver,\n    string memory message,\n    BasicMessageSender.PayFeesIn payFeesIn\n) external;\n```\n\nFor example, if you want to send a \"Hello World\" message type:\n\n```shell\nforge script ./script/Example05.s.sol:SendMessage -vvv --broadcast --rpc-url avalancheFuji --sig \"ru\nn(address,uint8,address,string,uint8)\" -- \u003cBASIC_MESSAGE_SENDER_ADDRESS\u003e 0 \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e \"Hello World\"\n0\n```\n\n5. Once the CCIP message is finalized on the destination blockchain, you can see the details about the latest message using the `script/Example02.s.sol:GetLatestMessageDetails` smart contract:\n\n```solidity\nfunction run(address basicMessageReceiver) external view;\n```\n\nFor example,\n\n```shell\nforge script ./script/Example02.s.sol:GetLatestMessageDetails -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(address)\" -- \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e\n```\n\n6. You can always withdraw tokens for Chainlink CCIP fees from the [`BasicMessageSender.sol`](./src/BasicMessageSender.sol) smart contract using the `cast send` command:\n\n```shell\ncast send \u003cBASIC_MESSAGE_SENDER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY \"withdraw(address)\" \u003cBENEFICIARY_ADDRESS\u003e\n```\n\n### Example 6 - Send \u0026 Receive Cross-Chain Messages and Pay with LINK Tokens\n\nTo send simple Text Cross-Chain Messages and pay for CCIP fees in LINK Tokens, follow the next steps:\n\n1. Deploy the [`BasicMessageSender.sol`](./src/BasicMessageSender.sol) smart contract on the **source blockchain**, using the `script/Example05.s.sol:DeployBasicMessageSender` smart contract:\n\n```solidity\nfunction run(SupportedNetworks source) external;\n```\n\nFor example, if you want to send a simple cross-chain message from Avalanche Fuji, run:\n\n```shell\nforge script ./script/Example05.s.sol:DeployBasicMessageSender -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8)\" -- 1\n```\n\n2. Fund the [`BasicMessageSender.sol`](./src/BasicMessageSender.sol) smart contract with Testnet LINKs, either manually using your wallet or by using the `cast send` command. For example, if you want to send 1 Fuji LINK, run:\n\n```shell\ncast send 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846 \"transfer(address,uint256)\" \u003cBASIC_MESSAGE_SENDER_ADDRESS\u003e 1000000000000000000 --rpc-url avalancheFuji --private-key=$PRIVATE_KEY\n```\n\n3. Deploy the [`BasicMessageReceiver.sol`](./src/BasicMessageReceiver.sol) smart contract to the **destination blockchain**. For this purpose, you can reuse the `script/Example02.s.sol:DeployBasicMessageReceiver` smart contract from the second example:\n\n```solidity\nfunction run(SupportedNetworks destination) external;\n```\n\nFor example, to deploy it to Ethereum Sepolia, run:\n\n```shell\nforge script ./script/Example02.s.sol:DeployBasicMessageReceiver -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\n4. Finally, send a cross-chain message using the `script/Example05.s.sol:SendMessage` smart contract:\n\n```solidity\nfunction run(\n    address payable sender,\n    SupportedNetworks destination,\n    address receiver,\n    string memory message,\n    BasicMessageSender.PayFeesIn payFeesIn\n) external;\n```\n\nFor example, if you want to send a \"Hello World\" message type:\n\n```shell\nforge script ./script/Example05.s.sol:SendMessage -vvv --broadcast --rpc-url avalancheFuji --sig \"ru\nn(address,uint8,address,string,uint8)\" -- \u003cBASIC_MESSAGE_SENDER_ADDRESS\u003e 0 \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e \"Hello World\"\n1\n```\n\n5. Once the CCIP message is finalized on the destination blockchain, you can see the details about the latest message using the `script/Example02.s.sol:GetLatestMessageDetails` smart contract:\n\n```solidity\nfunction run(address basicMessageReceiver) external view;\n```\n\nFor example,\n\n```shell\nforge script ./script/Example02.s.sol:GetLatestMessageDetails -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(address)\" -- \u003cBASIC_MESSAGE_RECEIVER_ADDRESS\u003e\n```\n\n6. You can always withdraw tokens for Chainlink CCIP fees from the [`BasicMessageSender.sol`](./src/BasicMessageSender.sol) smart contract using the `cast send` command:\n\n```shell\ncast send \u003cBASIC_MESSAGE_SENDER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY \"withdrawToken(address,address)\" \u003cBENEFICIARY_ADDRESS\u003e 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846\n```\n\n### Example 7 - Execute Received Message as a Function Call\n\nOur goal for this example is to mint an NFT on the destination blockchain by sending the `to` address from the source blockchain. It is extremely simple so we can understand the basic concepts, but you can expand it to accept payment for minting on the source blockchain, add extra features, etc.\n\nThe basic architecture diagram of what we want to accomplish looks like this:\n\n```mermaid\nflowchart LR\nsubgraph \"Source Blockchain\"\na(\"SourceMinter.sol\") -- \"`send abi.encodeWithSignature('mint(address)', msg.sender);`\" --\u003e b(\"Source Router\")\nend\n\nb(\"Source Router\") --\u003e c(\"CCIP\")\n\nc(\"CCIP\") --\u003e d(\"Destination Router\")\n\nsubgraph \"Destination Blockchain\"\nd(\"Destination Router\") -- \"`receive abi.encodeWithSignature('mint(address)', msg.sender);`\" --\u003e e(\"DestinationMinter.sol\")\ne(\"DestinationMinter.sol\") -- \"`call mint(to)`\" --\u003e f(\"MyNFT.sol\")\nend\n```\n\n1. Deploy the [`MyNFT.sol`](./src/cross-chain-nft-minter/MyNFT.sol) and [`DestinationMinter.sol`](./src/cross-chain-nft-minter/DestinationMinter.sol) smart contracts from the `./src/cross-chain-nft-minter` folder on the **destination blockchain**, by using the `script/CrossChainNFT.s.sol:DeployDestination` smart contract:\n\n```solidity\nfunction run(SupportedNetworks destination) external;\n```\n\nFor example, if you want to have an NFT collection on Ethereum Sepolia, run:\n\n```shell\nforge script ./script/CrossChainNFT.s.sol:DeployDestination -vvv --broadcast --rpc-url ethereumSepolia --sig \"run(uint8)\" -- 0\n```\n\n2. Deploy the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract on the **source blockchain**, by using the `script/CrossChainNFT.s.sol:DeploySource` smart contract:\n\n```solidity\nfunction run(SupportedNetworks source) external;\n```\n\nFor example, if you want to mint NFTs on Ethereum Sepolia from Avalanche Fuji, run:\n\n```shell\nforge script ./script/CrossChainNFT.s.sol:DeploySource -vvv --broadcast --rpc-url avalancheFuji --sig \"run(uint8)\" -- 1\n```\n\n3. Fund the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract with tokens for CCIP fees.\n\n- If you want to pay for CCIP fees in Native tokens:\n\n  Open Metamask and fund your contract with Native tokens. For example, if you want to mint from Avalanche Fuji to Ethereum Sepolia, you can send 1 Fuji AVAX to the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract.\n\n  Or, you can use the `cast send` command:\n\n  ```shell\n  cast send \u003cSOURCE_MINTER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY --value 1ether\n  ```\n\n- If you want to pay for CCIP fees in LINK tokens:\n\n  Open Metamask and fund your contract with LINK tokens. For example, if you want to mint from Avalanche Fuji to Ethereum Sepolia, you can send 1 Fuji LINK to the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract.\n\n  Or, you can use the `cast send` command:\n\n  ```shell\n  cast send 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846 \"transfer(address,uint256)\" \u003cSOURCE_MINTER_ADDRESS\u003e 1000000000000000000 --rpc-url avalancheFuji --private-key=$PRIVATE_KEY\n  ```\n\n4. Mint NFTs by calling the `mint()` function of the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract on the **source blockchain**. It will send the CCIP Cross-Chain Message with the ABI-encoded mint function signature from the [`MyNFT.sol`](./src/cross-chain-nft-minter/MyNFT.sol) smart contract. The [`DestinationMinter.sol`](./src/cross-chain-nft-minter/DestinationMinter.sol) smart contracts will receive the CCIP Cross-Chain Message with the ABI-encoded mint function signature as a payload and call the [`MyNFT.sol`](./src/cross-chain-nft-minter/MyNFT.sol) smart contract using it. The [`MyNFT.sol`](./src/cross-chain-nft-minter/MyNFT.sol) smart contract will then mint the new NFT to the `msg.sender` account from the `mint()` function of the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract, a.k.a to the account from which you will call the following command:\n\n```solidity\nfunction run(\n    address payable sourceMinterAddress,\n    SupportedNetworks destination,\n    address destinationMinterAddress,\n    SourceMinter.PayFeesIn payFeesIn\n) external;\n```\n\nFor example, if you want to mint NFTs on Ethereum Sepolia by sending requests from Avalanche Fuji, run:\n\n```shell\nforge script ./script/CrossChainNFT.s.sol:Mint -vvv --broadcast --rpc-url avalancheFuji --sig \"run(a\nddress,uint8,address,uint8)\" -- \u003cSOURCE_MINTER_ADDRESS\u003e 0 \u003cDESTINATION_MINTER_ADDRESS\u003e 0\n```\n\n5. Once the CCIP message is finalized on the destination blockchain, you can query the MyNFTs balance of your account, using the `cast call` command.\n\n![ccip-explorer](./img/ccip-explorer.png)\n\nFor example, to verify that the new MyNFT was minted, type:\n\n```shell\ncast call \u003cMY_NFT_ADDRESS\u003e \"balanceOf(address)\" \u003cPUT_YOUR_ADDRESS_HERE\u003e --rpc-url ethereumSepolia\n```\n\nOf course, you can see your newly minted NFT on popular NFT Marketplaces, like OpenSea for instance:\n\n![opensea](./img/opensea.png)\n\n6. You can always withdraw tokens for Chainlink CCIP fees from the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) smart contract using the `cast send` command.\n\nFor example, to withdraw tokens previously sent for Chainlink CCIP fees, run:\n\n```shell\ncast send \u003cSOURCE_MINTER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY \"withdraw(address)\" \u003cBENEFICIARY_ADDRESS\u003e\n```\n\nor\n\n```shell\ncast send \u003cSOURCE_MINTER_ADDRESS\u003e --rpc-url avalancheFuji --private-key=$PRIVATE_KEY \"withdrawToken(address,address)\" \u003cBENEFICIARY_ADDRESS\u003e 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846\n```\n\ndepending on whether you filled the [`SourceMinter.sol`](./src/cross-chain-nft-minter/SourceMinter.sol) contract with `Native (0)` or `LINK (1)` in step number 3.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmartcontractkit%2Fccip-starter-kit-foundry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmartcontractkit%2Fccip-starter-kit-foundry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmartcontractkit%2Fccip-starter-kit-foundry/lists"}