Note
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.
This project demonstrates how to mint an NFT on one blockchain from another blockchain using Chainlink CCIP.
Verify installation by typing:
node -v
and
npm -v
- Install packages
npm install
- Compile contracts
npx hardhat compile
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.
With Chainlink CCIP, one can:
- Transfer supported tokens
- Send messages (any data)
- Send messages and tokens
CCIP receiver can be:
- Smart contract that implements
CCIPReceiver.sol
- EOA
Note: If you send a message and token(s) to EOA, only tokens will arrive
To 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.
Imagine that you want to go to a conference that sells tickets as NFTs on one chain, but you have funds on some other chain. You will need to bridge your tokens to that chain where the NFT contract exists, to mint the NFT, and optionally bridge funds back.
We are building the cross-chain NFT minting system. This project aims 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.
The basic architecture diagram of what we want to accomplish looks like this:
flowchart LR
subgraph "Source Blockchain"
a("SourceMinter.sol") -- "`send abi.encodeWithSignature('mint(address)', msg.sender);`" --> b("Source Router")
end
b("Source Router") --> c("CCIP")
c("CCIP") --> d("Destination Router")
subgraph "Destination Blockchain"
d("Destination Router") -- "`receive abi.encodeWithSignature('mint(address)', msg.sender);`" --> e("DestinationMinter.sol")
e("DestinationMinter.sol") -- "`call mint(to)`" --> f("MyNFT.sol")
end
There are several Hardhat tasks available for deployment and interaction with this project. But before that, you need to set up some environment variables.
We are going to use the @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.
- Set a password for encrypting and decrypting the environment variable file. You can change it later by typing the same command.
npx env-enc set-pw
- Now set the following environment variables:
PRIVATE_KEY
, Source Blockchain RPC URL, Destination Blockchain RPC URL. You can see available options in the.env.example
file:
ETHEREUM_SEPOLIA_RPC_URL=""
OPTIMISM_GOERLI_RPC_URL=""
ARBITRUM_TESTNET_RPC_URL=""
AVALANCHE_FUJI_RPC_URL=""
POLYGON_MUMBAI_RPC_URL=""
To set these variables, type the following command and follow the instructions in the terminal:
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:
npx env-enc view
- Deploy the
MyNFT.sol
andDestinationMinter.sol
smart contracts to the destination blockchain, by running thedeploy-destination-minter
task:
npx hardhat deploy-destination-minter
--router <routerAddress> # Optional
For example, if you want to mint NFTs on avalancheFuji, run:
npx hardhat deploy-destination-minter --network avalancheFuji
- Deploy the
SourceMinter.sol
smart contract to the source blockchain, by running thedeploy-source-minter
task:
npx hardhat deploy-source-minter
--router <routerAddress> # Optional
--link <linkTokenAddress> # Optional
For example, if you want to mint NFTs on avalancheFuji by sending requests from ethereumSepolia, run:
npx hardhat deploy-source-minter --network ethereumSepolia
- Fund the
SourceMinter.sol
smart contract with tokens for CCIP fees.
-
If you want to pay for CCIP fees in Native tokens:
Open Metamask and fund your contract with Native tokens. For example, if you want to mint from Ethereum Sepolia to Avalanche Fuji, you can send 0.01 Sepolia ETH to the
SourceMinter.sol
smart contract.Or, you can execute the
fill-sender
task, by running:
npx hardhat fill-sender
--sender-address <sourceMinterAddress>
--blockchain <blockchain>
--amount <amountToSend>
--pay-fees-in <Native>
For example, if you want to fund it with 0.01 Sepolia ETH, run:
npx hardhat fill-sender --sender-address <SOURCE_MINTER_ADDRESS> --blockchain ethereumSepolia --amount 10000000000000000 --pay-fees-in Native
-
If you want to pay for CCIP fees in LINK tokens:
Open Metamask and fund your contract with LINK tokens. For example, if you want to mint from Ethereum Sepolia to Avalanche Fuji, you can send 0.001 Sepolia LINK to the
SourceMinter.sol
smart contract.Or, you can execute the
fill-sender
task, by running:
npx hardhat fill-sender
--sender-address <sourceMinterAddress>
--blockchain <blockchain>
--amount <amountToSend>
--pay-fees-in <LINK>
For example, if you want to fund it with 0.001 Sepolia LINK, run:
npx hardhat fill-sender --sender-address <SOURCE_MINTER_ADDRESS> --blockchain ethereumSepolia --amount 1000000000000000 --pay-fees-in LINK
- Mint NFTs by calling the
mint()
function of theSourceMinter.sol
smart contract on the source blockchain. It will send the CCIP Cross-Chain Message with the ABI-encoded mint function signature from theMyNFT.sol
smart contract. TheDestinationMinter.sol
smart contracts will receive the CCIP Cross-Chain Message with the ABI-encoded mint function signature as a payload and call theMyNFT.sol
smart contract using it. TheMyNFT.sol
smart contract will then mint the new NFT to themsg.sender
account from themint()
function of theSourceMinter.sol
smart contract, a.k.a to the account from which you will call the following command:
npx hardhat cross-chain-mint
--source-minter <sourceMinterAddress>
--source-blockchain <sourceBlockchain>
--destination-blockchain <destinationBlockchain>
--destination-minter <destinationMinterAddress>
--pay-fees-in <Native | LINK>
For example, if you want to mint NFTs on Avalanche Fuji by sending requests from Ethereum Sepolia, run:
npx hardhat cross-chain-mint --source-minter <SOURCE_MINTER_ADDRESS> --source-blockchain ethereumSepolia --destination-blockchain avalancheFuji --destination-minter <DESTNATION_MINTER_ADDRESS> --pay-fees-in Native
- Once the CCIP message is finalized on the destination blockchain, you can query the MyNFTs balance of your account, using the
balance-of
task:
npx hardhat balance-of
--my-nft <myNftContractAddress>
--blockchain <destinationBlockchain>
--owner <theAccountToCheckBalanceOf>
For example, to verify that the new MyNFT was minted, type:
npx hardhat balance-of --my-nft <MY_NFT_CONTRACT_ADDRESS> --blockchain avalancheFuji --owner <PUT_YOUR_EOA_ADDRESS_HERE>
Of course, you can see your newly minted NFT on popular NFT Marketplaces, like OpenSea for instance:
- You can always withdraw tokens for Chainlink CCIP fees from the
SourceMinter.sol
smart contract using thewithdraw
task. Note that the--token-address
flag is optional. If not provided, native coins will be withdrawn.
npx hardhat withdraw
--beneficiary <withdrawTo>
--blockchain <sourceMinterBlockchain>
--from <sourceMinterAddress>
--token-address <tokensToWithdraw> # Optional, if left empty native coins will be withdrawn
For example, to withdraw tokens previously sent for Chainlink CCIP fees, run:
npx hardhat withdraw --beneficiary <BENEFICIARY_ADDRESS> --blockchain ethereumSepolia --from <SOURCE_MINTER_ADDRESS>
or
npx hardhat withdraw --beneficiary <BENEFICIARY_ADDRESS> --blockchain ethereumSepolia --from <SOURCE_MINTER_ADDRESS> --token-address 0x779877A7B0D9E8603169DdbD7836e478b4624789
depending on whether you filled the SourceMinter.sol
contract with Native
or LINK
in step number 3.