NFT代码和注意点

ERC721

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721URIStorage.sol)

pragma solidity ^0.8.20;

import {ERC721} from "../ERC721.sol";
import {Strings} from "../../../utils/Strings.sol";
import {IERC4906} from "../../../interfaces/IERC4906.sol";
import {IERC165} from "../../../interfaces/IERC165.sol";

/**
 * @dev ERC721 token with storage based token URI management.
 */
abstract contract ERC721URIStorage is IERC4906, ERC721 {
    using Strings for uint256;

    // Interface ID as defined in ERC-4906. This does not correspond to a traditional interface ID as ERC-4906 only
    // defines events and does not include any external function.
    bytes4 private constant ERC4906_INTERFACE_ID = bytes4(0x49064906);

    // Optional mapping for token URIs
    mapping(uint256 tokenId => string) private _tokenURIs;

    /**
     * @dev See {IERC165-supportsInterface}
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC165) returns (bool) {
        return interfaceId == ERC4906_INTERFACE_ID || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireOwned(tokenId);

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via string.concat).
        if (bytes(_tokenURI).length > 0) {
            return string.concat(base, _tokenURI);
        }

        return super.tokenURI(tokenId);
    }

    /**
     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
     *
     * Emits {MetadataUpdate}.
     */
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        _tokenURIs[tokenId] = _tokenURI;
        emit MetadataUpdate(tokenId);
    }
}

NFT基础+计数器

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract GameItem is ERC721URIStorage {
    // 引入计数器库中的函数
    using Counters for Counters.Counter;
    // 通过计数器生成私有的计数器变量,用于生成唯一的标识符
    Counters.Counter private _tokenIds;

    constructor() ERC721("GameItem", "USD") {}

    function awardItem(address player, string memory tokenURI)
        public 
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

//使用Solidity5.9版本以下编译的智能合约才能部署在Xuperchain链上

pragma solidity ^0.5.0;

import "../.deps/node_modules/@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "../.deps/node_modules/@openzeppelin/contracts/drafts/Counters.sol";
import "../.deps/node_modules/@openzeppelin/contracts/ownership/Ownable.sol";

contract MyNFT is ERC721Full, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor()
        public  
        ERC721Full("MyToken", "MTK")
    {}

    function safeMint(string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        _mint(msg.sender, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _transferFrom(address from, address to, uint256 tokenId)
        internal
    {
        _transferFrom(from, to, tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        returns (string memory)
    {
        return tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        returns (bool)
    {
        return supportsInterface(interfaceId);
    }
}

NFT基础合约

pragma solidity ^0.5.0;

import "../.deps/node_modules/@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "../.deps/node_modules/@openzeppelin/contracts/drafts/Counters.sol";
import "../.deps/node_modules/@openzeppelin/contracts/ownership/Ownable.sol";

contract MyNFT is ERC721Full, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    uint256 public MAX_AMOUNT = 3;
    // 定义一个白名单 可以调用premint
    mapping(address => bool) public whiteList;
    // 定义一个变量 用于开放或者关闭提前铸造窗口
    bool public preMintWindow = false;
    bool public mintWindow = false;

    constructor()
        public  
        ERC721Full("Dogs", "DGS")
    {}

    // 为提前铸造NFT的提供内部优惠
    function preMint(string memory uri) public payable {
        require(preMintWindow, "Premint is not open yet!");
        require(msg.value == 0.001 ether, "The price of dog nft is 0.005 ether.");
        require(whiteList[msg.sender], "You are not in the whiteList.");
        require(totalSupply() < MAX_AMOUNT, "Dog NFT is sold out!");
        require(balanceOf(msg.sender) < 1, "Max amount of NFT minted by an adress is 1");
        // 设置tokenid
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        // 铸造nft
        _mint(msg.sender, tokenId);
        // 设置该nft对应的tokenid对应的uri
        _setTokenURI(tokenId, uri);
    }

    // NFT铸造代码
    function safeMint(string memory uri) public payable onlyOwner {
        require(mintWindow, "Mint is not open yet!");
        require(msg.value == 0.005 ether, "The price of dog nft is 0.005 ether.");
        require(totalSupply() < MAX_AMOUNT, "Dog NFT is sold out!");
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        _mint(msg.sender, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // 添加白名单 白名单的成员可以参与premint
    function addToWhiteList(address[] memory addrs) public onlyOwner {
        for (uint256 i = 0; i < addrs.length; i++) {
            whiteList[addrs[i]] = true;
        }
    }

    // 设置铸造窗口是否被打开
    function setMintAndPreMintWindow(bool _preMint, bool _mint) public onlyOwner {
        preMintWindow = _preMint;
        mintWindow = _mint;
    }

    function _transferFrom(address from, address to, uint256 tokenId)
        internal
    {
        _transferFrom(from, to, tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        returns (string memory)
    {
        return tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        returns (bool)
    {
        return supportsInterface(interfaceId);
    }
}

NFT的Metadata

pragma solidity ^0.5.0;

import "../.deps/node_modules/@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "../.deps/node_modules/@openzeppelin/contracts/drafts/Counters.sol";
import "../.deps/node_modules/@openzeppelin/contracts/ownership/Ownable.sol";

contract MyNFT is ERC721Full, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    uint256 public MAX_AMOUNT = 3;
    // 定义一个白名单 可以调用premint
    mapping(address => bool) public whiteList;
    // 定义一个变量 用于开放或者关闭提前铸造窗口
    bool public preMintWindow = false;
    bool public mintWindow = false;

    event LogInt(uint message);
    event LogString(string message);

    // METADATA of NFT
    string constant METADATA_SHIBAINU = "ipfs://QmXw7TEAJWKjKifvLE25Z9yjvowWk2NWY3WgnZPUto9XoA";
    string constant METADATA_HUSKY = "ipfs://QmTFXZBmmnSANGRGhRVoahTTVPJyGaWum8D3YicJQmG97m";
    string constant METADATA_BULLDOG = "ipfs://QmSM5h4WseQWATNhFWeCbqCTAGJCZc11Sa1P5gaXk38ybT";
    string constant METADATA_SHEPHERD = "ipfs://QmRGryH7a1SLyTccZdnatjFpKeaydJcpvQKeQTzQgEp9eK";

    constructor()
        public  
        ERC721Full("Dogs", "DGS")
    {}

    // 为提前铸造NFT的提供内部优惠
    function preMint() public payable {
        require(preMintWindow, "Premint is not open yet!");
        require(msg.value == 0.001 ether, "The price of dog nft is 0.005 ether.");
        require(whiteList[msg.sender], "You are not in the whiteList.");
        require(totalSupply() < MAX_AMOUNT, "Dog NFT is sold out!");
        require(balanceOf(msg.sender) < 1, "Max amount of NFT minted by an adress is 1");
        // 设置tokenid
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        // 铸造nft
        _mint(msg.sender, tokenId);
        // 设置该nft对应的tokenid对应的uri
        uint256 randomNumber = random(4);
        if (randomNumber == 0) {
            _setTokenURI(tokenId, METADATA_SHIBAINU);
        }
        else if (randomNumber == 1) {
            _setTokenURI(tokenId, METADATA_HUSKY);
        }
        else if (randomNumber == 2) {
            _setTokenURI(tokenId, METADATA_BULLDOG);
        }
        else if (randomNumber == 3) {
            _setTokenURI(tokenId, METADATA_SHEPHERD);
        }
    }

    // NFT铸造代码
    function safeMint() public payable onlyOwner {
        require(mintWindow, "Mint is not open yet!");
        require(msg.value == 0.005 ether, "The price of dog nft is 0.005 ether.");
        require(totalSupply() < MAX_AMOUNT, "Dog NFT is sold out!");
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        _mint(msg.sender, tokenId);
        // 设置该nft对应的tokenid对应的uri
        uint256 randomNumber = random(4);
        if (randomNumber == 0) {
            _setTokenURI(tokenId, METADATA_SHIBAINU);
        }
        else if (randomNumber == 1) {
            _setTokenURI(tokenId, METADATA_HUSKY);
        }
        else if (randomNumber == 2) {
            _setTokenURI(tokenId, METADATA_BULLDOG);
        }
        else if (randomNumber == 3) {
            _setTokenURI(tokenId, METADATA_SHEPHERD);
        }
        emit LogInt(tokenId);
    }

    // 添加白名单 白名单的成员可以参与premint
    function addToWhiteList(address[] memory addrs) public onlyOwner {
        for (uint256 i = 0; i < addrs.length; i++) {
            whiteList[addrs[i]] = true;
        }
    }

    // 设置铸造窗口是否被打开
    function setMintAndPreMintWindow(bool _preMint, bool _mint) public onlyOwner {
        preMintWindow = _preMint;
        mintWindow = _mint;
    }

    // 取款函数
    function withdraw(address payable addr) external onlyOwner {
        addr.transfer(address(this).balance);
    }

    // 随机数
    function random(uint number) public view returns(uint) {
        return uint(keccak256(abi.encodePacked(block.timestamp,block.difficulty,  
            msg.sender))) % number;
    }

    function _transferFrom(address from, address to, uint256 tokenId)
        internal
    {
        _transferFrom(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        returns (bool)
    {
        return supportsInterface(interfaceId);
    }
}

添加NFT的一些基本功能,后续需要加上NFT管理合约

pragma solidity ^0.5.0;

import "../.deps/node_modules/@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "../.deps/node_modules/@openzeppelin/contracts/drafts/Counters.sol";
import "../.deps/node_modules/@openzeppelin/contracts/ownership/Ownable.sol";

contract NFT is ERC721Full, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    uint256 public MAX_AMOUNT = 3;
    // 定义一个白名单 可以调用premint
    mapping(address => bool) public whiteList;
    // 记录铸造时间
    mapping(uint256 => uint256) private mintTime;
    // 定义枚举类型表示状态:审核中 / 预发行 / 发行中 / 已绝版 / 已销毁
    enum Status { PendingReview, PreIssued, BeingIssued, OutOfPrint, Destroyed }
    Status public nowStatus;
    // NFT的价格(设定为ERC20同质化货币的数量)
    uint256 public price;
    // 版权信息结构体
    struct Copyright {
        // 作者
        string author; 
        // 使用许可
        string license;  
    }
    // 将每一个拥有者映射到版权信息
    mapping(address => Copyright) public copyright;

    event LogInt(uint message);
    event LogString(string message);

    // METADATA of NFT
    string constant METADATA_SHIBAINU = "ipfs://QmXw7TEAJWKjKifvLE25Z9yjvowWk2NWY3WgnZPUto9XoA";
    string constant METADATA_HUSKY = "ipfs://QmTFXZBmmnSANGRGhRVoahTTVPJyGaWum8D3YicJQmG97m";
    string constant METADATA_BULLDOG = "ipfs://QmSM5h4WseQWATNhFWeCbqCTAGJCZc11Sa1P5gaXk38ybT";
    string constant METADATA_SHEPHERD = "ipfs://QmRGryH7a1SLyTccZdnatjFpKeaydJcpvQKeQTzQgEp9eK";

    // 参数一:价格 / 参数二:nft被铸造的最大数量 / 参数三:
    constructor(uint256 _price, uint256 _MAX_AMOUNT)
        public  
        ERC721Full("Dogs", "DGS")
    {
        // 初始化nft的价格
        setNFTValue(_price);
        // 初始化产品发布的总数量
        setMaxAmount(_MAX_AMOUNT);
        // 初始化当前nft的状态
        nowStatus = Status.PendingReview;
    }

    // 设置产品发布的总数量
    function setMaxAmount(uint256 _MAX_AMOUNT) public {
        MAX_AMOUNT = _MAX_AMOUNT;
    }

    // 设置当前的状态字段,通过后端审核然后更改状态,发行之后购买完变成已绝版
    function setStatus(Status _nowStatus) public {
        nowStatus = _nowStatus;
    }

    // 设置NFT的价格
    function setNFTValue(uint256 _price) public {
        price = _price;
    }

    // 获取当前区块的信息
    function getBlockInfo() public view returns (
        uint256 blockNumber,
        address blockMiner,
        uint256 blockParentHash
    ) {
        blockNumber = block.number; // 当前区块号(区块哈希)
        blockMiner = block.coinbase; // 当前区块的矿工地址(播报方)
        // 当前区块的哈希值
        // 区块被打包的时间
        // 当前区块所包含的交易数量
        blockParentHash = uint256(blockhash(block.number - 1)); // 当前区块的父区块的哈希值
    }

    // 溯源

    // 得到指定tokenid的发行时间
    function getMintTime(uint256 _tokenId) public view returns (uint256) {
        return mintTime[_tokenId];
    }

    // 设置版权信息
    function setCopyright(
        string memory _author,
        string memory _license
    ) public {
        copyright[msg.sender] = Copyright({
            author: _author,
            license: _license
        });
    }

    // 为提前铸造NFT的提供内部优惠
    function preMint() public payable {
        require(nowStatus == Status.PreIssued, "Premint is not open yet!");
        require(msg.value == price / 5, "The price of dog nft is 0.005 ether.");
        require(whiteList[msg.sender], "You are not in the whiteList.");
        require(totalSupply() < MAX_AMOUNT, "Dog NFT is sold out!");
        require(balanceOf(msg.sender) < 1, "Max amount of NFT minted by an adress is 1");
        // 设置tokenid
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        // 铸造nft
        _mint(msg.sender, tokenId);
        // 设置该nft对应的tokenid对应的uri
        uint256 randomNumber = random(4);
        if (randomNumber == 0) {
            _setTokenURI(tokenId, METADATA_SHIBAINU);
        }
        else if (randomNumber == 1) {
            _setTokenURI(tokenId, METADATA_HUSKY);
        }
        else if (randomNumber == 2) {
            _setTokenURI(tokenId, METADATA_BULLDOG);
        }
        else if (randomNumber == 3) {
            _setTokenURI(tokenId, METADATA_SHEPHERD);
        }
        // 记录铸造时间
        mintTime[tokenId] = block.timestamp;
        // 统计totalSupply是不是已经到达最大值
        if (totalSupply() == MAX_AMOUNT) {
            nowStatus = Status.OutOfPrint;
        }
    }

    // NFT铸造代码
    function safeMint() public payable onlyOwner {
        require(nowStatus == Status.BeingIssued, "Mint is not open yet!");
        require(msg.value == price, "The price of dog nft is 0.005 ether.");
        require(totalSupply() < MAX_AMOUNT, "Dog NFT is sold out!");
        uint256 tokenId = _tokenIds.current();
        _tokenIds.increment();
        _mint(msg.sender, tokenId);
        // 设置该nft对应的tokenid对应的uri
        uint256 randomNumber = random(4);
        if (randomNumber == 0) {
            _setTokenURI(tokenId, METADATA_SHIBAINU);
        }
        else if (randomNumber == 1) {
            _setTokenURI(tokenId, METADATA_HUSKY);
        }
        else if (randomNumber == 2) {
            _setTokenURI(tokenId, METADATA_BULLDOG);
        }
        else if (randomNumber == 3) {
            _setTokenURI(tokenId, METADATA_SHEPHERD);
        }
        // 记录铸造时间
        mintTime[tokenId] = block.timestamp;
        // 统计totalSupply是不是已经到达最大值
        if (totalSupply() == MAX_AMOUNT) {
            nowStatus = Status.OutOfPrint;
        }
    }

    // 添加白名单 白名单的成员可以参与premint
    function addToWhiteList(address[] memory addrs) public onlyOwner {
        for (uint256 i = 0; i < addrs.length; i++) {
            whiteList[addrs[i]] = true;
        }
    }

    // 取款函数
    function withdraw(address payable addr) external onlyOwner {
        addr.transfer(address(this).balance);
    }

    // 随机数
    function random(uint number) public view returns(uint) {
        return uint(keccak256(abi.encodePacked(block.timestamp,block.difficulty,  
            msg.sender))) % number;
    }

    // function _transferFrom(address from, address to, uint256 tokenId)
    //     internal
    // {
    //     _transferFrom(from, to, tokenId);
    // }

    // function supportsInterface(bytes4 interfaceId)
    //     public
    //     view
    //     returns (bool)
    // {
    //     return supportsInterface(interfaceId);
    // }
}