Skip to content

Commit

Permalink
Make ERC1967Upgrades a library (instead of an abstract contract)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx committed Jun 7, 2023
1 parent 11d6544 commit 33f0adc
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 47 deletions.
8 changes: 4 additions & 4 deletions contracts/mocks/proxy/UUPSLegacy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ contract UUPSUpgradeableLegacyMock is UUPSUpgradeableMock {
// An extra underscore prevents a name clash error.
function __setImplementation(address newImplementation) private {
require(newImplementation.code.length > 0, "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
StorageSlot.getAddressSlot(ERC1967Upgrade.IMPLEMENTATION_SLOT).value = newImplementation;
}

function _upgradeToAndCallSecureLegacyV1(address newImplementation, bytes memory data, bool forceCall) internal {
address oldImplementation = _getImplementation();
address oldImplementation = ERC1967Upgrade.getImplementation();

// Initial upgrade and setup call
__setImplementation(newImplementation);
Expand All @@ -37,9 +37,9 @@ contract UUPSUpgradeableLegacyMock is UUPSUpgradeableMock {
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
require(oldImplementation == ERC1967Upgrade.getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_upgradeTo(newImplementation);
ERC1967Upgrade.upgradeTo(newImplementation);
}
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/mocks/proxy/UUPSUpgradeableMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ contract UUPSUpgradeableMock is NonUpgradeableMock, UUPSUpgradeable {

contract UUPSUpgradeableUnsafeMock is UUPSUpgradeableMock {
function upgradeTo(address newImplementation) public override {
ERC1967Upgrade._upgradeToAndCall(newImplementation, bytes(""), false);
ERC1967Upgrade.upgradeToAndCall(newImplementation, bytes(""), false);
}

function upgradeToAndCall(address newImplementation, bytes memory data) public payable override {
ERC1967Upgrade._upgradeToAndCall(newImplementation, data, false);
ERC1967Upgrade.upgradeToAndCall(newImplementation, data, false);
}
}
6 changes: 3 additions & 3 deletions contracts/proxy/ERC1967/ERC1967Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import "./ERC1967Upgrade.sol";
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
contract ERC1967Proxy 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 initializing the storage of the proxy like a Solidity constructor.
*/
constructor(address _logic, bytes memory _data) payable {
_upgradeToAndCall(_logic, _data, false);
ERC1967Upgrade.upgradeToAndCall(_logic, _data, false);
}

/**
Expand All @@ -31,6 +31,6 @@ contract ERC1967Proxy is Proxy, ERC1967Upgrade {
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function _implementation() internal view virtual override returns (address impl) {
return ERC1967Upgrade._getImplementation();
return ERC1967Upgrade.getImplementation();
}
}
65 changes: 41 additions & 24 deletions contracts/proxy/ERC1967/ERC1967Upgrade.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,24 @@ import "../../utils/StorageSlot.sol";
*
* _Available since v4.1._
*/
abstract contract ERC1967Upgrade is IERC1967 {
library ERC1967Upgrade {
// =================== BEGIN --- COPIED FROM IERC1967.sol --- REMOVAL REQUIRES SOLIDITY 0.8.21 ====================
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);

/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);

/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
// ==================== END --- COPIED FROM IERC1967.sol --- REMOVAL REQUIRES SOLIDITY 0.8.21 =====================

// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

Expand All @@ -24,40 +41,40 @@ abstract contract ERC1967Upgrade is IERC1967 {
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}

/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(newImplementation.code.length > 0, "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}

/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
function upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
emit /*IERC1967.*/Upgraded(newImplementation);
}

/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
_upgradeTo(newImplementation);
function upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
Expand All @@ -68,19 +85,19 @@ abstract contract ERC1967Upgrade is IERC1967 {
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
function upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
// Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// this special case will break upgrade paths from old UUPS implementation to new ones.
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
require(slot == IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
upgradeToAndCall(newImplementation, data, forceCall);
}
}

Expand All @@ -89,7 +106,7 @@ abstract contract ERC1967Upgrade is IERC1967 {
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

/**
* @dev Returns the current admin.
Expand All @@ -98,39 +115,39 @@ abstract contract ERC1967Upgrade is IERC1967 {
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}

/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}

/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
function changeAdmin(address newAdmin) internal {
emit /*IERC1967.*/AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}

/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}

/**
Expand All @@ -142,7 +159,7 @@ abstract contract ERC1967Upgrade is IERC1967 {
IBeacon(newBeacon).implementation().code.length > 0,
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
}

/**
Expand All @@ -151,9 +168,9 @@ abstract contract ERC1967Upgrade is IERC1967 {
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
function upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
emit /*IERC1967.*/BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
Expand Down
6 changes: 3 additions & 3 deletions contracts/proxy/beacon/BeaconProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import "../ERC1967/ERC1967Upgrade.sol";
*
* _Available since v3.4._
*/
contract BeaconProxy is Proxy, ERC1967Upgrade {
contract BeaconProxy is Proxy {
/**
* @dev Initializes the proxy with `beacon`.
*
Expand All @@ -28,13 +28,13 @@ contract BeaconProxy is Proxy, ERC1967Upgrade {
* - `beacon` must be a contract with the interface {IBeacon}.
*/
constructor(address beacon, bytes memory data) payable {
_upgradeBeaconToAndCall(beacon, data, false);
ERC1967Upgrade.upgradeBeaconToAndCall(beacon, data, false);
}

/**
* @dev Returns the current implementation address of the associated beacon.
*/
function _implementation() internal view virtual override returns (address) {
return IBeacon(_getBeacon()).implementation();
return IBeacon(ERC1967Upgrade.getBeacon()).implementation();
}
}
10 changes: 5 additions & 5 deletions contracts/proxy/transparent/TransparentUpgradeableProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
* optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
*/
constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
_changeAdmin(admin_);
ERC1967Upgrade.changeAdmin(admin_);
}

/**
* @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior
*/
function _fallback() internal virtual override {
if (msg.sender == _getAdmin()) {
if (msg.sender == ERC1967Upgrade.getAdmin()) {
bytes memory ret;
bytes4 selector = msg.sig;
if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) {
Expand Down Expand Up @@ -93,7 +93,7 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
_requireZeroValue();

address newAdmin = abi.decode(msg.data[4:], (address));
_changeAdmin(newAdmin);
ERC1967Upgrade.changeAdmin(newAdmin);

return "";
}
Expand All @@ -105,7 +105,7 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
_requireZeroValue();

address newImplementation = abi.decode(msg.data[4:], (address));
_upgradeToAndCall(newImplementation, bytes(""), false);
ERC1967Upgrade.upgradeToAndCall(newImplementation, bytes(""), false);

return "";
}
Expand All @@ -117,7 +117,7 @@ contract TransparentUpgradeableProxy is ERC1967Proxy {
*/
function _dispatchUpgradeToAndCall() private returns (bytes memory) {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
_upgradeToAndCall(newImplementation, data, true);
ERC1967Upgrade.upgradeToAndCall(newImplementation, data, true);

return "";
}
Expand Down
10 changes: 5 additions & 5 deletions contracts/proxy/utils/UUPSUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import "../ERC1967/ERC1967Upgrade.sol";
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
abstract contract UUPSUpgradeable is IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);

Expand All @@ -31,7 +31,7 @@ abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
*/
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
require(ERC1967Upgrade.getImplementation() == __self, "Function must be called through active proxy");
_;
}

Expand All @@ -53,7 +53,7 @@ abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
return ERC1967Upgrade.IMPLEMENTATION_SLOT;
}

/**
Expand All @@ -67,7 +67,7 @@ abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
*/
function upgradeTo(address newImplementation) public virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
ERC1967Upgrade.upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}

/**
Expand All @@ -82,7 +82,7 @@ abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data, true);
ERC1967Upgrade.upgradeToAndCallUUPS(newImplementation, data, true);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const argv = require('yargs/yargs')()
compiler: {
alias: 'compileVersion',
type: 'string',
default: '0.8.13',
default: '0.8.20',
},
coinmarketcap: {
alias: 'coinmarketcapApiKey',
Expand Down

0 comments on commit 33f0adc

Please sign in to comment.