TokenTimelock库是是一个锁币合约。它允许指定地址在某一时间点及之后取出全部锁存的代币,实现了一个代币时间锁的功能。
[openzeppelin]:v4.8.3,[forge-std]:v1.5.6
TokenTimelock库是是一个锁币合约。它允许指定地址在某一时间点及之后取出全部锁存的代币,实现了一个代币时间锁的功能。
TokenTimelock合约可直接部署。
全部foundry测试合约:
测试使用的物料合约:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
contract MockERC20 is ERC20 {
constructor(string memory name, string memory symbol)
ERC20(name, symbol) {}
function mint(address account, uint amount) external {
_mint(account, amount);
}
}
// 对IERC20类型使用token/ERC20/utils/SafeERC20库
using SafeERC20 for IERC20;
// 用来锁存的ERC20合约地址
IERC20 private immutable _token;
// 可领取锁存代币的地址
address private immutable _beneficiary;
// 可释放锁存代币的时间戳起点
uint256 private immutable _releaseTime;
// 初始化函数
constructor(
IERC20 token_,
address beneficiary_,
uint256 releaseTime_
) {
// 要求代币可释放时间戳大于当前区块链时间戳
require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time");
// 设置锁存ERC20合约地址
_token = token_;
// 设置可领取锁存代币的地址
_beneficiary = beneficiary_;
// 设置可释放锁存代币的时间戳起点
_releaseTime = releaseTime_;
}
foundry代码验证:
contract TokenTimelockTest is Test {
address private _beneficiary = address(1024);
uint private _releaseTime = 2048;
MockERC20 private _token = new MockERC20("", "");
TokenTimelock private _testing = new TokenTimelock(_token, _beneficiary, _releaseTime);
function test_Constructor() external {
// revert if _releaseTime <= now
vm.expectRevert("TokenTimelock: release time is before current time");
new TokenTimelock(_token, _beneficiary, block.timestamp - 1);
vm.expectRevert("TokenTimelock: release time is before current time");
new TokenTimelock(_token, _beneficiary, block.timestamp);
}
}
token()
:返回锁存ERC20合约地址;beneficiary()
:返回可领取锁存代币的地址;releaseTime()
:可释放锁存代币的时间戳起点。 function token() public view virtual returns (IERC20) {
return _token;
}
function beneficiary() public view virtual returns (address) {
return _beneficiary;
}
function releaseTime() public view virtual returns (uint256) {
return _releaseTime;
}
foundry代码验证:
contract TokenTimelockTest is Test {
address private _beneficiary = address(1024);
uint private _releaseTime = 2048;
MockERC20 private _token = new MockERC20("", "");
TokenTimelock private _testing = new TokenTimelock(_token, _beneficiary, _releaseTime);
function test_Getter() external {
assertEq(address(_testing.token()), address(_token));
assertEq(_testing.beneficiary(), _beneficiary);
assertEq(_testing.releaseTime(), _releaseTime);
}
}
将全部锁存代币释放给可领取锁存代币的地址。
注:token/ERC20/utils/SafeERC20库中safeTransfer()详解参见:https://learnblockchain.cn/article/7276
function release() public virtual {
// 要求当前时间戳大于等于可释放锁存代币的时间戳起点
require(block.timestamp >= releaseTime(), "TokenTimelock: current time is before release time");
// 获得本合约名下锁存全部代币的数量(代币地址为_token)
uint256 amount = token().balanceOf(address(this));
// 要求锁存代币数量大于0
require(amount > 0, "TokenTimelock: no tokens to release");
// 使用SafeERC20库与锁存代币合约交互,将本合约名下全部该token释放给可领取锁存代币的地址
token().safeTransfer(beneficiary(), amount);
}
foundry代码验证:
contract TokenTimelockTest is Test {
address private _beneficiary = address(1024);
uint private _releaseTime = 2048;
MockERC20 private _token = new MockERC20("", "");
TokenTimelock private _testing = new TokenTimelock(_token, _beneficiary, _releaseTime);
function test_Release() external {
uint amountLocked = 10000;
_token.mint(address(_testing), amountLocked);
assertEq(_token.balanceOf(address(_testing)), amountLocked);
assertEq(_token.balanceOf(address(_beneficiary)), 0);
// case 1: revert if now < _releaseTime
assert(block.timestamp < _releaseTime);
vm.expectRevert("TokenTimelock: current time is before release time");
_testing.release();
// case 2: pass if now >= _releaseTime
vm.warp(_releaseTime);
_testing.release();
assertEq(_token.balanceOf(address(_testing)), 0);
assertEq(_token.balanceOf(address(_beneficiary)), amountLocked);
// case 3: revert if no token in TokenTimelock
vm.expectRevert("TokenTimelock: no tokens to release");
_testing.release();
}
}
ps: 本人热爱图灵,热爱中本聪,热爱V神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!