123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- // SPDX-License-Identifier: BSD-3-Clause
- // solhint-disable private-vars-leading-underscore
- /**
- * Copyright 2020 Compound Labs, Inc.
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
- * following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
- * following disclaimer in the documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- pragma solidity ^0.8.20;
- contract CompTimelock {
- event NewAdmin(address indexed newAdmin);
- event NewPendingAdmin(address indexed newPendingAdmin);
- event NewDelay(uint256 indexed newDelay);
- event CancelTransaction(
- bytes32 indexed txHash,
- address indexed target,
- uint256 value,
- string signature,
- bytes data,
- uint256 eta
- );
- event ExecuteTransaction(
- bytes32 indexed txHash,
- address indexed target,
- uint256 value,
- string signature,
- bytes data,
- uint256 eta
- );
- event QueueTransaction(
- bytes32 indexed txHash,
- address indexed target,
- uint256 value,
- string signature,
- bytes data,
- uint256 eta
- );
- uint256 public constant GRACE_PERIOD = 14 days;
- uint256 public constant MINIMUM_DELAY = 2 days;
- uint256 public constant MAXIMUM_DELAY = 30 days;
- address public admin;
- address public pendingAdmin;
- uint256 public delay;
- mapping(bytes32 => bool) public queuedTransactions;
- constructor(address admin_, uint256 delay_) {
- require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
- require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
- admin = admin_;
- delay = delay_;
- }
- receive() external payable {}
- function setDelay(uint256 delay_) public {
- require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");
- require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
- require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
- delay = delay_;
- emit NewDelay(delay);
- }
- function acceptAdmin() public {
- require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
- admin = msg.sender;
- pendingAdmin = address(0);
- emit NewAdmin(admin);
- }
- function setPendingAdmin(address pendingAdmin_) public {
- require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock.");
- pendingAdmin = pendingAdmin_;
- emit NewPendingAdmin(pendingAdmin);
- }
- function queueTransaction(
- address target,
- uint256 value,
- string memory signature,
- bytes memory data,
- uint256 eta
- ) public returns (bytes32) {
- require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
- require(
- eta >= getBlockTimestamp() + delay,
- "Timelock::queueTransaction: 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(
- address target,
- uint256 value,
- string memory signature,
- bytes memory data,
- uint256 eta
- ) public {
- require(msg.sender == admin, "Timelock::cancelTransaction: 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(
- address target,
- uint256 value,
- string memory signature,
- bytes memory data,
- uint256 eta
- ) public payable returns (bytes memory) {
- require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
- bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
- require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
- require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
- require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: 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);
- }
- // solium-disable-next-line security/no-call-value
- (bool success, bytes memory returnData) = target.call{value: value}(callData);
- require(success, "Timelock::executeTransaction: Transaction execution reverted.");
- emit ExecuteTransaction(txHash, target, value, signature, data, eta);
- return returnData;
- }
- function getBlockTimestamp() internal view returns (uint256) {
- // solium-disable-next-line security/no-block-members
- return block.timestamp;
- }
- }
|