solidity implements ERC721 token standard to release NFT

1. Non-fungible currency (NFT) - Wikipedia

Non-Fungible Token (English: Non-Fungible Token, abbreviation: NFT) is a crowdfunding support project method It is also a data unit on the blockchain (digital ledger). Each token can represent a unique digital data and serve as an electronic certification or certificate of ownership of virtual goods. Due to their non-fungible nature, non-fungible tokens can represent digital assets such as paintings, artwork, sounds, films, in-game items or other forms of creative works. While the works themselves are infinitely replicable, the tokens representing them are fully trackable on their underlying blockchain, providing buyers with proof of ownership. Cryptocurrencies such as Ethereum and Bitcoin have their own token standards to define the use of NFTs.

2、IERC165

ERC165 is a very simple Ethereum standard, mainly used to detect whether the contract supports the query interface. The caller only needs to pass in the ID of the interface he wants to detect (for example, the ID of ERC-165 is 0x01ffc9a7), and the function tells the caller whether the contract implements this interface in the form of a Boolean value (true is supported, false is not supported).
IERC165 is the ERC165 standard interface contract, which specifies the basic functions to be implemented by ERC165 supportsInterface

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


interface IERC165 {
    
    
    // ERC165 代币标准就是检查一个智能合约是不是支持了例如 ERC721 ,ERC1155 的接口。 
    // 如果支持,返回true
    function supportsInterface(
        bytes4 interfaceId
    ) external view returns (bool);
}

3、IERC721

IERC721 is the interface specification of the ERC721 standard, which defines and standardizes the functions that a standard ERC721 token contract should implement. Here, the ERC721 contract directly inherits from the IERC721() interface.

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

import "./IERC165.sol";

// ERC721标准接口.
interface IERC721 is IERC165 {
    
    
    // 转账时触发
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    // 授权时触发 
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    // 在批量授权时触发 
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    
    // 返回某地址的NFT持有量balance
    function balanceOf(address owner) external view returns (uint256 balance);
    // 返回某 tokenId 的主人 owner
    function ownerOf(uint256 tokenId) external view returns (address owner);
    // 实现了 ERC721Receiver 接口的安全转账和重载函数  
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from,address to,uint256 tokenId) external;
    // 普通转账,参数为转出地址from,接收地址to和tokenId。
    function transferFrom(address from,address to,uint256 tokenId) external;
            
    // 授权NFT给另外一个地址 to
    function approve(address to, uint256 tokenId) external;
    // 持有的该系列NFT批量授权给某个地址operator
    function setApprovalForAll(address operator, bool _approved) external;
    // 查询tokenId被批准给了哪个地址。
    function getApproved(uint256 tokenId) external view returns (address operator);
    // 查询某地址的NFT是否批量授权给了另一个operator地址
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

4、IERC721Receiver

In order to prevent NFT from being transferred to the black hole contract, ERC721 implements the safeTransferFrom() safe transfer function. The target contract must implement the IERC721Receiver interface to receive ERC721 tokens, otherwise it will revert.

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


interface IERC721Receiver {
    
    
    function onERC721Received(
        address operator,
        address from,
        uint tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

5, IERC721Metadata

IERC721Metadata is an extended interface of ERC721. It defines the metadata information of the contract, including the contract name, logo and tokenURI of each token.

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


interface IERC721Metadata {
    
    
    // 代币名称
    function name() external view returns (string memory);
    // 代币符号 
    function symbol() external view returns (string memory);
    // 通过 tokenId 查询 metadata 的链接url
    function tokenURI(uint256 tokenId) external view returns (string memory);
} 

6、ERC721

ERC-721 - Non-fungible token standard, mainly used to issue unique tokenized assets such as encrypted collectibles, game equipment, etc.

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

import "./IERC165.sol";
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./IERC721Metadata.sol";
import "./Address.sol";
import "./Strings.sol";

contract ERC721 is IERC721, IERC721Metadata {
    
    
    using Address for address; // 使用Address库,用isContract来判断地址是否为合约
    using Strings for uint256; // 使用String库,

    // Token名称  
    string public override name;
    // Token代号  
    string public override symbol;
    // tokenId 到 owner address 的持有人映射
    mapping(uint => address) private _owners;
    // address 到持仓数量的持仓量映射
    mapping(address => uint) private _balances;
    // tokenID 到授权地址的授权映射
    mapping(uint => address) private _tokenApprovals;
    //  owner地址。到operator地址的批量授权映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // 构造函数,初始化`name` 和`symbol` .
    constructor(string memory name_, string memory symbol_) {
    
    
        name = name_;
        symbol = symbol_;
    }

    // 实现IERC165接口 supportsInterface
    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
    
    
        return interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId;
    }

    // 实现IERC721的balanceOf,利用_balances变量查询owner地址的balance。
    function balanceOf(address owner) external view override returns (uint) {
    
    
        require(owner != address(0), "owner = zero address");
        return _balances[owner];
    }

    // 实现IERC721的ownerOf,利用_owners变量查询tokenId的owner。
    function ownerOf(uint tokenId) public view override returns (address owner) {
    
    
        owner = _owners[tokenId];
        require(owner != address(0), "token doesn't exist");
    }       

    // 实现IERC721的isApprovedForAll,利用_operatorApprovals变量查询owner地址是否将所持NFT批量授权给了operator地址。
    function isApprovedForAll(address owner, address operator) external view override returns (bool) {
    
    
        return _operatorApprovals[owner][operator];
    }

    // 实现IERC721的setApprovalForAll,将持有代币全部授权给operator地址。
    function setApprovalForAll(address operator, bool approved) external override {
    
    
        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    // 实现IERC721的getApproved,利用_tokenApprovals变量查询tokenId的授权地址。
    function getApproved(uint tokenId) external view override returns (address) {
    
    
        require(_owners[tokenId] != address(0), "token doesn't exist");
        return _tokenApprovals[tokenId];
    }

    // 授权函数。通过调整_tokenApprovals来,授权 to 地址操作 tokenId,同时释放Approval事件。
    function _approve(address owner, address to, uint tokenId) private {
    
    
        _tokenApprovals[tokenId] = to;
        emit Approval(owner, to, tokenId);
    }

    // 实现IERC721的approve,将tokenId授权给 to 地址。
    function approve(address to, uint tokenId) external override {
    
    
        address owner = _owners[tokenId];
        require(
            msg.sender == owner || _operatorApprovals[owner][msg.sender],
            "not owner nor approved for all"
        );
        _approve(owner, to, tokenId);
    }

    // 查询 spender地址是否可以使用tokenId(他是owner或被授权地址)。
    function _isApprovedOrOwner(address owner, address spender, uint tokenId) private view returns (bool) {
    
    
        return (spender == owner ||
            _tokenApprovals[tokenId] == spender ||
            _operatorApprovals[owner][spender]);
    }

    // 转账函数
    function _transfer( address owner, address from, address to, uint tokenId) private {
    
    
        require(from == owner, "not owner");
        require(to != address(0), "transfer to the zero address");

        _approve(owner, address(0), tokenId);   // 清空token的授权 
        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }
    
    // 实现IERC721的transferFrom,非安全转账,不建议使用。调用_transfer函数
    function transferFrom(address from, address to, uint tokenId) external override {
    
    
        address owner = ownerOf(tokenId);
        require(
            _isApprovedOrOwner(owner, msg.sender, tokenId),
            "not owner nor approved"
        );
        _transfer(owner, from, to, tokenId);
    }

    // 安全转账,安全地将 tokenId 代币从 from 转移到 to
    function _safeTransfer( address owner, address from, address to, uint tokenId, bytes memory _data ) private {
    
    
        _transfer(owner, from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver");
    }

    // 实现IERC721的safeTransferFrom,安全转账,调用了_safeTransfer函数。
    function safeTransferFrom(address from, address to, uint tokenId, bytes memory _data) public override {
    
    
        address owner = ownerOf(tokenId);
        require(_isApprovedOrOwner(owner, msg.sender, tokenId), "not owner nor approved");
        _safeTransfer(owner, from, to, tokenId, _data);
    }

    // safeTransferFrom重载函数
    function safeTransferFrom( address from, address to, uint tokenId) external override {
    
    
        safeTransferFrom(from, to, tokenId, "");
    }

    // 铸造函数。通过调整_balances和_owners变量来铸造tokenId并转账给 to,同时释放Transfer事件。。
    function _mint(address to, uint tokenId) internal virtual {
    
    
        require(to != address(0), "mint to zero address");
        require(_owners[tokenId] == address(0), "token already minted");

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    // 销毁函数,通过调整_balances和_owners变量来销毁tokenId,同时释放Transfer事件。
    function _burn(uint tokenId) internal virtual {
    
    
        address owner = ownerOf(tokenId);
        require(msg.sender == owner, "not owner of token");

        _approve(owner, address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);
    }

    // 调用IERC721Receiver-onERC721Received, 以防 tokenId 被不小心转入黑洞。
    function _checkOnERC721Received(address from, address to, uint tokenId, bytes memory _data) private returns (bool) {
    
    
        if (to.isContract()) {
    
    
            // 是否实现了 IERC721Receiver接口
            return IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) 
                    == IERC721Receiver.onERC721Received.selector;
        } else {
    
    
            return true;
        }
    }

    // 实现IERC721Metadata的tokenURI函数,查询metadata。
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
    
    
        require(_owners[tokenId] != address(0), "Token Not Exist");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    // 计算{tokenURI}的BaseURI,tokenURI就是把baseURI和tokenId拼接在一起,需要开发重写。
    function _baseURI() internal view virtual returns (string memory) {
    
    
        return "";
    }
}

7. Implementation of ERC721 NFT

Write another contract to inherit ERC721, and write the MAX_APES state variable, constructor, baseURI function and forging mint function.

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

import "./ERC721.sol";

contract NFTSang is ERC721 {
    
    
    uint public MAX_APES = 10000; // 总量

    // 构造函数
    constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_){
    
    
    }

    // ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariAR3jxcaWtq/
    function _baseURI() internal pure override returns (string memory) {
    
    
        return "ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariAR3jxcaWtq/";
    }
    
    // 铸造函数
    function mint(address to, uint tokenId) external {
    
    
        require(tokenId >= 0 && tokenId < MAX_APES, "tokenId out of range");
        _mint(to, tokenId);
    }
}

8. Compile and deploy

redmix can be compiled and run directly.

Insert image description here

Free forging tokens, official use requires developers to continue to improve, such as stipulating who has the authority to forge this token.
Insert image description here

Guess you like

Origin blog.csdn.net/weixin_61361738/article/details/134763741