mirror of https://github.com/crytic/slither
commit
c0764c9639
@ -0,0 +1,224 @@ |
||||
pragma solidity ^0.8.0; |
||||
|
||||
import "./Proxy.sol"; |
||||
import "./Address.sol"; |
||||
|
||||
/** |
||||
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an |
||||
* implementation address that can be changed. This address is stored in storage in the location specified by |
||||
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the |
||||
* implementation behind the proxy. |
||||
* |
||||
* Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see |
||||
* {TransparentUpgradeableProxy}. |
||||
*/ |
||||
contract UpgradeableProxy is Proxy { |
||||
/** |
||||
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. |
||||
* |
||||
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded |
||||
* function call, and allows initializating the storage of the proxy like a Solidity constructor. |
||||
*/ |
||||
constructor(address _logic, bytes memory _data) payable { |
||||
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); |
||||
_setImplementation(_logic); |
||||
if(_data.length > 0) { |
||||
Address.functionDelegateCall(_logic, _data); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @dev Emitted when the implementation is upgraded. |
||||
*/ |
||||
event Upgraded(address indexed implementation); |
||||
|
||||
/** |
||||
* @dev Storage slot with the address of the current implementation. |
||||
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is |
||||
* validated in the constructor. |
||||
*/ |
||||
bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; |
||||
|
||||
/** |
||||
* @dev Returns the current implementation address. |
||||
*/ |
||||
function _implementation() internal view virtual override returns (address impl) { |
||||
bytes32 slot = _IMPLEMENTATION_SLOT; |
||||
// solhint-disable-next-line no-inline-assembly |
||||
assembly { |
||||
impl := sload(slot) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @dev Upgrades the proxy to a new implementation. |
||||
* |
||||
* Emits an {Upgraded} event. |
||||
*/ |
||||
function _upgradeTo(address newImplementation) internal virtual { |
||||
_setImplementation(newImplementation); |
||||
emit Upgraded(newImplementation); |
||||
} |
||||
|
||||
/** |
||||
* @dev Stores a new address in the EIP1967 implementation slot. |
||||
*/ |
||||
function _setImplementation(address newImplementation) private { |
||||
require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract"); |
||||
|
||||
bytes32 slot = _IMPLEMENTATION_SLOT; |
||||
|
||||
// solhint-disable-next-line no-inline-assembly |
||||
assembly { |
||||
sstore(slot, newImplementation) |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* @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 interface 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) 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 admin_) { |
||||
admin_ = _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 implementation_) { |
||||
implementation_ = _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 virtual 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 virtual 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 virtual ifAdmin { |
||||
_upgradeTo(newImplementation); |
||||
Address.functionDelegateCall(newImplementation, data); |
||||
} |
||||
|
||||
/** |
||||
* @dev Returns the current admin. |
||||
*/ |
||||
function _admin() internal view virtual 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) private { |
||||
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-_beforeFallback}. |
||||
*/ |
||||
function _beforeFallback() internal virtual override { |
||||
require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); |
||||
super._beforeFallback(); |
||||
} |
||||
} |
Loading…
Reference in new issue