简介
对应的代码TimeLock.sol
TimeLock.sol 直译为时间锁。其实是一个在solidity中实现的队列。
可以将代码加入队列,从队列中执行。执行方式为call调用。不支持重复调用,相同的地址和参数作为一个任务,重复添加无效,调用时只会调用一次,后续调用会失败,调用后再次加入队列可以调用成功。
代码解析
-
uint public constant GRACE_PERIOD = 14 days;
宽限时间 -
uint public constant MINIMUM_DELAY = 0;
最小延期时间 -
uint public constant MAXIMUM_DELAY = 30 days;
最大延期时间 -
address public admin;
管理员 -
address public pendingAdmin;
待定管理员 -
uint public delay;
延期时间 -
mapping (bytes32 => bool) public queuedTransactions;
交易队列 -
constructor(address admin_, uint delay_)
构造方法
传入 管理员的地址和延期时间,延期时间需要大于等于 MINIMUM_DELAY且小于等于MAXIMUM_DELAY。
管理员地址不能是0地址。
constructor(address admin_, uint delay_) public {
require(delay_ >= MINIMUM_DELAY, "Timelock: Delay must exceed minimum delay.");
require(delay_ <= MAXIMUM_DELAY, "Timelock: Delay must not exceed maximum delay.");
require(admin_ != address(0), "Timelock: Admin must not be 0 address");
admin = admin_;
delay = delay_;
}
-
receive() external payable { }
合约最多可以具有一个receive函数。这个函数不能有参数,不能返回任何参数,并且必须具有receive可见性和payable状态可变性。当向合约发送 Ether 且未指定调用任何函数(calldata 为空)时执行。这是在普通的以太坊转账上执行的函数(例如,通过.send()或.transfer()转账)。
如果 receive 函数不存在,但是有payable的 fallback 回退函数,那么在进行纯以太转账时,fallback 函数会调用。
如果两个函数都没有,这个合约就没法通过常规的转账交易接收以太(会抛出异常)。 -
function setDelay(uint delay_)
设置新的延期时间,只有管理员能进行设置,延期时间需要大于等于MINIMUM_DELAY且小于等于MAXIMUM_DELAY
function setDelay(uint delay_) public {
require(msg.sender == address(this), "Timelock: Call must come from Timelock.");
require(delay_ >= MINIMUM_DELAY, "Timelock: Delay must exceed minimum delay.");
require(delay_ <= MAXIMUM_DELAY, "Timelock: Delay must not exceed maximum delay.");
delay = delay_;
emit NewDelay(delay);
}
function acceptAdmin()
任职管理员
待定管理员转正,由待定管理员调用,并将其设置为管理员,待定管理员设置为0地址。
function acceptAdmin() public {
require(msg.sender == pendingAdmin, "Timelock: Call must come from pendingAdmin.");
admin = msg.sender;
pendingAdmin = address(0);
emit NewAdmin(admin);
}
function setPendingAdmin(address pendingAdmin_)
设置待定管理员
由管理员调用,将某人设置为待定管理员
function setPendingAdmin(address pendingAdmin_) public {
require(msg.sender == address(this), "Timelock: Call must come from Timelock.");
pendingAdmin = pendingAdmin_;
emit NewPendingAdmin(pendingAdmin);
}
function queueTransaction
加入队列
由管理员调用 当 eta>=当前区块时间+延期时间时可以调用,将某提案的target, value, signature, data, eta存入队列,并设置状态为true,代表队列等待执行
function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) {
require(msg.sender == admin, "Timelock: Call must come from admin.");
require(eta >= getBlockTimestamp().add(delay), "Timelock: Estimated execution block must satisfy delay.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, eta);
return txHash;
}
function cancelTransaction
取消队列由管理员调用 将target, value, signature, data, eta的提案状态设置为false
function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public {
require(msg.sender == admin, "Timelock: Call must come from admin.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, eta);
}
function executeTransaction
执行队列
由管理员执行。
根据target, value, signature, data, eta取出队列,如果是待执行状态,则执行该队列。
只有区块在eta及之后,eta+宽限时间之前才能执行。
队列使用call的方式进行执行。
function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) {
require(msg.sender == admin, "Timelock: Call must come from admin.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
require(queuedTransactions[txHash], "Timelock: Transaction hasn't been queued.");
require(getBlockTimestamp() >= eta, "Timelock: Transaction hasn't surpassed time lock.");
require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock: Transaction is stale.");
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returnData) = target.call{value: value}(callData); //solhint-disable avoid-call-value
require(success, "Timelock: Transaction execution reverted.");
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
return returnData;
}
getBlockTimestamp()
获取当前区块的时间戳
function getBlockTimestamp() internal view returns (uint) {
// solhint-disable-next-line not-rely-on-time
return block.timestamp;
}