Cross-chain NFT minting: CCIP metadata URL transfer issue

I’m working on a project to mint NFTs across different blockchains using Chainlink’s CCIP. The tutorial I’m following puts the IPFS NFT Metadata URL directly in the destination chain’s MyNFT contract. But I want users to input the URL on the source chain and send it to the destination chain for minting.

I’ve tried to do this but it’s not working as expected. After sending the transaction from the source chain with the URL, the CCIP transaction goes through fine. However, I can’t find the minted NFT on the destination chain.

Here’s what my code looks like on the source chain:

function mint(
    uint64 _destChainId,
    address _to,
    address _tokenAddr,
    uint256 _tokenAmount,
    string memory _nftMetadata
) external onlyAllowedChain(_destChainId) returns (bytes32 msgId) {
    Client.EVMTokenAmount[] memory tokens = new Client.EVMTokenAmount[](1);
    tokens[0] = Client.EVMTokenAmount(_tokenAddr, _tokenAmount);
    
    Client.EVM2AnyMessage memory ccipMsg = Client.EVM2AnyMessage({
        receiver: abi.encode(_to),
        data: abi.encodeWithSignature("mint(address, string)", msg.sender, _nftMetadata),
        tokenAmounts: tokens,
        extraArgs: "",
        feeToken: address(linkToken)
    });
    
    uint256 ccipFee = router.getFee(_destChainId, ccipMsg);
    require(linkToken.balanceOf(address(this)) >= ccipFee, "Not enough LINK");
    
    linkToken.approve(address(router), ccipFee);
    IERC20(_tokenAddr).approve(address(router), _tokenAmount);
    
    msgId = router.ccipSend(_destChainId, ccipMsg);
    emit TokensSent(msgId, _destChainId, _to, _tokenAddr, _tokenAmount, ccipFee);
}

And here’s the mint function on the destination chain:

function mint(address to, string memory TOKEN_URI) public {
    _safeMint(to, tokenId);
    _setTokenURI(tokenId, TOKEN_URI);
    unchecked {
        tokenId++;
    }
}

What am I doing wrong? How can I fix this to properly transfer and use the metadata URL?

hey, i had a similar problem. check ur destination contract’s _ccipReceive function. it should decode the message and call mint correctly. also make sure ur using the right CCIP version and router addresses on both chains. those little things can mess everything up. good luck!

hey there! i’m curious about your project - it sounds pretty interesting! have you considered using events to track what’s happening during the minting process? that could help you figure out where things are going wrong.

maybe something like this on the destination chain:

event NFTMinted(address to, string tokenURI);

function mint(address to, string memory TOKEN_URI) public {
    _safeMint(to, tokenId);
    _setTokenURI(tokenId, TOKEN_URI);
    unchecked {
        tokenId++;
    }
    emit NFTMinted(to, TOKEN_URI);
}

this way, you could see if the mint function is actually being called and with what parameters. also, are you sure the CCIP message is actually reaching the destination chain? maybe there’s an issue with gas fees or something?

what tools are you using to debug this? have you tried etherscan or tenderly? they can be super helpful for cross-chain stuff.

oh, and one more thing - are you testing on testnets? sometimes mainnet and testnet configurations can be different, so that’s worth double-checking too.

let me know if any of this helps or if you figure it out! i’d love to hear more about your project :slight_smile:

I’ve encountered similar issues when working with cross-chain NFT minting. The problem likely lies in how you’re handling the CCIP message on the destination chain. Make sure you have a ‘_ccipReceive’ function that correctly unpacks and processes the incoming message.

In your destination contract, implement something like this:

function _ccipReceive(Client.Any2EVMMessage memory message) internal override {
    (address to, string memory tokenURI) = abi.decode(message.data, (address, string));
    mint(to, tokenURI);
}

This function will decode the incoming message and call your mint function with the correct parameters. Also, ensure your destination contract inherits from CCIPReceiver and has the necessary CCIP-related setup.

Lastly, double-check that your contracts on both chains are using compatible CCIP versions and that your router addresses are correct for each network. These small details can often cause unexpected behavior in cross-chain operations.