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 }