Solidity如何处理block.timestamp实现8:00-18:00合约调用限制
Solidity 时间区间校验实现方案
核心计算原理
block.timestamp返回当前区块的Unix时间戳(单位:秒,基准为1970-01-01 00:00:00 UTC),不需要做复杂的字符串格式化,直接通过数学运算就能提取当日时间信息完成校验:
- 一天总秒数固定为
86400(246060) - 时间戳对86400取模,得到的结果就是当前时间距离当日UTC 0点的偏移秒数
- 对应时段的秒数直接用小时数乘3600计算即可,比如UTC 8AM对应
8*3600=28800,UTC 6PM对应18*3600=64800
修正后的完整代码
你原有代码存在一处逻辑疏漏:校验完2小时间隔后没有更新用户上次调用时间,会导致间隔校验规则完全不生效。加入时间区间校验的完整可运行代码如下:
mapping(address => uint256) public coffeKitchenLatestAqcuiredBalances; // 常量统一定义,方便后续调整 uint256 public constant CALL_INTERVAL = 7200; // 2小时调用间隔 uint256 public constant DAY_SECONDS = 86400; uint256 public constant ALLOW_START = 8 * 3600; // 允许调用起始时间:UTC 8AM uint256 public constant ALLOW_END = 18 * 3600; // 允许调用结束时间:UTC 6PM function requestStarCoinFromCoffeeKitchenFaucet() public { address callerAddress = msg.sender; uint256 userLastRetrieveTime = coffeKitchenLatestAqcuiredBalances[callerAddress]; uint256 epochNow = block.timestamp; // 校验调用时间在允许区间内 uint256 secondsOfDay = epochNow % DAY_SECONDS; require(secondsOfDay >= ALLOW_START && secondsOfDay < ALLOW_END, "Only callable between 8AM and 6PM"); // 校验2小时调用间隔 if (userLastRetrieveTime != 0){ require(userLastRetrieveTime < epochNow - CALL_INTERVAL,"You need to wait for 2 hours from your last call"); } // 更新用户上次调用时间,否则间隔校验永远不生效 coffeKitchenLatestAqcuiredBalances[callerAddress] = epochNow; // 此处编写发放StarCoin的业务逻辑 }
注意事项
- 时区适配:以上代码默认时间区间基于UTC时区,如果你需要对应特定时区的本地时间,只需要在取模前加/减对应时区偏移的秒数即可。比如要适配北京时间(UTC+8)的8AM-6PM,只需要把
secondsOfDay的计算逻辑改为uint256 secondsOfDay = (epochNow + 8*3600) % DAY_SECONDS;,起止时间常量保持83600和183600不变即可。 - 时间戳操纵风险:矿工可以对区块时间戳做最多几十秒的小幅调整,这类时间校验不适合高价值资产的严格时间判断,但水龙头这类低价值场景完全可以正常使用。
- 不要在链上做复杂的日期字符串格式化,纯数学运算的gas成本最低,也完全能满足区间校验需求。
内容的提问来源于stack exchange,提问作者Akiner Alkan
相关产品推荐
相关产品推荐

