# 常见盲盒实现方案

By [sen](https://paragraph.com/@sen-3) · 2022-04-29

---

众所周知，NFT盲盒是一个比较大众的玩法，其中也有不少事故发生，比如提前泄露稀有度事件，这里有一篇分析很细致的文章，感兴趣的同学可以看看：

[https://learnblockchain.cn/article/2939](https://learnblockchain.cn/article/2939)

如果对opensea上图片是如何显示的不清楚，可以参考我上篇文章：

[https://mirror.xyz/0xC0329563206dd8d6565F2878FCEB189480Cd3197/XnOxEeg9z5rJu9tCZJ6SSs4hUzIUvWYDqED4ajFgXo8](https://mirror.xyz/0xC0329563206dd8d6565F2878FCEB189480Cd3197/XnOxEeg9z5rJu9tCZJ6SSs4hUzIUvWYDqED4ajFgXo8)

解决方案
----

那么怎么能安全的公开透明的公布盲盒数据呢？

我这里总结从交互方式上分为两种：

### 方案一

项目方统一开盒，这种比较简单，实际上就是修改baseURI，因为NFT的图片是由接口tokenURI控制的，所以只要修改tokenURI就能统一修改图片路径

    /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
    
            string memory baseURI = _baseURI();
            return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
        }
    

拿图片存放在ipfs举例，开盒前的盲盒图上传至ipfs，代码中判断未开盒状态，直接返回盲盒图对应的配置文件地址即可；然后把开盒后的图片放到一个文件夹中，要求图片名跟tokenId对应上，把整个文件夹批量上传，然后把文件夹的CID访问路径设置为baseURI即可。

优点是简单易懂，gasfee低，缺点玩法比较单一，与现实中盲盒不太一样，而且项目方在开盒后，还需要证明开盒后的图片是在出售盲盒前就定死的了（实际上很多项目都没证明，甚至是图片都没上ipfs或arweave，而是存储在中心化服务器上）（证明方式：可以在盲盒合约里写上开盒后图片CID的hash值）。

### 方案二

玩家自己控制开盒，每个token可以自主选择什么时候开盒，一般需要玩家自己去项目方官网发起一笔开盒交易。该方案优点比较明显，盲盒与实物分开售卖，而且玩家可以自己决定什么时候开盒，与现实的盲盒玩法几乎一致，缺点是实现难度略高，gasfee稍高（开盒也需要玩家掏gasfee）；

具体实现可以参考我另一篇文章：

[https://mirror.xyz/0xC0329563206dd8d6565F2878FCEB189480Cd3197/VXYRBEi79hZ6BmEy9TQRdQXpDaE\_ik5t\_SNOb\_bwx4Q](https://mirror.xyz/0xC0329563206dd8d6565F2878FCEB189480Cd3197/VXYRBEi79hZ6BmEy9TQRdQXpDaE_ik5t_SNOb_bwx4Q)

2022-5-13更新
-----------

### 方案三

方案二有个点就是一旦有一个NFT开盒后，ipfs上的文件夹地址就暴露了，那么剩下的盲盒长啥样也都能看到了，只是因为开盒是随机映射的，所以还不确定哪个tokenId能开出哪张图。今天看到有个项目规避了这个问题（前提是项目方不暴露）：

[https://opensea.io/collection/zombieclub-token](https://opensea.io/collection/zombieclub-token)

### 前期准备

项目方提前把图片上传至ipfs，然后把图片的cid排好的顺序组织成了Merkle Tree，将root hash值保存至合约上，这样可以证明项目方没有擅自修改图片顺序，也没有换图

### 开盒模式

该项目使用两步走的开盒方式

**_第一步先申请开盒_**

![申请开盒](https://storage.googleapis.com/papyrus_images/09643355769475264bc4f156d33923a717a52c1d1c2c9c75ad118be2d1bfbb2b.png)

申请开盒

使用的是chainlink的VRF产生随机数，用来从tokenIdMap中随机抽一个index出来，这个跟我之前分享的[随机数抽取池](https://mirror.xyz/0xC0329563206dd8d6565F2878FCEB189480Cd3197/VXYRBEi79hZ6BmEy9TQRdQXpDaE_ik5t_SNOb_bwx4Q)是类似的做法，可以参考了解。

通过这一步，拿到了一个随机数，与剩余未开盒数取模，得到一个随机的index，他这里叫revealId

**_第二步开盒_**

![开盒逻辑](https://storage.googleapis.com/papyrus_images/beb6dc2bbf2fcde043f501ce78ebc5b6cd5447d2420fa3018b5d537e6f0096ec.png)

开盒逻辑

可以看到传进来的\_index需要跟上一步获取的revealId一致，确保是合法的；然后把经过一定算法encode过的byte数组保存起来，tokenURI接口获取时，解析得到CID拼接前缀ipfs://后返回，完成真实图片的披露

### 总结

优点：纯随机不可预测，除项目方提前知道所有图片长啥样外，其他人只有到开盒那一瞬间才知道

缺点：开盒需要两步，且里面逻辑较复杂，导致gasfee较贵（专门去对比了下，任何一步的操作都比mint要贵）

---

*Originally published on [sen](https://paragraph.com/@sen-3/lmIDh1AfcGgOWi6oeYIE)*
