github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/dispute/weth/DelayedWETH.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 5 import { ISemver } from "src/universal/ISemver.sol"; 6 7 import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; 8 import { IWETH } from "src/dispute/interfaces/IWETH.sol"; 9 import { WETH98 } from "src/dispute/weth/WETH98.sol"; 10 11 import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; 12 13 /// @title DelayedWETH 14 /// @notice DelayedWETH is an extension to WETH9 that allows for delayed withdrawals. Accounts must trigger an unlock 15 /// function before they can withdraw WETH. Accounts must trigger unlock by specifying a sub-account and an 16 /// amount of WETH to unlock. Accounts can trigger the unlock function at any time, but must wait a delay 17 /// period before they can withdraw after the unlock function is triggered. DelayedWETH is designed to be used 18 /// by the DisputeGame contracts where unlock will only be triggered after a dispute is resolved. DelayedWETH 19 /// is meant to sit behind a proxy contract and has an owner address that can pull WETH from any account and 20 /// can recover ETH from the contract itself. Variable and function naming vaguely follows the vibe of WETH9. 21 /// Not the prettiest contract in the world, but it gets the job done. 22 contract DelayedWETH is OwnableUpgradeable, WETH98, IDelayedWETH, ISemver { 23 /// @notice Semantic version. 24 /// @custom:semver 0.2.0 25 string public constant version = "0.2.0"; 26 27 /// @inheritdoc IDelayedWETH 28 mapping(address => mapping(address => WithdrawalRequest)) public withdrawals; 29 30 /// @notice Withdrawal delay in seconds. 31 uint256 internal immutable DELAY_SECONDS; 32 33 /// @notice Address of the SuperchainConfig contract. 34 SuperchainConfig public config; 35 36 /// @param _delay The delay for withdrawals in seconds. 37 constructor(uint256 _delay) { 38 DELAY_SECONDS = _delay; 39 initialize({ _owner: address(0), _config: SuperchainConfig(address(0)) }); 40 } 41 42 /// @notice Initializes the contract. 43 /// @param _owner The address of the owner. 44 /// @param _config Address of the SuperchainConfig contract. 45 function initialize(address _owner, SuperchainConfig _config) public initializer { 46 __Ownable_init(); 47 _transferOwnership(_owner); 48 config = _config; 49 } 50 51 /// @inheritdoc IDelayedWETH 52 function delay() external view returns (uint256) { 53 return DELAY_SECONDS; 54 } 55 56 /// @inheritdoc IDelayedWETH 57 function unlock(address _guy, uint256 _wad) external { 58 // Note that the unlock function can be called by any address, but the actual unlocking capability still only 59 // gives the msg.sender the ability to withdraw from the account. As long as the unlock and withdraw functions 60 // are called with the proper recipient addresses, this will be safe. Could be made safer by having external 61 // accounts execute withdrawals themselves but that would have added extra complexity and made DelayedWETH a 62 // leaky abstraction, so we chose this instead. 63 WithdrawalRequest storage wd = withdrawals[msg.sender][_guy]; 64 wd.timestamp = block.timestamp; 65 wd.amount += _wad; 66 } 67 68 /// @inheritdoc IWETH 69 function withdraw(uint256 _wad) public override(WETH98, IWETH) { 70 withdraw(msg.sender, _wad); 71 } 72 73 /// @inheritdoc IDelayedWETH 74 function withdraw(address _guy, uint256 _wad) public { 75 require(!config.paused(), "DelayedWETH: contract is paused"); 76 WithdrawalRequest storage wd = withdrawals[msg.sender][_guy]; 77 require(wd.amount >= _wad, "DelayedWETH: insufficient unlocked withdrawal"); 78 require(wd.timestamp > 0, "DelayedWETH: withdrawal not unlocked"); 79 require(wd.timestamp + DELAY_SECONDS <= block.timestamp, "DelayedWETH: withdrawal delay not met"); 80 wd.amount -= _wad; 81 super.withdraw(_wad); 82 } 83 84 /// @inheritdoc IDelayedWETH 85 function recover(uint256 _wad) external { 86 require(msg.sender == owner(), "DelayedWETH: not owner"); 87 uint256 amount = _wad < address(this).balance ? _wad : address(this).balance; 88 payable(msg.sender).transfer(amount); 89 } 90 91 /// @inheritdoc IDelayedWETH 92 function hold(address _guy, uint256 _wad) external { 93 require(msg.sender == owner(), "DelayedWETH: not owner"); 94 allowance[_guy][msg.sender] = _wad; 95 emit Approval(_guy, msg.sender, _wad); 96 } 97 }