github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/universal/Proxy.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { Constants } from "src/libraries/Constants.sol";
     5  
     6  /// @title Proxy
     7  /// @notice Proxy is a transparent proxy that passes through the call if the caller is the owner or
     8  ///         if the caller is address(0), meaning that the call originated from an off-chain
     9  ///         simulation.
    10  contract Proxy {
    11      /// @notice An event that is emitted each time the implementation is changed. This event is part
    12      ///         of the EIP-1967 specification.
    13      /// @param implementation The address of the implementation contract
    14      event Upgraded(address indexed implementation);
    15  
    16      /// @notice An event that is emitted each time the owner is upgraded. This event is part of the
    17      ///         EIP-1967 specification.
    18      /// @param previousAdmin The previous owner of the contract
    19      /// @param newAdmin      The new owner of the contract
    20      event AdminChanged(address previousAdmin, address newAdmin);
    21  
    22      /// @notice A modifier that reverts if not called by the owner or by address(0) to allow
    23      ///         eth_call to interact with this proxy without needing to use low-level storage
    24      ///         inspection. We assume that nobody is able to trigger calls from address(0) during
    25      ///         normal EVM execution.
    26      modifier proxyCallIfNotAdmin() {
    27          if (msg.sender == _getAdmin() || msg.sender == address(0)) {
    28              _;
    29          } else {
    30              // This WILL halt the call frame on completion.
    31              _doProxyCall();
    32          }
    33      }
    34  
    35      /// @notice Sets the initial admin during contract deployment. Admin address is stored at the
    36      ///         EIP-1967 admin storage slot so that accidental storage collision with the
    37      ///         implementation is not possible.
    38      /// @param _admin Address of the initial contract admin. Admin has the ability to access the
    39      ///               transparent proxy interface.
    40      constructor(address _admin) {
    41          _changeAdmin(_admin);
    42      }
    43  
    44      // slither-disable-next-line locked-ether
    45      receive() external payable {
    46          // Proxy call by default.
    47          _doProxyCall();
    48      }
    49  
    50      // slither-disable-next-line locked-ether
    51      fallback() external payable {
    52          // Proxy call by default.
    53          _doProxyCall();
    54      }
    55  
    56      /// @notice Set the implementation contract address. The code at the given address will execute
    57      ///         when this contract is called.
    58      /// @param _implementation Address of the implementation contract.
    59      function upgradeTo(address _implementation) public virtual proxyCallIfNotAdmin {
    60          _setImplementation(_implementation);
    61      }
    62  
    63      /// @notice Set the implementation and call a function in a single transaction. Useful to ensure
    64      ///         atomic execution of initialization-based upgrades.
    65      /// @param _implementation Address of the implementation contract.
    66      /// @param _data           Calldata to delegatecall the new implementation with.
    67      function upgradeToAndCall(
    68          address _implementation,
    69          bytes calldata _data
    70      )
    71          public
    72          payable
    73          virtual
    74          proxyCallIfNotAdmin
    75          returns (bytes memory)
    76      {
    77          _setImplementation(_implementation);
    78          (bool success, bytes memory returndata) = _implementation.delegatecall(_data);
    79          require(success, "Proxy: delegatecall to new implementation contract failed");
    80          return returndata;
    81      }
    82  
    83      /// @notice Changes the owner of the proxy contract. Only callable by the owner.
    84      /// @param _admin New owner of the proxy contract.
    85      function changeAdmin(address _admin) public virtual proxyCallIfNotAdmin {
    86          _changeAdmin(_admin);
    87      }
    88  
    89      /// @notice Gets the owner of the proxy contract.
    90      /// @return Owner address.
    91      function admin() public virtual proxyCallIfNotAdmin returns (address) {
    92          return _getAdmin();
    93      }
    94  
    95      //// @notice Queries the implementation address.
    96      /// @return Implementation address.
    97      function implementation() public virtual proxyCallIfNotAdmin returns (address) {
    98          return _getImplementation();
    99      }
   100  
   101      /// @notice Sets the implementation address.
   102      /// @param _implementation New implementation address.
   103      function _setImplementation(address _implementation) internal {
   104          bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
   105          assembly {
   106              sstore(proxyImplementation, _implementation)
   107          }
   108          emit Upgraded(_implementation);
   109      }
   110  
   111      /// @notice Changes the owner of the proxy contract.
   112      /// @param _admin New owner of the proxy contract.
   113      function _changeAdmin(address _admin) internal {
   114          address previous = _getAdmin();
   115          bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
   116          assembly {
   117              sstore(proxyOwner, _admin)
   118          }
   119          emit AdminChanged(previous, _admin);
   120      }
   121  
   122      /// @notice Performs the proxy call via a delegatecall.
   123      function _doProxyCall() internal {
   124          address impl = _getImplementation();
   125          require(impl != address(0), "Proxy: implementation not initialized");
   126  
   127          assembly {
   128              // Copy calldata into memory at 0x0....calldatasize.
   129              calldatacopy(0x0, 0x0, calldatasize())
   130  
   131              // Perform the delegatecall, make sure to pass all available gas.
   132              let success := delegatecall(gas(), impl, 0x0, calldatasize(), 0x0, 0x0)
   133  
   134              // Copy returndata into memory at 0x0....returndatasize. Note that this *will*
   135              // overwrite the calldata that we just copied into memory but that doesn't really
   136              // matter because we'll be returning in a second anyway.
   137              returndatacopy(0x0, 0x0, returndatasize())
   138  
   139              // Success == 0 means a revert. We'll revert too and pass the data up.
   140              if iszero(success) { revert(0x0, returndatasize()) }
   141  
   142              // Otherwise we'll just return and pass the data up.
   143              return(0x0, returndatasize())
   144          }
   145      }
   146  
   147      /// @notice Queries the implementation address.
   148      /// @return Implementation address.
   149      function _getImplementation() internal view returns (address) {
   150          address impl;
   151          bytes32 proxyImplementation = Constants.PROXY_IMPLEMENTATION_ADDRESS;
   152          assembly {
   153              impl := sload(proxyImplementation)
   154          }
   155          return impl;
   156      }
   157  
   158      /// @notice Queries the owner of the proxy contract.
   159      /// @return Owner address.
   160      function _getAdmin() internal view returns (address) {
   161          address owner;
   162          bytes32 proxyOwner = Constants.PROXY_OWNER_ADDRESS;
   163          assembly {
   164              owner := sload(proxyOwner)
   165          }
   166          return owner;
   167      }
   168  }