从foundry工程化的角度详细解读Openzeppelin中的Timers库及对应测试。
[openzeppelin]:v4.8.3,[forge-std]:v1.5.6
Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/utils/Timers.sol
Timers库是一个专门用于控制和管理有时间有效期窗口的工具库。时间有效期可以从区块链时间戳和区块高度两个维度进行管理。
封装Timers library成为一个可调用合约:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/utils/Timers.sol";
contract MockTimersTimestamp {
using Timers for Timers.Timestamp;
Timers.Timestamp _tt;
function getDeadline() external view returns (uint64){
return _tt.getDeadline();
}
function setDeadline(uint64 newTimestamp) external {
_tt.setDeadline(newTimestamp);
}
function reset() external {
_tt.reset();
}
function isUnset() external view returns (bool) {
return _tt.isUnset();
}
function isStarted() external view returns (bool) {
return _tt.isStarted();
}
function isPending() external view returns (bool) {
return _tt.isPending();
}
function isExpired() external view returns (bool) {
return _tt.isExpired();
}
}
contract MockTimersBlockNumber {
using Timers for Timers.BlockNumber;
Timers.BlockNumber _tb;
function getDeadline() external view returns (uint64){
return _tb.getDeadline();
}
function setDeadline(uint64 newBlockNumber) external {
_tb.setDeadline(newBlockNumber);
}
function reset() external {
_tb.reset();
}
function isUnset() external view returns (bool) {
return _tb.isUnset();
}
function isStarted() external view returns (bool) {
return _tb.isStarted();
}
function isPending() external view returns (bool) {
return _tb.isPending();
}
function isExpired() external view returns (bool) {
return _tb.isExpired();
}
}
全部foundry测试合约:
struct Timestamp {
// 主合约中设定的时间有效期窗口的截止时间戳,用uint64类型存储
uint64 _deadline;
}
设置和返回当前时间有效期窗口的截止时间戳。
function setDeadline(Timestamp storage timer, uint64 timestamp) internal {
// 修改Timestamp中的_deadline成员值
timer._deadline = timestamp;
}
function getDeadline(Timestamp memory timer) internal pure returns (uint64) {
// 返回Timestamp中的_deadline成员值
return timer._deadline;
}
foundry代码验证
contract TimersTest is Test {
MockTimersTimestamp mtt = new MockTimersTimestamp();
function test_SetDeadlineAndGetDeadline_ForTimestamp() external {
mtt.setDeadline(1024);
assertEq(1024, mtt.getDeadline());
}
}
reset(Timestamp storage)为重置截止时间戳;
isUnset(Timestamp memory) 为查询当前主合约的截止时间戳是否为未设置的状态,即截止时间戳是否为0。
function reset(Timestamp storage timer) internal {
// 将主合约的截止时间戳重置为0
timer._deadline = 0;
}
function isUnset(Timestamp memory timer) internal pure returns (bool) {
// 判断当前主合约的截止时间戳是否为0
return timer._deadline == 0;
}
foundry代码验证
contract TimersTest is Test {
MockTimersTimestamp mtt = new MockTimersTimestamp();
function test_ResetAndIsUnset_ForTimestamp() external {
mtt.setDeadline(1024);
assertFalse(mtt.isUnset());
mtt.reset();
assertTrue(mtt.isUnset());
assertEq(0, mtt.getDeadline());
}
}
function isStarted(Timestamp memory timer) internal pure returns (bool) {
// 判断当前主合约的截止时间戳是否大于0
return timer._deadline > 0;
}
function isPending(Timestamp memory timer) internal view returns (bool) {
// 判断当前区块链时间戳是否小于主合约设置的截止时间戳
return timer._deadline > block.timestamp;
}
function isExpired(Timestamp memory timer) internal view returns (bool) {
// 只有当时间戳有效期管理系统处于开启状态 且 当前区块链时间戳大于等于主合约设置的截止时间戳时,才会被认作是expired。 有任何一点不满足,都会返回false。
return isStarted(timer) && timer._deadline <= block.timestamp;
}
foundry代码验证
contract TimersTest is Test {
MockTimersTimestamp mtt = new MockTimersTimestamp();
function test_IsStartedAndIsPendingAndIsExpired_ForTimestamp() external {
mtt.reset();
assertFalse(mtt.isStarted());
mtt.setDeadline(1024);
assertTrue(mtt.isStarted());
// check pending/expired status
vm.warp(1024 - 1);
assertTrue(mtt.isPending());
assertFalse(mtt.isExpired());
vm.warp(1024);
assertFalse(mtt.isPending());
assertTrue(mtt.isExpired());
vm.warp(1024 + 1);
assertFalse(mtt.isPending());
assertTrue(mtt.isExpired());
// isExpired() always returns false if unset
mtt.reset();
assertFalse(mtt.isExpired());
}
}
struct BlockNumber {
// 主合约中设定的时间有效期窗口的截止区块高度,用uint64类型存储
uint64 _deadline;
}
设置和返回当前时间有效期窗口的截止区块高度。
function getDeadline(BlockNumber memory timer) internal pure returns (uint64) {
// 返回BlockNumber中的_deadline成员值
return timer._deadline;
}
function setDeadline(BlockNumber storage timer, uint64 timestamp) internal {
// 修改BlockNumber中的_deadline成员值
timer._deadline = timestamp;
}
foundry代码验证
contract TimersTest is Test {
MockTimersBlockNumber mtb = new MockTimersBlockNumber();
function test_SetDeadlineAndGetDeadline_ForBlockNumber() external {
mtb.setDeadline(1024);
assertEq(1024, mtb.getDeadline());
}
}
reset(BlockNumber storage)为重置截止区块高度;
isUnset(BlockNumber memory) 为查询当前主合约的截止区块高度是否为未设置的状态,即截止区块高度是否为0。
function reset(BlockNumber storage timer) internal {
// 将主合约的截止区块高度重置为0
timer._deadline = 0;
}
function isUnset(BlockNumber memory timer) internal pure returns (bool) {
// 判断当前主合约的截止区块高度是否为0
return timer._deadline == 0;
}
foundry代码验证
contract TimersTest is Test {
MockTimersBlockNumber mtb = new MockTimersBlockNumber();
function test_ResetAndIsUnset_ForBlockNumber() external {
mtb.setDeadline(1024);
assertFalse(mtb.isUnset());
mtb.reset();
assertTrue(mtb.isUnset());
assertEq(0, mtb.getDeadline());
}
}
function isStarted(BlockNumber memory timer) internal pure returns (bool) {
// 判断当前主合约的截止区块高度是否大于0
return timer._deadline > 0;
}
function isPending(BlockNumber memory timer) internal view returns (bool) {
// 判断当前区块链区块高度是否小于主合约设置的截止区块高度
return timer._deadline > block.number;
}
function isExpired(BlockNumber memory timer) internal view returns (bool) {
// 只有区块高度有效期管理系统处于开启状态 且 当前区块链区块高度大于等于主合约设置的截止区块高度时,才会被认作是expired。 有任何一点不满足,都会返回false。
return isStarted(timer) && timer._deadline <= block.number;
}
foundry代码验证
contract TimersTest is Test {
MockTimersBlockNumber mtb = new MockTimersBlockNumber();
function test_IsStartedAndIsPendingAndIsExpired_ForBlockNumber() external {
mtb.reset();
assertFalse(mtb.isStarted());
mtb.setDeadline(1024);
assertTrue(mtb.isStarted());
// check pending/expired status
vm.roll(1024 - 1);
assertTrue(mtb.isPending());
assertFalse(mtb.isExpired());
vm.roll(1024);
assertFalse(mtb.isPending());
assertTrue(mtb.isExpired());
vm.roll(1024 + 1);
assertFalse(mtb.isPending());
assertTrue(mtb.isExpired());
// isExpired() always returns false if unset
mtb.reset();
assertFalse(mtb.isExpired());
}
}
ps:\ 本人热爱图灵,热爱中本聪,热爱V神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!