// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/**
* @title OwnedUpgradeabilityProxy
* @dev This contract combines an upgradeability proxy with basic authorization control functionalities
*/
contract Proxy {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event ProxyOwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev This event will be emitted every time the implementation gets upgraded
* @param implementation representing the address of the upgraded implementation
*/
event Upgraded(address indexed implementation);
// Storage position of the address of the maintenance boolean
bytes32 private constant maintenancePosition = keccak256("com.proxy.maintenance");
// Storage position of the address of the current implementation
bytes32 private constant implementationPosition = keccak256("com.proxy.implementation");
// Storage position of the owner of the contract
bytes32 private constant proxyOwnerPosition = keccak256("com.proxy.owner");
/**
* @dev the constructor sets the original owner of the contract to the sender account.
*/
constructor() {
setUpgradeabilityOwner(msg.sender);
}
/**
* @dev Tells if contract is on maintenance
* @return _maintenance if contract is on maintenance
*/
function maintenance() public view returns (bool _maintenance) {
bytes32 position = maintenancePosition;
assembly {
_maintenance := sload(position)
}
}
/**
* @dev Sets if contract is on maintenance
*/
function setMaintenance(bool _maintenance) external onlyProxyOwner {
bytes32 position = maintenancePosition;
assembly {
sstore(position, _maintenance)
}
}
/**
* @dev Tells the address of the owner
* @return owner the address of the owner
*/
function proxyOwner() public view returns (address owner) {
bytes32 position = proxyOwnerPosition;
assembly {
owner := sload(position)
}
}
/**
* @dev Sets the address of the owner
*/
function setUpgradeabilityOwner(address newProxyOwner) internal {
bytes32 position = proxyOwnerPosition;
assembly {
sstore(position, newProxyOwner)
}
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferProxyOwnership(address newOwner) public onlyProxyOwner {
require(newOwner != address(0), 'OwnedUpgradeabilityProxy: INVALID');
emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
setUpgradeabilityOwner(newOwner);
}
/*
* @dev Allows the proxy owner to upgrade the current version of the proxy.
* @param implementation representing the address of the new implementation to be set.
*/
function upgradeTo(address newImplementation) public onlyProxyOwner {
_upgradeTo(newImplementation);
}
/*
* @dev Allows the proxy owner to upgrade the current version of the proxy and call the new implementation
* to initialize whatever is needed through a low level call.
* @param implementation representing the address of the new implementation to be set.
* @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
* signature of the implementation to be called with the needed payload
*/
function upgradeToAndCall(address newImplementation, bytes memory data) payable public onlyProxyOwner {
upgradeTo(newImplementation);
(bool success, ) = address(this).call{ value: msg.value }(data);
require(success, "OwnedUpgradeabilityProxy: INVALID");
}
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
fallback() external payable {
_fallback();
}
receive () external payable {
_fallback();
}
/**
* @dev Tells the address of the current implementation
* @return impl address of the current implementation
*/
function implementation() public view returns (address impl) {
bytes32 position = implementationPosition;
assembly {
impl := sload(position)
}
}
/**
* @dev Sets the address of the current implementation
* @param newImplementation address representing the new implementation to be set
*/
function setImplementation(address newImplementation) internal {
bytes32 position = implementationPosition;
assembly {
sstore(position, newImplementation)
}
}
/**
* @dev Upgrades the implementation address
* @param newImplementation representing the address of the new implementation to be set
*/
function _upgradeTo(address newImplementation) internal {
address currentImplementation = implementation();
require(currentImplementation != newImplementation, 'OwnedUpgradeabilityProxy: INVALID');
setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
function _fallback() internal {
if (maintenance()) {
require(msg.sender == proxyOwner(), 'OwnedUpgradeabilityProxy: FORBIDDEN');
}
address _impl = implementation();
require(_impl != address(0), 'OwnedUpgradeabilityProxy: INVALID');
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
let size := returndatasize()
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyProxyOwner() {
require(msg.sender == proxyOwner(), 'OwnedUpgradeabilityProxy: FORBIDDEN');
_;
}
}
{
"compilationTarget": {
"contracts/Proxy.sol": "Proxy"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"ProxyOwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"impl","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maintenance","outputs":[{"internalType":"bool","name":"_maintenance","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxyOwner","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_maintenance","type":"bool"}],"name":"setMaintenance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferProxyOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]