♈
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
  • Connecting MetaMask to Remix
  • Creating your Smart Contract
  1. 4. Cross-Chain Tokens With Chainlink CCIP

Create Smart Contract In Remix

Ethereum's Online IDE

PreviousGetting USDC Testnet TokensNextCompile and Deploy

Last updated 1 year ago

Connecting MetaMask to Remix

Open up in the browser and go to the "DEPLOY & RUN TRANSACTIONS" icon on left side:

In the “ENVIRONMENT” drop-down menu, select “Injected Provider - MetaMask”:

Make sure you are on Custom (43113) network, which is Avalanche Fuji Testnet:

If you are not connect to different network, go into MetaMask and connect to the Avalanche Fuji Testnet


Creating your Smart Contract

Create a smart contract called TransferUSDCBasic.sol. First, go to the 1. “FILE EXPLORER” icon and then click the 2. “Create new file” icon:

Name the new Solidity file TransferUSDCBasic.sol and a new tab will open in Remix:

  • Copy and paste the code into the new TransferUSDCBasic.sol file

  • Or copy and paste this following code:

TransferUSDCBasic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

// Deploy this contract on Fuji

import {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 {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol";
import {SafeERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/utils/SafeERC20.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 TransferUSDCBasic {
    using SafeERC20 for IERC20;

    error NotEnoughBalanceForFees(uint256 currentBalance, uint256 calculatedFees);
    error NotEnoughBalanceUsdcForTransfer(uint256 currentBalance);
    error NothingToWithdraw();

    address public owner;
    IRouterClient private immutable ccipRouter;
    IERC20 private immutable linkToken;
    IERC20 private immutable usdcToken;

    // https://docs.chain.link/ccip/supported-networks/v1_2_0/testnet#avalanche-fuji
    address ccipRouterAddress = 0xF694E193200268f9a4868e4Aa017A0118C9a8177;

    // https://docs.chain.link/resources/link-token-contracts#fuji-testnet
    address linkAddress = 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846;

    // https://developers.circle.com/stablecoins/docs/usdc-on-test-networks
    address usdcAddress = 0x5425890298aed601595a70AB815c96711a31Bc65;

    // https://docs.chain.link/ccip/supported-networks/v1_2_0/testnet#ethereum-sepolia
    uint64 destinationChainSelector = 16015286601757825753;

    event UsdcTransferred(
        bytes32 messageId,
        uint64 destinationChainSelector,
        address receiver,
        uint256 amount,
        uint256 ccipFee
    );

    constructor() {
        owner = msg.sender;
        ccipRouter = IRouterClient(ccipRouterAddress);
        linkToken = IERC20(linkAddress);
        usdcToken = IERC20(usdcAddress);
    }

    function transferUsdcToSepolia(
        address _receiver,
        uint256 _amount
    )
        external
        returns (bytes32 messageId)
    {
        Client.EVMTokenAmount[]
            memory tokenAmounts = new Client.EVMTokenAmount[](1);
        Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
            token: address(usdcToken),
            amount: _amount
        });
        tokenAmounts[0] = tokenAmount;

        Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
            receiver: abi.encode(_receiver),
            data: "",
            tokenAmounts: tokenAmounts,
            extraArgs: Client._argsToBytes(
                Client.EVMExtraArgsV1({gasLimit: 0})
            ),
            feeToken: address(linkToken)
        });

        uint256 ccipFee = ccipRouter.getFee(
            destinationChainSelector,
            message
        );

        if (ccipFee > linkToken.balanceOf(address(this)))
            revert NotEnoughBalanceForFees(linkToken.balanceOf(address(this)), ccipFee);
        linkToken.approve(address(ccipRouter), ccipFee);

        if (_amount > usdcToken.balanceOf(msg.sender))
            revert NotEnoughBalanceUsdcForTransfer(usdcToken.balanceOf(msg.sender));
        usdcToken.safeTransferFrom(msg.sender, address(this), _amount);
        usdcToken.approve(address(ccipRouter), _amount);

        // Send CCIP Message
        messageId = ccipRouter.ccipSend(destinationChainSelector, message);

        emit UsdcTransferred(
            messageId,
            destinationChainSelector,
            _receiver,
            _amount,
            ccipFee
        );
    }

    function allowanceUsdc() public view returns (uint256 usdcAmount) {
        usdcAmount = usdcToken.allowance(msg.sender, address(this));
    }

    function balancesOf(address account) public view returns (uint256 linkBalance, uint256 usdcBalance) {
        linkBalance =  linkToken.balanceOf(account);
        usdcBalance = IERC20(usdcToken).balanceOf(account);
    }

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function withdrawToken(
        address _beneficiary,
        address _token
    ) public onlyOwner {
        uint256 amount = IERC20(_token).balanceOf(address(this));
        if (amount == 0) revert NothingToWithdraw();
        IERC20(_token).transfer(_beneficiary, amount);
    }
}

Code is available here:

GitHub
Remix
LogoRemix - Ethereum IDE
DEPLOY & RUN TRANSACTIONS ICON
Select Injected Provider - MetaMask
Connected to (43113) - Avalanche Fuji Testnet
Create a new Solidity file
Your new blank Solidity file