Skip to content

Commit

Permalink
feat: transparent proxy and proxy admin base
Browse files Browse the repository at this point in the history
  • Loading branch information
seinmyung25 committed Dec 27, 2023
1 parent 45e4b91 commit b82d587
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 0 deletions.
66 changes: 66 additions & 0 deletions contracts/proxy/admin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/ProxyAdmin.sol)

pragma solidity ^0.8.20;

import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import { IEcoOwnable, EcoOwnable } from "../access/EcoOwnable.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

interface IEcoProxyAdmin {
function initEcoProxyAdmin(address initialOwner) external ;
}

/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract EcoProxyAdmin is IEcoProxyAdmin, EcoOwnable {
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
* and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

/**
* @dev Sets the initial owner who can perform upgrades.
*/
constructor(address initialOwner) {
initEcoProxyAdmin(initialOwner);
}

function initEcoProxyAdmin(address initialOwner) public override initializer {
initEcoOwnable(initialOwner);
}

/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
* See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
* - If `data` is empty, `msg.value` must be zero.
*/
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}

contract EcoProxyForProxyAdmin is ERC1967Proxy {
constructor(address proxyAdminLogic, address initialOwner)
ERC1967Proxy(
proxyAdminLogic,
abi.encodeWithSelector(IEcoProxyAdmin.initEcoProxyAdmin.selector, initialOwner)
)
{}
}
79 changes: 79 additions & 0 deletions contracts/proxy/transparent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import { EcoProxyForProxyAdmin } from "./admin.sol";

// EcoTransparentUpgradeableProxy
contract EcoTUPWithAdmin is ERC1967Proxy {
// An immutable address for the admin to avoid unnecessary SLOADs before each call
// at the expense of removing the ability to change the admin once it's set.
// This is acceptable if the admin is always a ProxyAdmin instance or similar contract
// with its own ability to transfer the permissions to another account.
address private immutable _admin;

/**
* @dev The proxy caller is the current admin, and can't fallback to the proxy target.
*/
error ProxyDeniedAdminAccess();

/**
* @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
* backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
* {ERC1967Proxy-constructor}.
*/
constructor(address proxyAdmin, address _logic, bytes memory _data) payable
ERC1967Proxy(_logic, _data) {
_admin = proxyAdmin;
// Set the storage value and emit an event for ERC-1967 compatibility
ERC1967Utils.changeAdmin(_proxyAdmin());
}

/**
* @dev Returns the admin of this proxy.
*/
function _proxyAdmin() internal virtual returns (address) {
return _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 == _proxyAdmin()) {
if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
revert ProxyDeniedAdminAccess();
} else {
_dispatchUpgradeToAndCall();
}
} else {
super._fallback();
}
}

/**
* @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
function _dispatchUpgradeToAndCall() private {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
ERC1967Utils.upgradeToAndCall(newImplementation, data);
}
}

contract EcoTUPWithAdminLogic is EcoTUPWithAdmin {
constructor(address proxyAdminLogic, address _logic, bytes memory _data) payable
EcoTUPWithAdmin(
address(new EcoProxyForProxyAdmin(proxyAdminLogic, msg.sender)),
_logic,
_data
) {}
}

0 comments on commit b82d587

Please sign in to comment.