Cross-chain ERC-721

Deploying the ERC721 NFT Contract on the Sender Chain

Below is an example of a standard ERC721 contract. You can use the Remix - Ethereum IDE to deploy this contract on an EVM-compatible blockchain.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SampleERC721 is ERC721 ,Ownable{
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    string private _baseTokenURI;

    constructor(string memory name, string memory symbol) ERC721(name, symbol) {}

    function mint() public {
        _safeMint(msg.sender, _tokenIds.current());
        _tokenIds.increment();
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function setBaseURI(string calldata baseURI) external onlyOwner{
        _baseTokenURI = baseURI;
    }
}

Once the contract is deployed, you can mint an NFT and conduct a cross-chain transfer via the zkBridge official website. If this is the first time you are transferring this NFT, zkBridge will automatically create a mapping contract on the receiver chain. If you wish to deploy the mapping contract for the NFT on the receiver chain by yourself, please refer to the tutorial below.

Deploying the Mapping Contract on the Receiver Chain

For ERC721, you need to implement the IZKBridgeErc721 interface and grant minting and burning permissions to the NFT bridge contract on the receiver chain.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

interface IZKBridgeErc721 {

    function mint(address _to, uint256 _tokenId, string memory tokenURI_) external;

    function burn(uint256 _tokenId) external;

    function chainId() external view returns (uint16);

    function nativeContract() external view returns (bytes32);
}

contract ONFT is Ownable, IZKBridgeErc721, ERC721 {

    address public bridge;

    string private _baseTokenURI;

    uint16 _chainId;

    bytes32 _nativeContract;

    modifier onlyBridge() {
        require(msg.sender == bridge, "caller is not the bridge");
        _;
    }

    constructor(
        string memory _name,
        string memory _symbol,
        uint16 chainId_,
        bytes32 nativeContract_,
        address _bridge
    ) ERC721(_name, _symbol) {
        _chainId = chainId_;
        _nativeContract = nativeContract_;
        bridge = _bridge;
    }

    function mint(
        address _to,
        uint256 _tokenId,
        string memory tokenURI_
    ) external override onlyBridge {
        _mint(_to, _tokenId);
    }

    function burn(uint256 _tokenId) external override onlyBridge {
        require(_exists(_tokenId), "Burn of nonexistent token");
        _burn(_tokenId);
    }

    function chainId() external view returns (uint16) {
        return _chainId;
    }

    function nativeContract() external view returns (bytes32) {
        return _nativeContract;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function setBaseURI(string calldata baseURI) external onlyOwner{
        _baseTokenURI = baseURI;
    }
}

After deploying the contract on the receiver chain, please notify us and provide both the sender chain's and receiver chain's contract addresses, so we can add the mapping relationship for you.

To get in touch, you may either:

  1. Send an email to [email protected]

  2. Reach out to our community moderators on our Discord Server

Example:

Last updated