如何在Solidity的Mint循环中实现1-4951区间不重复随机数功能
Solidity 实现1-4951区间不重复随机mint方案
实现思路
- 采用优化版Fisher-Yates(费雪-耶茨)洗牌算法,无需提前初始化全量ID数组,大幅节省部署和运行gas
- 通过mapping记录已置换的ID位置,确保每次抽取的随机ID不会重复
- 每次成功mint后递增supply,最终supply数值等于已mint的总数量
依赖存储变量
// 存储已置换的ID映射,key为原索引,value为对应分配的tokenId mapping(uint256 => uint256) private _idMap; // 当前已mint的总数量 uint256 public supply; // 最大可mint的ID上限 uint256 public constant MAX_ID = 4951;
修改后的mint逻辑代码
function mint(address _to, uint256 _mintAmount) external { // 前置校验:mint数量需大于0,且不能超过剩余可mint额度 require(_mintAmount > 0 && supply + _mintAmount <= MAX_ID, "Invalid mint amount"); for (uint256 i = 0; i < _mintAmount; i++) { // 计算当前剩余可选的ID总数 uint256 remaining = MAX_ID - supply; // 生成[0, remaining)区间的随机数,本示例为基础实现,生产环境建议替换为可验证随机源 uint256 rand = uint256(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, _to, i))) % remaining; uint256 currentIndex = supply + rand; // 读取选中的tokenId,无置换记录时默认取索引+1(适配ID从1开始的规则) uint256 tokenId = _idMap[currentIndex] == 0 ? currentIndex + 1 : _idMap[currentIndex]; // 将当前选中的位置与剩余区间的最后一个位置置换,避免后续重复抽取 uint256 lastIndex = MAX_ID - 1 - supply; _idMap[currentIndex] = _idMap[lastIndex] == 0 ? lastIndex + 1 : _idMap[lastIndex]; // 执行mint逻辑 _safeMint(_to, tokenId); // 每成功mint一个,supply自增1,最终总增量等于本次mint数量 supply++; } }
说明
上述实现无需提前初始化1~4951的全量ID数组,仅在mint时写入mapping记录,gas成本和mint数量成正比,适配NFT等常见mint场景。
注意事项
- 示例中的随机数生成方式仅做功能演示,直接使用区块变量生成的随机数存在被矿工操纵的风险,生产环境如果需要高安全性的随机数,建议接入可验证随机数服务
- 逻辑原生保证所有抽取的tokenId都是1~4951区间内的不重复数值,supply累计值等于全合约累计mint的总数量
内容的提问来源于stack exchange,提问作者Haidarali Masu
相关产品推荐
相关产品推荐

