如何在Solidity中创建计时器并在计时结束后重置VIP列表?
问题分析与修复方案
核心问题排查
你的VIP重置功能失效主要由以下几个原因导致:
- VIP数组未同步更新:添加VIP时仅修改了
monthlyVIP映射,未将地址存入vip数组,导致resetVIPs遍历空数组,无法执行重置操作。 - 计时周期不符合需求:当前计时器仅设置60秒后结束,而非你需要的5周时长。
- 重置触发逻辑缺失:
timeIsOver修饰器未被任何函数使用,导致时间到期后无法触发重置(Solidity无原生定时任务,需通过外部调用触发)。 - 重置后数组未清理:重置VIP状态后未清空
vip数组,会导致后续重置操作重复处理无效地址,浪费Gas。
修复后的合约代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; import '@openzeppelin/contracts/access/Ownable.sol'; contract TestContractV1 is ERC721, Ownable{ uint256 public mintPrice = 0.00000000000000001 ether; uint256 public totalSupply; uint256 public maxSupply; bool public isMintEnabled; uint public start; uint public end; // 5周对应的秒数:5*7*24*60*60 = 3024000秒 uint public constant VIP_CYCLE = 3024000; mapping(address => uint256) public mintedWalletes; mapping(address => bool) public whitelist; mapping(address => bool) public monthlyVIP; address[] public vip; constructor() payable ERC721('Test V1', 'TV1'){ maxSupply = 2; } // 检查时间是否到期的修饰器 modifier timeIsOver{ require(block.timestamp >= end, "Time not up yet"); _; } function toggleIsMintEnabled() external onlyOwner{ isMintEnabled = !isMintEnabled; whitelist[msg.sender] = true; startTimer(); } function setMaxSupply(uint256 maxSupply_) external onlyOwner{ maxSupply = maxSupply_; } function addToWhitelist(address[] calldata toAddAddresses) external onlyOwner { for (uint i = 0; i < toAddAddresses.length; i++) { whitelist[toAddAddresses[i]] = true; } } function addToVIP(address[] calldata toAddAddresses) external onlyOwner { for (uint i = 0; i < toAddAddresses.length; i++) { address addr = toAddAddresses[i]; // 避免重复添加同一地址到数组 if (!monthlyVIP[addr]) { monthlyVIP[addr] = true; vip.push(addr); } } } function removeFromWhitelist(address[] calldata toRemoveAddresses) external onlyOwner { for (uint i = 0; i < toRemoveAddresses.length; i++) { delete whitelist[toRemoveAddresses[i]]; } } function resetVIPs() public timeIsOver { for (uint i=0; i<vip.length ; i++) { monthlyVIP[vip[i]] = false; } // 清空VIP数组,避免后续重复处理 delete vip; // 重置计时器,开启下一个周期 startTimer(); } function whitelistFunc() public view { require(whitelist[msg.sender], "NOT_IN_WHITELIST"); } function startTimer() public{ start = block.timestamp; // 直接设置结束时间,无需单独调用endTimer end = start + VIP_CYCLE; } function timeLeft() public view returns(uint){ if(block.timestamp >= end) return 0; return end - block.timestamp; } function mint() external payable { // 铸造时检查是否到VIP重置时间,若到期则自动触发重置 if(block.timestamp >= end){ resetVIPs(); } require(isMintEnabled, 'MINTING_NOT_ENABLED'); require(whitelist[msg.sender], 'NOT_IN_WHITELIST'); require(mintedWalletes[msg.sender] < 100, 'REARD_BEEN_CLAIMED_ALREADY'); require(monthlyVIP[msg.sender] , 'YOU_NOT_MONTHLY_VIP'); require(maxSupply > totalSupply, 'SOLD_OUT'); mintedWalletes[msg.sender]++; totalSupply++; uint256 tokenId = totalSupply; _safeMint(msg.sender, tokenId); } }
关键修改说明
- 维护VIP数组:在
addToVIP中添加重复检查,仅将未加入的地址存入vip数组,确保resetVIPs能遍历所有有效VIP地址。 - 修正计时周期:定义
VIP_CYCLE常量为5周对应的秒数,并在startTimer中直接设置结束时间,移除冗余的endTimer函数。 - 优化重置触发逻辑:在
mint函数中增加时间检查,到期自动触发resetVIPs;同时将resetVIPs与timeIsOver修饰器绑定,确保只有时间到期后才能调用重置。 - 清理VIP数组:重置VIP状态后清空
vip数组,避免后续操作处理无效地址,节省Gas消耗。 - 修复时间计算错误:
timeLeft函数修正为计算剩余时间(原代码逻辑错误,返回的是已超时的时长)。
内容的提问来源于stack exchange,提问作者NoNam4
相关产品推荐
相关产品推荐

