solidity Dapp TimeLock时间锁工具

        时间锁是从时间的维度上,对智能合约中函数执行的时间做了一个限定;也就是将智能合约中的某个函数限制在将来某一段时间执行的代码。

时间锁使用场景

        智能合约中的时间锁有很多潜在的应用场景,主要是在defi和dao中大量使用,前者是为了锁住合约中的资金,增加安全性;后者是为了方便全体成员共识。例如通证首次公开发行中,用于实现通证销售的一些功能。时间锁也可以被用来按照时间表授权投资资金使用,即用户只有在一段时间以后才可以取出资金。还有就是通过智能合约去实现遗嘱。在DAO中,TimeLock合约为不同意系统决定的成员提供在执行决定前退出系统的时间。

        时间锁的应用场景还有很多,它们可以让合约内的交易变得更加安全和透明。但是时间锁无法自动触发,所以你需要在某个时间节点回来执行这个函数。想要让它们自己执行的话,就需要自动化你的合约。值得一提的是,时间锁作为安全解决方案的一种,并不能被当作是100%的保障手段。

时间锁一般使用方法

  • 在创建Timelock合约,时间锁合约的管理员一般为项目的多签钱包,保证去中心化。

  • 向Timelock合约内添加,需要锁定的交易。

  • 满足时间要求之后,再次调用TimeLock合约执行合约。

时间锁合约内容

变量设置

    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;   //时间锁交易队列

事件,包括加入队列事件,执行交易事件,取消交易事件。

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

函数

constructor() 构造方法设置管理员的地址和延期时间,延期时间需要大于等于MINIMUM_DELAY且小于等于MAXIMUM_DELAY。管理员地址不能是0地址。

receive() 是一个接收以太币函数,一个合约中最多可以有一个 receive 函数。在对合约转账时会执行 receive 函数,例如通过 transfer()、send() 或 call()。如果 receive 函数不存在,那么 fallback 回退函数会被调用。

getTxId() 通过hash函数给交易生成一个id。

    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

交易加入队列由管理员调用。当满足条件 (当前区块时间+最小延期时间 <=_timestamp<=当前区块时间+最大延期时间)时,给交易生成id并添加到交易队列中,设置状态为true,代表交易等待执行。

    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

执行交易由管理员执行。根据_target, _value, _func, _data, _timestamp从队列中取出交易,如果是待执行状态,接着判断时间知否满足条件,只有区块时间大于_timestamp,且小鱼_timestamp+宽限时间才能执行。使用call的进行执行交易。

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

取消队列由管理员调用 将txId的交易状态设置为false;触发事件Cancel;

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

        queued[_txId] = false;

        emit Cancel(_txId);
    }

猜你喜欢

转载自blog.csdn.net/xq723310/article/details/130400616