solidity Dapp TimeLock time lock tool

        Time lock is to limit the execution time of functions in the smart contract from the dimension of time; that is, the code that limits the execution of a function in the smart contract to a certain period of time in the future.

Time lock usage scenarios

        Time locks in smart contracts have many potential application scenarios, mainly in DeFi and DAO. The former is to lock the funds in the contract and increase security; the latter is to facilitate the consensus of all members. For example, in the initial public offering of tokens, it is used to realize some functions of token sales. Time locks can also be used to authorize the use of investment funds according to a schedule, that is, users can only withdraw funds after a certain period of time. There is also the realization of wills through smart contracts. In a DAO, the TimeLock contract gives members who disagree with the system's decision time to exit the system before enforcing the decision.

        There are many application scenarios of time locks, which can make transactions in contracts more secure and transparent. But the time lock cannot be triggered automatically, so you need to come back and execute this function at a certain time node. To make them execute themselves, you need to automate your contracts. It is worth mentioning that, as a security solution, time lock cannot be regarded as a 100% guarantee.

General usage of time lock

  • When creating Timelocka contract, the administrator of the time-lock contract is generally the multi-signature wallet of the project to ensure decentralization.

  • Add transactions that need to be locked to the Timelock contract.

  • After meeting the time requirements, call the TimeLock contract again to execute the contract.

Timelock contract content

variable settings

    uint public constant MIN_DELAY = 10; // 秒   最小延时时间
    uint public constant MAX_DELAY = 1000; // 秒   最大延时时间
    uint public constant GRACE_PERIOD = 1000; // 秒  宽限时间

    address public owner;      //管理员
    mapping(bytes32 => bool) public queued;   //时间锁交易队列

Events , including joining queue events, executing transaction events, and canceling transaction events.

    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);

function

constructor() The constructor sets the address of the administrator and the delay time. The delay time needs to be greater than or equal to MINIMUM_DELAY and less than or equal to MAXIMUM_DELAY. The administrator address cannot be 0 address.

receive() is a function to receive ether, and there can be at most one receive function in a contract. The receive function is executed when transferring money to a contract, for example via transfer(), send() or call(). If the receive function does not exist, then the fallback fallback function will be called.

getTxId() Generate an id for the transaction through the hash function.

    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));
    }

queueTx

交易Join the queue is called by the administrator. When the conditions are met (current block time + minimum extension time <= _timestamp <= current block time + maximum extension time), an id is generated for the transaction and added to the transaction queue, and the status is set to true, which means the transaction is waiting to be executed.

    function queueTx(
        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 + max
        if (
            _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);
    }

executeTx

Executing transactions are performed by administrators. Take out the transaction from the queue according to _target, _value, _func, _data, _timestamp. If it is pending execution, then judge whether the time meets the conditions. Only when the block time is greater than _timestamp, and Xiaoyu _timestamp+grace time can be executed. Use call to execute transactions.

function executeTx(
        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 + grace period
        if (block.timestamp < _timestamp) {
            revert TimestampNotPassedError(block.timestamp, _timestamp);
        }
        if (block.timestamp > _timestamp + GRACE_PERIOD) {
            revert TimestampExpiredError(block.timestamp, _timestamp + GRACE_PERIOD);
        }

        queued[txId] = false;

        // prepare data
        bytes memory data;
        if (bytes(_func).length > 0) {
            // data = func selector + _data
            data = abi.encodePacked(bytes4(keccak256(bytes(_func))), _data);
        } else {
            // call fallback with data
            data = _data;
        }

        // call target
        (bool ok, bytes memory res) = _target.call{value: _value}(data);
        if (!ok) {
            revert TxFailedError();
        }

        emit Execute(txId, _target, _value, _func, _data, _timestamp);

        return res;
    }

cancelTx

The cancel queue is called by the administrator to set the transaction status of txId to false; the event Cancel is triggered;

    function cancelTx(bytes32 _txId) external onlyOwner {
        if (!queued[_txId]) {
            revert NotQueuedError(_txId);
        }

        queued[_txId] = false;

        emit Cancel(_txId);
    }

Guess you like

Origin blog.csdn.net/xq723310/article/details/130400616