NFT标准开发技能Skill nft-standards

这个技能专注于掌握和实现NFT(非同质化代币)的标准,包括ERC-721和ERC-1155。它涵盖元数据处理、铸造策略、市场集成、版税设置、灵魂绑定代币和动态NFT等高级功能。适用于艺术、游戏、收藏品NFT集合的创建,市场开发,以及数字资产系统的构建。关键词:NFT标准, ERC-721, ERC-1155, 智能合约开发, 元数据处理, 区块链技术, 市场集成, 版税管理, 动态NFT。

NFT 0 次安装 0 次浏览 更新于 3/16/2026

名称: nft-standards 描述: 实现NFT标准(ERC-721, ERC-1155),包括正确的元数据处理、铸造策略和市场集成。在创建NFT合约、构建NFT市场或实现数字资产系统时使用。

NFT标准

掌握ERC-721和ERC-1155 NFT标准、元数据最佳实践和高级NFT功能。

何时使用此技能

  • 创建NFT集合(艺术、游戏、收藏品)
  • 实现市场功能
  • 构建链上或链下元数据
  • 创建灵魂绑定代币(不可转移)
  • 实现版税和收入分享
  • 开发动态/进化NFT

ERC-721(非同质化代币标准)

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

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

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

    uint256 public constant MAX_SUPPLY = 10000;
    uint256 public constant MINT_PRICE = 0.08 ether;
    uint256 public constant MAX_PER_MINT = 20;

    constructor() ERC721("MyNFT", "MNFT") {}

    function mint(uint256 quantity) external payable {
        require(quantity > 0 && quantity <= MAX_PER_MINT, "无效数量");
        require(_tokenIds.current() + quantity <= MAX_SUPPLY, "超过最大供应量");
        require(msg.value >= MINT_PRICE * quantity, "支付不足");

        for (uint256 i = 0; i < quantity; i++) {
            _tokenIds.increment();
            uint256 newTokenId = _tokenIds.current();
            _safeMint(msg.sender, newTokenId);
            _setTokenURI(newTokenId, generateTokenURI(newTokenId));
        }
    }

    function generateTokenURI(uint256 tokenId) internal pure returns (string memory) {
        // 返回IPFS URI或链上元数据
        return string(abi.encodePacked("ipfs://QmHash/", Strings.toString(tokenId), ".json"));
    }

    // 必需的重写
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 batchSize
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
    }

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

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

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

    function withdraw() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }
}

ERC-1155(多代币标准)

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

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

contract GameItems is ERC1155, Ownable {
    uint256 public constant SWORD = 1;
    uint256 public constant SHIELD = 2;
    uint256 public constant POTION = 3;

    mapping(uint256 => uint256) public tokenSupply;
    mapping(uint256 => uint256) public maxSupply;

    constructor() ERC1155("ipfs://QmBaseHash/{id}.json") {
        maxSupply[SWORD] = 1000;
        maxSupply[SHIELD] = 500;
        maxSupply[POTION] = 10000;
    }

    function mint(
        address to,
        uint256 id,
        uint256 amount
    ) external onlyOwner {
        require(tokenSupply[id] + amount <= maxSupply[id], "超过最大供应量");

        _mint(to, id, amount, "");
        tokenSupply[id] += amount;
    }

    function mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) external onlyOwner {
        for (uint256 i = 0; i < ids.length; i++) {
            require(tokenSupply[ids[i]] + amounts[i] <= maxSupply[ids[i]], "超过最大供应量");
            tokenSupply[ids[i]] += amounts[i];
        }

        _mintBatch(to, ids, amounts, "");
    }

    function burn(
        address from,
        uint256 id,
        uint256 amount
    ) external {
        require(from == msg.sender || isApprovedForAll(from, msg.sender), "未授权");
        _burn(from, id, amount);
        tokenSupply[id] -= amount;
    }
}

元数据标准

链下元数据(IPFS)

{
  "name": "NFT #1",
  "description": "NFT的描述",
  "image": "ipfs://QmImageHash",
  "attributes": [
    {
      "trait_type": "Background",
      "value": "Blue"
    },
    {
      "trait_type": "Rarity",
      "value": "Legendary"
    },
    {
      "trait_type": "Power",
      "value": 95,
      "display_type": "number",
      "max_value": 100
    }
  ]
}

链上元数据

contract OnChainNFT is ERC721 {
    struct Traits {
        uint8 background;
        uint8 body;
        uint8 head;
        uint8 rarity;
    }

    mapping(uint256 => Traits) public tokenTraits;

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        Traits memory traits = tokenTraits[tokenId];

        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "NFT #', Strings.toString(tokenId), '",',
                        '"description": "On-chain NFT",',
                        '"image": "data:image/svg+xml;base64,', generateSVG(traits), '",',
                        '"attributes": [',
                        '{"trait_type": "Background", "value": "', Strings.toString(traits.background), '"},',
                        '{"trait_type": "Rarity", "value": "', getRarityName(traits.rarity), '"}',
                        ']}'
                    )
                )
            )
        );

        return string(abi.encodePacked("data:application/json;base64,", json));
    }

    function generateSVG(Traits memory traits) internal pure returns (string memory) {
        // 基于特征生成SVG
        return "...";
    }
}

版税(EIP-2981)

import "@openzeppelin/contracts/interfaces/IERC2981.sol";

contract NFTWithRoyalties is ERC721, IERC2981 {
    address public royaltyRecipient;
    uint96 public royaltyFee = 500; // 5%

    constructor() ERC721("Royalty NFT", "RNFT") {
        royaltyRecipient = msg.sender;
    }

    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        return (royaltyRecipient, (salePrice * royaltyFee) / 10000);
    }

    function setRoyalty(address recipient, uint96 fee) external onlyOwner {
        require(fee <= 1000, "版税费用过高"); // 最高10%
        royaltyRecipient = recipient;
        royaltyFee = fee;
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, IERC165)
        returns (bool)
    {
        return interfaceId == type(IERC2981).interfaceId ||
               super.supportsInterface(interfaceId);
    }
}

灵魂绑定代币(不可转移)

contract SoulboundToken is ERC721 {
    constructor() ERC721("Soulbound", "SBT") {}

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 batchSize
    ) internal virtual override {
        require(from == address(0) || to == address(0), "代币为灵魂绑定");
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
    }

    function mint(address to) external {
        uint256 tokenId = totalSupply() + 1;
        _safeMint(to, tokenId);
    }

    // 允许销毁(用户可以销毁他们的SBT)
    function burn(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, "非代币所有者");
        _burn(tokenId);
    }
}

动态NFT

contract DynamicNFT is ERC721 {
    struct TokenState {
        uint256 level;
        uint256 experience;
        uint256 lastUpdated;
    }

    mapping(uint256 => TokenState) public tokenStates;

    function gainExperience(uint256 tokenId, uint256 exp) external {
        require(ownerOf(tokenId) == msg.sender, "非代币所有者");

        TokenState storage state = tokenStates[tokenId];
        state.experience += exp;

        // 升级逻辑
        if (state.experience >= state.level * 100) {
            state.level++;
        }

        state.lastUpdated = block.timestamp;
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        TokenState memory state = tokenStates[tokenId];

        // 基于当前状态生成元数据
        return generateMetadata(tokenId, state);
    }

    function generateMetadata(uint256 tokenId, TokenState memory state)
        internal
        pure
        returns (string memory)
    {
        // 动态元数据生成
        return "";
    }
}

气体优化铸造(ERC721A)

import "erc721a/contracts/ERC721A.sol";

contract OptimizedNFT is ERC721A {
    uint256 public constant MAX_SUPPLY = 10000;
    uint256 public constant MINT_PRICE = 0.05 ether;

    constructor() ERC721A("Optimized NFT", "ONFT") {}

    function mint(uint256 quantity) external payable {
        require(_totalMinted() + quantity <= MAX_SUPPLY, "超过最大供应量");
        require(msg.value >= MINT_PRICE * quantity, "支付不足");

        _mint(msg.sender, quantity);
    }

    function _baseURI() internal pure override returns (string memory) {
        return "ipfs://QmBaseHash/";
    }
}

资源

  • references/erc721.md: ERC-721规范详情
  • references/erc1155.md: ERC-1155多代币标准
  • references/metadata-standards.md: 元数据最佳实践
  • references/enumeration.md: 代币枚举模式
  • assets/erc721-contract.sol: 生产ERC-721模板
  • assets/erc1155-contract.sol: 生产ERC-1155模板
  • assets/metadata-schema.json: 标准元数据格式
  • assets/metadata-uploader.py: IPFS上传工具

最佳实践

  1. 使用OpenZeppelin: 经过实战测试的实现
  2. 固定元数据: 使用固定服务在IPFS上
  3. 实现版税: EIP-2981以兼容市场
  4. 气体优化: 使用ERC721A进行批量铸造
  5. 揭示机制: 占位符→揭示模式
  6. 枚举: 支持市场中的walletOfOwner
  7. 白名单: 使用Merkle树进行高效白名单

市场集成

  • OpenSea: ERC-721/1155, 元数据标准
  • LooksRare: 版税执行
  • Rarible: 协议费用, 懒惰铸造
  • Blur: 气体优化交易