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  }