mirror of
				https://github.com/OpenZeppelin/openzeppelin-contracts.git
				synced 2021-05-29 14:48:28 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			154 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			154 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| pragma solidity ^0.6.0;
 | |
| 
 | |
| import "./UpgradeableProxy.sol";
 | |
| 
 | |
| /**
 | |
|  * @dev This contract implements a proxy that is upgradeable by an admin.
 | |
|  * 
 | |
|  * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 | |
|  * clashing], which can potentially be used in an attack, this contract uses the
 | |
|  * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 | |
|  * things that go hand in hand:
 | |
|  * 
 | |
|  * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 | |
|  * that call matches one of the admin functions exposed by the proxy itself.
 | |
|  * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 | |
|  * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 | |
|  * "admin cannot fallback to proxy target".
 | |
|  * 
 | |
|  * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 | |
|  * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 | |
|  * to sudden errors when trying to call a function from the proxy implementation.
 | |
|  * 
 | |
|  * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 | |
|  * you should think of the `ProxyAdmin` instance as the real administrative inerface of your proxy.
 | |
|  */
 | |
| contract TransparentUpgradeableProxy is UpgradeableProxy {
 | |
|     /**
 | |
|      * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
 | |
|      * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
 | |
|      */
 | |
|     constructor(address _logic, address _admin, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
 | |
|         assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
 | |
|         _setAdmin(_admin);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Emitted when the admin account has changed.
 | |
|      */
 | |
|     event AdminChanged(address previousAdmin, address newAdmin);
 | |
| 
 | |
|     /**
 | |
|      * @dev Storage slot with the admin of the contract.
 | |
|      * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
 | |
|      * validated in the constructor.
 | |
|      */
 | |
|     bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
 | |
| 
 | |
|     /**
 | |
|      * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
 | |
|      */
 | |
|     modifier ifAdmin() {
 | |
|         if (msg.sender == _admin()) {
 | |
|             _;
 | |
|         } else {
 | |
|             _fallback();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Returns the current admin.
 | |
|      * 
 | |
|      * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
 | |
|      * 
 | |
|      * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
 | |
|      * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
 | |
|      * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
 | |
|      */
 | |
|     function admin() external ifAdmin returns (address) {
 | |
|         return _admin();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Returns the current implementation.
 | |
|      * 
 | |
|      * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
 | |
|      * 
 | |
|      * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
 | |
|      * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
 | |
|      * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
 | |
|      */
 | |
|     function implementation() external ifAdmin returns (address) {
 | |
|         return _implementation();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Changes the admin of the proxy.
 | |
|      * 
 | |
|      * Emits an {AdminChanged} event.
 | |
|      * 
 | |
|      * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
 | |
|      */
 | |
|     function changeAdmin(address newAdmin) external ifAdmin {
 | |
|         require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
 | |
|         emit AdminChanged(_admin(), newAdmin);
 | |
|         _setAdmin(newAdmin);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Upgrade the implementation of the proxy.
 | |
|      * 
 | |
|      * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
 | |
|      */
 | |
|     function upgradeTo(address newImplementation) external ifAdmin {
 | |
|         _upgradeTo(newImplementation);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
 | |
|      * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
 | |
|      * proxied contract.
 | |
|      * 
 | |
|      * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
 | |
|      */
 | |
|     function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
 | |
|         _upgradeTo(newImplementation);
 | |
|         // solhint-disable-next-line avoid-low-level-calls
 | |
|         (bool success,) = newImplementation.delegatecall(data);
 | |
|         require(success);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Returns the current admin.
 | |
|      */
 | |
|     function _admin() internal view returns (address adm) {
 | |
|         bytes32 slot = _ADMIN_SLOT;
 | |
|         // solhint-disable-next-line no-inline-assembly
 | |
|         assembly {
 | |
|             adm := sload(slot)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Stores a new address in the EIP1967 admin slot.
 | |
|      */
 | |
|     function _setAdmin(address newAdmin) internal {
 | |
|         bytes32 slot = _ADMIN_SLOT;
 | |
| 
 | |
|         // solhint-disable-next-line no-inline-assembly
 | |
|         assembly {
 | |
|             sstore(slot, newAdmin)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_willFallback}.
 | |
|      */
 | |
|     function _willFallback() internal override virtual {
 | |
|         require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
 | |
|         super._willFallback();
 | |
|     }
 | |
| }
 | 
