github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/L1/DelayedVetoable.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 import { ISemver } from "src/universal/ISemver.sol"; 5 6 /// @title DelayedVetoable 7 /// @notice This contract enables a delay before a call is forwarded to a target contract, and during the delay period 8 /// the call can be vetoed by the authorized vetoer. 9 /// This contract does not support value transfers, only data is forwarded. 10 /// Additionally, this contract cannot be used to forward calls with data beginning with the function selector 11 /// of the queuedAt(bytes32) function. This is because of input validation checks which solidity performs at 12 /// runtime on functions which take an argument. 13 contract DelayedVetoable is ISemver { 14 /// @notice Error for when attempting to forward too early. 15 error ForwardingEarly(); 16 17 /// @notice Error for unauthorized calls. 18 error Unauthorized(address expected, address actual); 19 20 /// @notice An event that is emitted when the delay is activated. 21 /// @param delay The delay that was activated. 22 event DelayActivated(uint256 delay); 23 24 /// @notice An event that is emitted when a call is initiated. 25 /// @param callHash The hash of the call data. 26 /// @param data The data of the initiated call. 27 event Initiated(bytes32 indexed callHash, bytes data); 28 29 /// @notice An event that is emitted each time a call is forwarded. 30 /// @param callHash The hash of the call data. 31 /// @param data The data forwarded to the target. 32 event Forwarded(bytes32 indexed callHash, bytes data); 33 34 /// @notice An event that is emitted each time a call is vetoed. 35 /// @param callHash The hash of the call data. 36 /// @param data The data forwarded to the target. 37 event Vetoed(bytes32 indexed callHash, bytes data); 38 39 /// @notice The address that all calls are forwarded to after the delay. 40 address internal immutable TARGET; 41 42 /// @notice The address that can veto a call. 43 address internal immutable VETOER; 44 45 /// @notice The address that can initiate a call. 46 address internal immutable INITIATOR; 47 48 /// @notice The delay which will be set after the initial system deployment is completed. 49 uint256 internal immutable OPERATING_DELAY; 50 51 /// @notice The current amount of time to wait before forwarding a call. 52 uint256 internal _delay; 53 54 /// @notice The time that a call was initiated. 55 mapping(bytes32 => uint256) internal _queuedAt; 56 57 /// @notice A modifier that reverts if not called by the vetoer or by address(0) to allow 58 /// eth_call to interact with this proxy without needing to use low-level storage 59 /// inspection. We assume that nobody is able to trigger calls from address(0) during 60 /// normal EVM execution. 61 modifier readOrHandle() { 62 if (msg.sender == address(0)) { 63 _; 64 } else { 65 // This WILL halt the call frame on completion. 66 _handleCall(); 67 } 68 } 69 70 /// @notice Semantic version. 71 /// @custom:semver 1.0.0 72 string public constant version = "1.0.0"; 73 74 /// @notice Sets the target admin during contract deployment. 75 /// @param vetoer_ Address of the vetoer. 76 /// @param initiator_ Address of the initiator. 77 /// @param target_ Address of the target. 78 /// @param operatingDelay_ Time to delay when the system is operational. 79 constructor(address vetoer_, address initiator_, address target_, uint256 operatingDelay_) { 80 // Note that the _delay value is not set here. Having an initial delay of 0 is helpful 81 // during the deployment of a new system. 82 VETOER = vetoer_; 83 INITIATOR = initiator_; 84 TARGET = target_; 85 OPERATING_DELAY = operatingDelay_; 86 } 87 88 /// @notice Gets the initiator 89 /// @return initiator_ Initiator address. 90 function initiator() external virtual readOrHandle returns (address initiator_) { 91 initiator_ = INITIATOR; 92 } 93 94 //// @notice Queries the vetoer address. 95 /// @return vetoer_ Vetoer address. 96 function vetoer() external virtual readOrHandle returns (address vetoer_) { 97 vetoer_ = VETOER; 98 } 99 100 //// @notice Queries the target address. 101 /// @return target_ Target address. 102 function target() external readOrHandle returns (address target_) { 103 target_ = TARGET; 104 } 105 106 /// @notice Gets the delay 107 /// @return delay_ Delay address. 108 function delay() external readOrHandle returns (uint256 delay_) { 109 delay_ = _delay; 110 } 111 112 /// @notice Gets entries in the _queuedAt mapping. 113 /// @param callHash The hash of the call data. 114 /// @return queuedAt_ The time the callHash was recorded. 115 function queuedAt(bytes32 callHash) external readOrHandle returns (uint256 queuedAt_) { 116 queuedAt_ = _queuedAt[callHash]; 117 } 118 119 /// @notice Used for all calls that pass data to the contract. 120 fallback() external { 121 _handleCall(); 122 } 123 124 /// @notice Receives all calls other than those made by the vetoer. 125 /// This enables transparent initiation and forwarding of calls to the target and avoids 126 /// the need for additional layers of abi encoding. 127 function _handleCall() internal { 128 // The initiator and vetoer activate the delay by passing in null data. 129 if (msg.data.length == 0 && _delay == 0) { 130 if (msg.sender != INITIATOR && msg.sender != VETOER) { 131 revert Unauthorized(INITIATOR, msg.sender); 132 } 133 _delay = OPERATING_DELAY; 134 emit DelayActivated(_delay); 135 return; 136 } 137 138 bytes32 callHash = keccak256(msg.data); 139 140 // Case 1: The initiator is calling the contract to initiate a call. 141 if (msg.sender == INITIATOR && _queuedAt[callHash] == 0) { 142 if (_delay == 0) { 143 // This forward function will halt the call frame on completion. 144 _forwardAndHalt(callHash); 145 } 146 _queuedAt[callHash] = block.timestamp; 147 emit Initiated(callHash, msg.data); 148 return; 149 } 150 151 // Case 2: The vetoer is calling the contract to veto a call. 152 // Note: The vetoer retains the ability to veto even after the delay has passed. This makes censoring the vetoer 153 // more costly, as there is no time limit after which their transaction can be included. 154 if (msg.sender == VETOER && _queuedAt[callHash] != 0) { 155 delete _queuedAt[callHash]; 156 emit Vetoed(callHash, msg.data); 157 return; 158 } 159 160 // Case 3: The call is from an unpermissioned actor. We'll forward the call if the delay has 161 // passed. 162 if (_queuedAt[callHash] == 0) { 163 // The call has not been initiated, so we'll treat this is an unauthorized initiation attempt. 164 revert Unauthorized(INITIATOR, msg.sender); 165 } 166 167 if (_queuedAt[callHash] + _delay > block.timestamp) { 168 // Not enough time has passed, so we'll revert. 169 revert ForwardingEarly(); 170 } 171 172 // Delete the call to prevent replays 173 delete _queuedAt[callHash]; 174 _forwardAndHalt(callHash); 175 } 176 177 /// @notice Forwards the call to the target and halts the call frame. 178 function _forwardAndHalt(bytes32 callHash) internal { 179 // Forward the call 180 emit Forwarded(callHash, msg.data); 181 (bool success, bytes memory returndata) = TARGET.call(msg.data); 182 if (success == true) { 183 assembly { 184 return(add(returndata, 0x20), mload(returndata)) 185 } 186 } else { 187 assembly { 188 revert(add(returndata, 0x20), mload(returndata)) 189 } 190 } 191 } 192 }