Mint from Destination 2 - Base Sepolia

For this part of the workshop we will be minting on a new destination. In the Recorded Workshop, the destination Chain was Polygon Mumbai, However, Mumbai has been deprecated so we will be changing it

We will demonstrate how you can send messages & mint tokens across multiple chains using Chainlink CCIP

We will demonstrate how you can send messages & mint tokens across multiple chains using Chainlink CCIP

Workshop Source Code

We will be using Base Sepolia instead, since Polygon Mumbai was deprecated in April 2024.

Follow this link to switch your Metamask wallet to the Base Sepolia network - Make sure the Chain ID is 84532

Use the Faucet to receive some LINK and ETH on the Base Sepolia Network

Make sure you add the LINK token to your wallet by importing it as per the above links. You can either do that by copying the Link token address and manually importing it, or by clicking the button provided.


Now that we have our important prerequisites taken care of, lets dive into the code. Make a new file in Remix, CrossSourceMinterBaseSepolia.sol and copy the following in there.

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

// Deploy this contract on Base Sepolia

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 {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 CrossSourceMinterBaseSepolia {

    // 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.
    error NothingToWithdraw(); // Used when trying to withdraw but there's nothing to withdraw.

    IRouterClient public router;
    LinkTokenInterface public linkToken;
    uint64 public destinationChainSelector;
    address public owner;
    address public destinationMinter;

    event MessageSent(bytes32 messageId);

    constructor(address destMinterAddress) {
        owner = msg.sender;

        // https://docs.chain.link/ccip/supported-networks/testnet

        // from Base Sepolia
        address routerAddressBaseSepolia = 0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93;
        router = IRouterClient(routerAddressBaseSepolia);
        linkToken = LinkTokenInterface(0xE4aB69C077896252FAFBD49EFD26B5D171A32410);
        linkToken.approve(routerAddressBaseSepolia, type(uint256).max);

        // to Sepolia
        destinationChainSelector = 16015286601757825753;
        destinationMinter = destMinterAddress;
    }

    function mintOnEthSepolia() external {
        // Mint from Base Sepolia network = chain[2]
        Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
            receiver: abi.encode(destinationMinter),
            data: abi.encodeWithSignature("mintFrom(address,uint256)", msg.sender, 2),
            tokenAmounts: new Client.EVMTokenAmount[](0),
            extraArgs: Client._argsToBytes(
                Client.EVMExtraArgsV1({gasLimit: 980_000})
            ),
            feeToken: address(linkToken)
        });        

        // Get the fee required to send the message
        uint256 fees = router.getFee(destinationChainSelector, message);

        if (fees > linkToken.balanceOf(address(this)))
            revert NotEnoughBalance(linkToken.balanceOf(address(this)), fees);

        bytes32 messageId;
        // Send the message through the router and store the returned message ID
        messageId = router.ccipSend(destinationChainSelector, message);
        emit MessageSent(messageId);
    }

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

    function linkBalance (address account) public view returns (uint256) {
        return linkToken.balanceOf(account);
    }

    function withdrawLINK(
        address beneficiary
    ) public onlyOwner {
        uint256 amount = linkToken.balanceOf(address(this));
        if (amount == 0) revert NothingToWithdraw();
        linkToken.transfer(beneficiary, amount);
    }
}

Copy this code into your new CrossSourceMinterBase.sol File

Go to the Deployment tab and include your CrossDestinationMinter.sol's address as the parameter (this is the same contract we deployed on Ethereum Sepolia)

Now send the newly created CrossSourceMinterBaseSepolia contract 5 LINK so it can pay for operations.

Once LINK is in the contract you should confirm that it is there by clicking the linkBalance button and pass the CrossSourceMinterBaseSepolia address as the input parameter.

Have patience, this transaction may take some time as it works towards finality on the Source & Destination Chain.

If done correctly the outcome should look something like this

CONGRATULATIONS 🥳

Celebrate by tweeting about #chainlink and share your screenshots!

Last updated