♈
Bootcamp-2024
  • Intro
  • 1. Blockchain & Wallet Fundamentals
    • Blockchain Introduction
    • State Machines
    • Cryptography
    • Distributed Networks
    • Game Theory
    • What is Web3
    • MetaMask Wallet Installation
    • Transferring Tokens with MetaMask
  • 2. Smart Contract & Solidity Fundamentals
    • Using Remix
    • Create, compile and publish your first smart contract
    • Interact with already published smart contracts
    • Blockchain Explorer
    • Verify source code on Etherscan
  • 3. Oracles, ERC20 & Chainlink Data Feeds
    • Oracles
    • Create & Deploy ERC20
    • Data Feeds
  • 4. Cross-Chain Tokens With Chainlink CCIP
    • Setting up MetaMask
    • Getting USDC Testnet Tokens
    • Create Smart Contract In Remix
    • Compile and Deploy
    • Approve USDC
    • Send LINK to your Contract
    • Send USDC from Fuji to Sepolia
    • USDC on Sepolia
  • 5. Mentoring Session
  • 6. NFTs & Chainlink Automation
    • NFT Basics
    • Dynamic NFTs
    • Creating an NFT Smart Contract
    • Deploying Your Dynamic NFTs
  • 7. Chainlink CCIP & Cross-Chain NFT dApps
    • Create and deploy CCIP NFT Contracts
    • Mint on Source Chain
    • Fund Contract
    • Mint On Sepolia From Fuji
    • Mint from Destination 2 - Base Sepolia
  • 8. Random Numbers with Chainlink VRF
    • Introduction to Chainlink VRF
    • Hands On Game Using VRF
  • 9. Off-Chain Data with Chainlink Functions
    • Chainlink Functions Playground
    • Setting up MetaMask
    • Remix
    • Functions Subscription
    • Creating The Functions Consumer Contract
    • Sending a Request from Remix
    • City Weather and Examples
    • City Weather on Chainlink Functions
  • 10. Connecting the 🌏 with Chainlink
  • Glossary
Powered by GitBook
On this page
  • We are going to start this workshop on Remix
  • CrossChainPriceNFT.sol
  • We will now check this collection on Opensea.
  1. 7. Chainlink CCIP & Cross-Chain NFT dApps

Create and deploy CCIP NFT Contracts

We will now create and deploy our CCIP Contracts.

Previous7. Chainlink CCIP & Cross-Chain NFT dAppsNextMint on Source Chain

Last updated 1 year ago


We are going to start this workshop on Remix

In Remix, switch to the DEPLOY & RUN TRANSACTIONS view. You will be very familiar with this by now!


Make sure you are on the fifth icon on the lefthand side of remix

  • it should be titled DEPLOY & RUN TRANSACTIONS

Make sure you have selected “Injected provider - Metamask” and that the network is (11155111) which is Ethereum Sepolia. If not make sure to switch your network in your Metamask extension.

CrossChainPriceNFT.sol

In the Remix File Explorer, create a new file. Name it CrossChainPriceNFT.sol.

I am providing the code for you here, there were some slight changes in this block of code from the source code used during the Recorded Workshop. Polygon's Mumbai Testnet was deprecated so I took the liberty of updating this code to exclude all references to Mumbai and instead replaced it with parameters that point towards Base Sepolia test network

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

// Deploy this contract on Sepolia

// Importing OpenZeppelin contracts
import "@openzeppelin/contracts@4.6.0/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts@4.6.0/utils/Counters.sol";
import "@openzeppelin/contracts@4.6.0/utils/Base64.sol";

// Importing Chainlink contracts
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract CrossChainPriceNFT is ERC721, ERC721URIStorage {
    using Counters for Counters.Counter;
    using Strings for uint256;

    Counters.Counter public tokenIdCounter;

    // Create price feed
    AggregatorV3Interface internal priceFeed;
    uint256 public lastPrice = 0;

    string priceIndicatorUp = unicode"😀";
    string priceIndicatorDown = unicode"😔";
    string priceIndicatorFlat = unicode"😑";
    string public priceIndicator;

    struct ChainStruct {
        uint64 code;
        string name;
        string color;
    }
    mapping (uint256 => ChainStruct) chain;

    //https://docs.chain.link/ccip/supported-networks/testnet
    constructor() ERC721("CrossChain Price", "CCPrice") {
        chain[0] = ChainStruct ({
            code: 16015286601757825753,
            name: "Sepolia",
            color: "#0000ff" //blue
        });
        chain[1] = ChainStruct ({
            code: 14767482510784806043,
            name: "Fuji",
            color: "#ff0000" //red
        });
        chain[2] = ChainStruct ({
            code: 10344971235874465080,
            name: "Base Sepolia",
            color: "#4b006e" //purple
        });

        // https://docs.chain.link/data-feeds/price-feeds/addresses        
        priceFeed = AggregatorV3Interface(
            // Sepolia BTC/USD
            0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43            
        );

        // Mint an NFT
        mint(msg.sender);
    }

    function mint(address to) public {
        // Mint from Sepolia network = chain[0]
        mintFrom(to, 0);
    }

    function mintFrom(address to, uint256 sourceId) public {
        // sourceId 0 Sepolia, 1 Fuji, 2 Base Sepolia
        uint256 tokenId = tokenIdCounter.current();
        _safeMint(to, tokenId);
        updateMetaData(tokenId, sourceId);    
        tokenIdCounter.increment();
    }

    // Update MetaData
    function updateMetaData(uint256 tokenId, uint256 sourceId) public {
        // Create the SVG string
        string memory finalSVG = buildSVG(sourceId);
           
        // Base64 encode the SVG
        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "Cross-chain Price SVG",',
                        '"description": "SVG NFTs in different chains",',
                        '"image": "data:image/svg+xml;base64,',
                        Base64.encode(bytes(finalSVG)), '",',
                        '"attributes": [',
                            '{"trait_type": "source",',
                            '"value": "', chain[sourceId].name ,'"},',
                            '{"trait_type": "price",',
                            '"value": "', lastPrice.toString() ,'"}',
                        ']}'
                    )
                )
            )
        );
        // Create token URI
        string memory finalTokenURI = string(
            abi.encodePacked("data:application/json;base64,", json)
        );
        // Set token URI
        _setTokenURI(tokenId, finalTokenURI);
    }

    // Build the SVG string
    function buildSVG(uint256 sourceId) internal returns (string memory) {

        // Create SVG rectangle with random color
        string memory headSVG = string(
            abi.encodePacked(
                "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' xmlns:xlink='http://www.w3.org/1999/xlink' xmlns:svgjs='http://svgjs.com/svgjs' width='500' height='500' preserveAspectRatio='none' viewBox='0 0 500 500'> <rect width='100%' height='100%' fill='",
                chain[sourceId].color,
                "' />"
            )
        );
        // Update emoji based on price
        string memory bodySVG = string(
            abi.encodePacked(
                "<text x='50%' y='50%' font-size='128' dominant-baseline='middle' text-anchor='middle'>",
                comparePrice(),
                "</text>"
            )
        );
        // Close SVG
        string memory tailSVG = "</svg>";

        // Concatenate SVG strings
        string memory _finalSVG = string(
            abi.encodePacked(headSVG, bodySVG, tailSVG)
        );
        return _finalSVG;
    }

    // Compare new price to previous price
    function comparePrice() public returns (string memory) {
        uint256 currentPrice = getChainlinkDataFeedLatestAnswer();
        if (currentPrice > lastPrice) {
            priceIndicator = priceIndicatorUp;
        } else if (currentPrice < lastPrice) {
            priceIndicator = priceIndicatorDown;
        } else {
            priceIndicator = priceIndicatorFlat;
        }
        lastPrice = currentPrice;
        return priceIndicator;
    }

    function getChainlinkDataFeedLatestAnswer() public view returns (uint256) {
        (, int256 price, , , ) = priceFeed.latestRoundData();
        return uint256(price);
    }

    function tokenURI(uint256 tokenId)
        public view override(ERC721, ERC721URIStorage) returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    // The following function is an override required by Solidity.
    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage)
    {
        super._burn(tokenId);
    }
}

We will now deploy the code. Switch to Remix's DEPLOY & RUN TRANSACTIONS tab, select the CrossChainPriceNFT file and click the Deploy button.


We will now check this collection on Opensea.

Copy your CrossChainPriceNFT contract address.

  • Save this link to your collection so you can check on the status of your NFT's


Use this link to navigate the testnet version of Opensea. In the search bar you will search your CrossChainPriceNFT.sol address that you just deployed.

testnet.opensea.io
Remix - Ethereum IDE
REMIX IDE
OpenSea, the largest NFT marketplaceOpenSea
Testnet Opensea
Logo
Logo
Example showing Connection to Injected Provider - Metamask & Connection to the Sepolia Network
Example of DEPLOY & RUN TRANSACTIONS TAB with CrossChainPriceNFT.sol Selected
Confirm the Transaction
Example of copying the CrossChainPriceNFT address
What your collection should look like