Last active
January 21, 2023 05:32
-
-
Save liamzebedee/45b8e5e93b4e07db0dc9f4273c6d69e6 to your computer and use it in GitHub Desktop.
A high-quality Ethereum proxy in under 100 lines of code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SPDX-License-Identifier: LGPL-3.0-only | |
| pragma solidity ^0.8.13; | |
| // An implementation of a proxy. | |
| // The proxy forwards all calls to the implementation. | |
| // The proxy has an admin. | |
| contract ProxyStorage { | |
| struct ProxyStore { | |
| address admin; | |
| address implementation; | |
| bool init; | |
| } | |
| bytes32 constant STORE_SLOT = bytes32(uint(keccak256("eth.nakamofo.proxy")) - 1); | |
| function _store() internal pure returns (ProxyStore storage store) { | |
| bytes32 s = STORE_SLOT; | |
| assembly { | |
| store.slot := s | |
| } | |
| } | |
| } | |
| contract Proxy is | |
| ProxyStorage | |
| { | |
| constructor(address _resolver) { | |
| _setProxyAdmin(msg.sender); | |
| } | |
| event Upgraded(address indexed implementation); | |
| event AdminChanged(address previousAdmin, address newAdmin); | |
| function proxyAdmin() public view returns (address) { | |
| return _store().admin; | |
| } | |
| function implementation() public view returns (address) { | |
| return _store().implementation; | |
| } | |
| function setProxyAdmin(address _admin) public { | |
| require(msg.sender == _store().admin, "ERR_UNAUTHORISED"); | |
| _setProxyAdmin(_admin); | |
| } | |
| function _setProxyAdmin(address _admin) internal { | |
| emit AdminChanged(_store().admin, _admin); | |
| _store().admin = _admin; | |
| } | |
| function upgrade(address _implementation) public { | |
| require(msg.sender == _store().admin, "ERR_UNAUTHORISED"); | |
| emit Upgraded(_implementation); | |
| _store().implementation = _implementation; | |
| } | |
| /// @dev Fallback function forwards all transactions and returns all received return data. | |
| function _fallback() internal { | |
| address _implementation = _store().implementation; | |
| assembly { | |
| calldatacopy(0, 0, calldatasize()) | |
| let success := delegatecall(not(0), _implementation, 0, calldatasize(), 0, 0) | |
| returndatacopy(0, 0, returndatasize()) | |
| switch success | |
| case 0 { | |
| revert(0, returndatasize()) | |
| } | |
| default { | |
| return(0, returndatasize()) | |
| } | |
| } | |
| } | |
| /** | |
| * @dev Fallback function that delegates calls to the masterCopy. | |
| * Runs when no other function in the contract matches the call data. | |
| */ | |
| fallback () external payable { | |
| _fallback(); | |
| } | |
| /** | |
| * @dev Fallback function that receives ether and delegates calls to masterCopy. | |
| * Runs when call data is empty. | |
| */ | |
| receive () external payable { | |
| _fallback(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment