Go to Remix and select the CrossChainPriceNFT on the deployed contracts section in the lower lefthand side of the screen.
You will want to make sure you use the Mint Function, and you want to provide your Wallet address as the parameter as you want to mint the NFTs to your Ethereum Sepolia wallet address.
Confirm the transaction in your Metamask Wallet.
Once you Confirm your transaction, open testnet.opensea.io or use the link to your collection that we saved in the last section to see your freshly minted NFT in the Collection
CrossDestinationMinter.sol
On Remix open FILE EXPLORER
Create a new file and name it CrossDestinationMinter.sol
Paste this code there.
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;// Deploy this contract on Sepoliaimport {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";interface InftMinter {functionmintFrom(address account,uint256 sourceId) external;}/** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */contractCrossDestinationMinterisCCIPReceiver { InftMinter public nft;eventMintCallSuccessfull();// https://docs.chain.link/ccip/supported-networks/testnetaddress routerSepolia =0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59;constructor(address nftAddress) CCIPReceiver(routerSepolia) { nft =InftMinter(nftAddress); }function_ccipReceive(Client.Any2EVMMessagememory message ) internaloverride { (bool success, ) =address(nft).call(message.data);require(success);emitMintCallSuccessfull(); }functiontestMint() external {// Mint from Sepolia nft.mintFrom(msg.sender,0); }functiontestMessage() external {// Mint from Sepoliabytesmemory message; message = abi.encodeWithSignature("mintFrom(address,uint256)", msg.sender,0); (bool success, ) =address(nft).call(message);require(success);emitMintCallSuccessfull(); }functionupdateNFT(address nftAddress) external { nft =InftMinter(nftAddress); }}
Deploy this code to Ethereum Sepolia. The parameter is the address of your deployed CrossChainPriceNFT.sol Contract , on Ethereum Sepolia.
We will then test it by executing testMint and testMessage on newly deployed contract CrossDestinationMinter.sol:
Click one of these functions, confirm the transaction, and when that's done click the other and confirm its transaction in Metamask.
You can check to confirm that they worked by refreshing your Opensea Collection
CrossSourceMinter.sol
We Need to switch our network to Avalanche Fuji. You can do the following by going into your Metamask and selecting "Networks", then selecting the Avalanche Fuji Network. If you dont already have the Avalanche Fuji Network Imported in your Metamask extension you can find it by going to:
going into your metamask extension and selecting networks,
then selecting the Avalanche Fuji Network.
If you dont already have the Avalanche Fuji Network Imported in your Metamask extension you can find it by visiting chainlist.org
Once you have switched networks, return to Remix. In FILE EXPLORER create a new file.
Enter the Second Icon Titled - FILE EXPLORER and create a new file.
We will be naming this file CrossSourceMinter.sol
And paste the following code in there:
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;// Deploy this contract on Fujiimport {IRouterClient} from"@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";import {Client} from"@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";import {LinkTokenInterface} from"@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";/** * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. */contract CrossSourceMinter {// Custom errors to provide more descriptive revert messages. error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees); // Used to make sure contract has enough balance to cover the fees.
errorNothingToWithdraw(); // Used when trying to withdraw but there's nothing to withdraw. IRouterClient public router; LinkTokenInterface public linkToken;uint64public destinationChainSelector;addresspublic owner;addresspublic destinationMinter;eventMessageSent(bytes32 messageId);constructor(address destMinterAddress) { owner = msg.sender;// https://docs.chain.link/ccip/supported-networks/testnet// from Fujiaddress routerAddressFuji =0xF694E193200268f9a4868e4Aa017A0118C9a8177; router =IRouterClient(routerAddressFuji); linkToken =LinkTokenInterface(0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846); linkToken.approve(routerAddressFuji, type(uint256).max);// to Sepolia destinationChainSelector =16015286601757825753; destinationMinter = destMinterAddress; }functionmintOnSepolia() external {// Mint from Fuji network = chain[1] Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ receiver: abi.encode(destinationMinter), data: abi.encodeWithSignature("mintFrom(address,uint256)", msg.sender,1), tokenAmounts:new Client.EVMTokenAmount[](0), extraArgs: Client._argsToBytes( Client.EVMExtraArgsV1({gasLimit:980_000}) ), feeToken:address(linkToken) }); // Get the fee required to send the messageuint256 fees = router.getFee(destinationChainSelector, message);if (fees > linkToken.balanceOf(address(this)))revertNotEnoughBalance(linkToken.balanceOf(address(this)), fees);bytes32 messageId;// Send the message through the router and store the returned message ID messageId = router.ccipSend(destinationChainSelector, message);emitMessageSent(messageId); }modifieronlyOwner() {require(msg.sender == owner); _; }functionlinkBalance (address account) publicviewreturns (uint256) {return linkToken.balanceOf(account); }functionwithdrawLINK(address beneficiary ) publiconlyOwner {uint256 amount = linkToken.balanceOf(address(this));if (amount ==0) revertNothingToWithdraw(); linkToken.transfer(beneficiary, amount); }}
Copy this code and paste it directly into your newly created CrossSourceMinter.sol file in Remix
Next, we deploy this smart contract. Its constructor function takes the following parameter: address of our deployed CrossDestinationMinter.sol(deployed on Sepolia).
(this is the contract that we deployed on Sepolia)
If you've gotten this far congratulations! We're almost there!