Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nex-t1.ai/llms.txt

Use this file to discover all available pages before exploring further.

Deploy an NFT

Learn how to create, deploy, and mint NFTs on Nexis Appchain using ERC721 standards.

Simple NFT Contract

contracts/SimpleNFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

contract SimpleNFT is ERC721, ERC721URIStorage, Ownable {
    uint256 private _tokenIdCounter;

    constructor() ERC721("My NFT", "MNFT") Ownable(msg.sender) {}

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

Advanced NFT with Features

contracts/AdvancedNFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract AdvancedNFT is
    ERC721,
    ERC721URIStorage,
    ERC721Burnable,
    ERC721Royalty,
    AccessControl
{
    using Counters for Counters.Counter;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    Counters.Counter private _tokenIdCounter;

    uint256 public constant MAX_SUPPLY = 10000;
    uint256 public mintPrice = 0.01 ether;
    bool public publicMintEnabled = false;

    event Minted(address indexed to, uint256 indexed tokenId);

    constructor() ERC721("Advanced NFT", "ANFT") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        
        // Set default royalty: 5% to creator
        _setDefaultRoyalty(msg.sender, 500); // 500 = 5%
    }

    function mint(address to, string memory uri) public payable {
        require(publicMintEnabled || hasRole(MINTER_ROLE, msg.sender), "Minting disabled");
        require(_tokenIdCounter.current() < MAX_SUPPLY, "Max supply reached");
        
        if (!hasRole(MINTER_ROLE, msg.sender)) {
            require(msg.value >= mintPrice, "Insufficient payment");
        }

        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
        
        emit Minted(to, tokenId);
    }

    function setMintPrice(uint256 _price) external onlyRole(DEFAULT_ADMIN_ROLE) {
        mintPrice = _price;
    }

    function togglePublicMint() external onlyRole(DEFAULT_ADMIN_ROLE) {
        publicMintEnabled = !publicMintEnabled;
    }

    function setRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        _setTokenRoyalty(tokenId, receiver, feeNumerator);
    }

    function withdraw() external onlyRole(DEFAULT_ADMIN_ROLE) {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

    function totalSupply() public view returns (uint256) {
        return _tokenIdCounter.current();
    }

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

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721URIStorage, ERC721Royalty, AccessControl)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721, ERC721URIStorage, ERC721Royalty)
    {
        super._burn(tokenId);
    }
}

Metadata Structure

metadata.json
{
  "name": "NFT #1",
  "description": "An amazing NFT on Nexis Appchain",
  "image": "ipfs://QmXxxx.../image.png",
  "attributes": [
    {
      "trait_type": "Background",
      "value": "Blue"
    },
    {
      "trait_type": "Rarity",
      "value": "Legendary"
    }
  ]
}

Deploy and Mint

scripts/deploy-nft.js
const hre = require("hardhat");
const { ethers } = require("hardhat");

async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying NFT with:", deployer.address);

  // Deploy NFT
  const NFT = await ethers.getContractFactory("AdvancedNFT");
  const nft = await NFT.deploy();
  await nft.waitForDeployment();

  const address = await nft.getAddress();
  console.log("NFT deployed:", address);

  // Mint first NFT
  const tokenURI = "ipfs://QmXxxx.../metadata.json";
  const mintTx = await nft.mint(deployer.address, tokenURI);
  await mintTx.wait();

  console.log("Minted NFT #0 to:", deployer.address);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Upload to IPFS

scripts/upload-to-ipfs.js
const { create } = require('ipfs-http-client');
const fs = require('fs');

const ipfs = create({ url: 'https://ipfs.infura.io:5001/api/v0' });

async function uploadToIPFS() {
  // Upload image
  const image = fs.readFileSync('./assets/image.png');
  const imageResult = await ipfs.add(image);
  console.log('Image CID:', imageResult.path);

  // Upload metadata
  const metadata = {
    name: "My NFT #1",
    description: "A cool NFT",
    image: `ipfs://${imageResult.path}`,
    attributes: [
      { trait_type: "Rarity", value: "Rare" }
    ]
  };

  const metadataResult = await ipfs.add(JSON.stringify(metadata));
  console.log('Metadata CID:', metadataResult.path);
  console.log('Full URI:', `ipfs://${metadataResult.path}`);

  return `ipfs://${metadataResult.path}`;
}

uploadToIPFS().catch(console.error);

Interact with NFT

const nft = new ethers.Contract(nftAddress, nftABI, signer);

// Mint NFT
const tx = await nft.mint(recipientAddress, "ipfs://Qm.../metadata.json");
await tx.wait();

// Get token URI
const uri = await nft.tokenURI(0);
console.log("Token URI:", uri);

// Transfer NFT
const transferTx = await nft.transferFrom(fromAddress, toAddress, tokenId);
await transferTx.wait();

// Check royalty info
const salePrice = ethers.parseEther("1");
const [receiver, royaltyAmount] = await nft.royaltyInfo(tokenId, salePrice);
console.log("Royalty:", ethers.formatEther(royaltyAmount), "to", receiver);

Next Steps

Deploy NFT Collection

Create a full NFT collection

Build NFT Marketplace

Build marketplace for your NFTs