NFT盲盒开发

1,156 阅读3分钟

2021年NFT火的一塌糊涂,称之为NFT元年也不为过。
而盲盒玩法又为其增加了可玩性和吸引力。
今年2月份,阿狸 ALI&HIS FRENS NFT元数据泄漏导致盲盒变明盒。
今天就来分享下NFT盲盒的玩法。

编写智能合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
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/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

/// @custom:security-contact bluequin@163.com
contract FdfNFT is ERC721, ERC721URIStorage, ERC721Burnable, AccessControl {
    using Counters for Counters.Counter;
    //是否开启盲盒
    bool private _blindBoxOpened = false;
    //设置盲盒uri
    string baseUri;
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("FDF20220325FNFT", "FDF20220325FNFT") {
      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
      _grantRole(MINTER_ROLE, msg.sender);
    }

    function setBaseUri(string memory _baseUri) public {

        require(
            hasRole(DEFAULT_ADMIN_ROLE, _msgSender()),
            "BaseERC721Uri: only admin can do this action"
        );
        baseUri = _baseUri;
    }

    function setBlindBoxOpened(bool _status) public {
        require(
            hasRole(DEFAULT_ADMIN_ROLE, _msgSender()),
            "BaseERC721BlindBox: only admin can do this action"
        );
        _blindBoxOpened = _status;
    }


    function safeMint(address to, string memory uri) public onlyRole(MINTER_ROLE) {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.
    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }


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

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

}

发布合约到rinkeby测试环境

这里直接用remix发布就行

image.png 在浏览器中查看合约部署情况。 image.png 这里看到合约已经创建成功,合约地址 0x5a501c079DB0BeB9A50aCA810be9d7277080E1B4 image.png

编写图片服务器接口

图片可以上传到ipfs,但考虑到需要收额外的手续费,以及更加灵活的和前端调用,所以这里采用自己编写服务器接口
image.png 接口代码,笔者这里使用java编写
image.png

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `image` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (1, '开启盲盒,单车变摩托', '开启盲盒,单车变摩托', 'https://lh3.googleusercontent.com/Op-cRm7h8p34PvxvzZbHh5nAmkS2rVZYil1lUYfyKrI4WuEOTG9X3_C1KvtlXGdyfwG_5wyfyOBwIytw9JZVcAkPtXq4PAdLOsmS=w340');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (2, '111', '朋克汽车', 'https://img01.jituwang.com/190326/256609-1Z326042P376.jpg');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (3, '222', '朋克', 'https://img01.jituwang.com/190330/256613-1Z33016444621.jpg');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (4, '333', '朋克', 'https://pic.ntimg.cn/file/20220107/32652940_141026506103_2.jpg');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (5, '444', '金克斯', 'https://pic.ntimg.cn/file/20220218/33805678_164928525106_2.jpg');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (6, '555', '源计划', 'https://img0.baidu.com/it/u=2501200624,302351145&fm=253&fmt=auto&app=138&f=PNG');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (7, '666', '源计划', 'https://img2.baidu.com/it/u=2016764601,1618302265&fm=253&fmt=auto&app=138&f=PNG');
INSERT INTO `test` (`id`, `name`, `description`, `image`) VALUES (8, '777', '未来战士', 'https://img0.baidu.com/it/u=718823970,501814686&fm=253&fmt=auto&app=138&f=PNG');

接口返回json结构数据。包含三个字段
image:图片真实地址
name:图片名称
description:图片描述

铸造NFT

1.调用setBaseUri方法,设置baseUri为盲盒图片 image.png
2.调用safeMint方法铸造nft

image.png
这里多创建几个

进入opensea测试网查看

testnets.opensea.io/

image.png

image.png

image.png

image.png 可以看到nft盲盒如下

image.png

开启盲盒

调用setBlindBoxOpened方法开启盲盒

image.png
refresh metadata

image.png 可以看到所有图片都已经展示出来了。(两张图片依然为盲盒,是因为我铸造NFT的时候,图片就是盲盒图片,尴尬一下😓)