Nesse artigo iremos aprender a como criar um contrato com bloqueio de tempo, para agendar a execução de uma transação no futuro.
Bloqueio de Tempo (TimeLock)
TimeLock
é um contrato que publica uma transação a ser executada no futuro. Após um período de espera mínimo, a transação pode ser executada.
TimeLock's
são comumente usados em DAOs.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract TimeLock {error NotOwnerError();error AlreadyQueuedError(bytes32 txId);error TimestampNotInRangeError(uint blockTimestamp, uint timestamp);error NotQueuedError(bytes32 txId);error TimestampNotPassedError(uint blockTimestmap, uint timestamp);error TimestampExpiredError(uint blockTimestamp, uint expiresAt);error TxFailedError();event Queue(bytes32 indexed txId,address indexed target,uint value,string func,bytes data,uint timestamp);event Execute(bytes32 indexed txId,address indexed target,uint value,string func,bytes data,uint timestamp);event Cancel(bytes32 indexed txId);uint public constant MIN_DELAY = 10; // segundosuint public constant MAX_DELAY = 1000; // segundosuint public constant GRACE_PERIOD = 1000; // segundosaddress public owner;// id transação => enfileiradasmapping(bytes32 => bool) public queued;constructor() {owner = msg.sender;}modifier onlyOwner() {if (msg.sender != owner) {revert NotOwnerError();}_;}receive() external payable {}function getTxId(address _target,uint _value,string calldata _func,bytes calldata _data,uint _timestamp) public pure returns (bytes32) {return keccak256(abi.encode(_target, _value, _func, _data, _timestamp));}/*** @param _target Endereço do contrato ou conta para chamar* @param _value Quantia de ETH a enviar* @param _func Assinatura de função, por exemplo "foo(address,uint256)"* @param _data Envio de dados codificados ABI.* @param _timestamp Timestamp após o qual a transação pode ser executada.*/function queue(address _target,uint _value,string calldata _func,bytes calldata _data,uint _timestamp) external onlyOwner returns (bytes32 txId) {txId = getTxId(_target, _value, _func, _data, _timestamp);if (queued[txId]) {revert AlreadyQueuedError(txId);}// ---|------------|---------------|-------// block block + min block + maxif (_timestamp < block.timestamp + MIN_DELAY ||_timestamp > block.timestamp + MAX_DELAY) {revert TimestampNotInRangeError(block.timestamp, _timestamp);}queued[txId] = true;emit Queue(txId, _target, _value, _func, _data, _timestamp);}function execute(address _target,uint _value,string calldata _func,bytes calldata _data,uint _timestamp) external payable onlyOwner returns (bytes memory) {bytes32 txId = getTxId(_target, _value, _func, _data, _timestamp);if (!queued[txId]) {revert NotQueuedError(txId);}// ----|-------------------|-------// timestamp timestamp + período de carênciaif (block.timestamp < _timestamp) {revert TimestampNotPassedError(block.timestamp, _timestamp);}if (block.timestamp > _timestamp + GRACE_PERIOD) {revert TimestampExpiredError(block.timestamp, _timestamp + GRACE_PERIOD);}queued[txId] = false;// preparar os dadosbytes memory data;if (bytes(_func).length > 0) {// data = func selector + _datadata = abi.encodePacked(bytes4(keccak256(bytes(_func))), _data);} else {// chamada de fallback com dadosdata = _data;}// alvo de chamada(bool ok, bytes memory res) = _target.call{value: _value}(data);if (!ok) {revert TxFailedError();}emit Execute(txId, _target, _value, _func, _data, _timestamp);return res;}function cancel(bytes32 _txId) external onlyOwner {if (!queued[_txId]) {revert NotQueuedError(_txId);}queued[_txId] = false;emit Cancel(_txId);}}