Skip to content

Instantly share code, notes, and snippets.

@td-bn
Last active November 4, 2021 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save td-bn/2849d9a09aa997989e838e3b269144e9 to your computer and use it in GitHub Desktop.
Save td-bn/2849d9a09aa997989e838e3b269144e9 to your computer and use it in GitHub Desktop.
Proxy contracts gist
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract Logic {
event Added(uint256 result);
event Fallback();
function add(uint256 a, uint256 b) external returns (uint256 result) {
result = a +b;
emit Added(result);
}
fallback() external {
emit Fallback();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
abstract contract Proxy {
fallback() external payable virtual {
_fallback();
}
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
function _delegate(address implementation) internal virtual {
assembly {
calldatacopy(0, 0, calldatasize())
// Call the implementation.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
function _implementation() internal view virtual returns(address);
function _beforeFallback() internal virtual {
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/access/Ownable.sol";
import "./TransparentUpgradableProxy.sol";
contract ProxyAdmin is Ownable {
function getProxyImplementation(TransparentUpgradableProxy proxy) public view returns (address) {
// bytes4(keccak256("implementation()")) == 0x5c60da1b
(bool success, bytes memory returnData) = address(proxy).staticcall(hex"5c60da1b");
require(success);
return abi.decode(returnData, (address));
}
function getProxyAdmin(TransparentUpgradableProxy proxy) public view returns (address) {
// bytes4(keccak256("admin()")) == 0xf851a440
(bool success, bytes memory returnData) = address(proxy).staticcall(hex"f851a440");
require(success);
return abi.decode(returnData, (address));
}
function changeProxyAdmin(TransparentUpgradableProxy proxy, address newAdmin) public virtual onlyOwner {
proxy.changeAdmin(newAdmin);
}
function upgrade(TransparentUpgradableProxy proxy, address implementation) public virtual onlyOwner {
proxy.upgradeTo(implementation);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract Test {
address proxy;
constructor(address _proxy) {
proxy = _proxy;
}
function test() external returns (bytes memory) {
bytes memory payload = abi.encodeWithSignature("add(uint256,uint256)", 30, 12);
(bool success, bytes memory returnData) = proxy.call(payload);
require(success, "call to proxy failed");
return returnData;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./UpgradableProxy.sol";
contract TransparentUpgradableProxy is UpgradableProxy {
bytes32 constant ADMIN_SLOT = keccak256("leave.me.alone.slot");
event AdminChanged(address previousAdmin, address newAdmin);
constructor(address _logic, address admin_) UpgradableProxy(_logic) {
_setAdmin(admin_);
}
modifier isAdmin() {
if (msg.sender == _admin()) {
_;
} else {
_fallback();
}
}
function admin() external isAdmin returns (address admin_) {
admin_ = _admin();
}
function implementation() external isAdmin returns(address implementation_) {
implementation_ = _implementation();
}
function upgradeTo(address newImplementation) external isAdmin {
_upgradeTo(newImplementation);
}
function changeAdmin(address newAdmin) external virtual isAdmin {
require(newAdmin != address(0), "TransparentUpgradableProxy: new admin is address 0");
emit AdminChanged(_admin(), newAdmin);
_setAdmin(newAdmin);
}
function _setAdmin(address newAdmin) private {
bytes32 slot = ADMIN_SLOT;
assembly {
sstore(slot, newAdmin)
}
}
function _admin() internal view virtual returns(address admin) {
bytes32 slot = ADMIN_SLOT;
assembly {
admin := sload(slot)
}
}
function _beforeFallback() internal virtual override {
require(msg.sender != _admin(), "Admin cannot fallback to proxy target");
super._beforeFallback();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/Address.sol";
import "./Proxy.sol";
contract UpgradableProxy is Proxy {
bytes32 constant IMPLEMENTATION_SLOT = keccak256("proxy.upgradable.pattern.test.mine");
event Upgraded(address indexed implementation);
constructor(address _logic) {
_setImplementation(_logic);
}
function getImplementation() public view returns (address) {
return _implementation();
}
function _implementation() internal view override returns (address) {
address impl;
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
impl := sload(slot)
}
return impl;
}
function _upgradeTo(address newImplementation) internal virtual {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "address not a contract");
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
}
function _beforeFallback() internal virtual override {
super._beforeFallback();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/solc-0.7/contracts/math/SafeMath.sol";
contract UpgradedLogic {
address proxy;
using SafeMath for uint256;
event AddedSafely(uint256 result);
event Fallback();
function add(uint256 a, uint256 b) external returns (uint256 result) {
result = a.add(b);
emit AddedSafely(result);
}
fallback() external {
emit Fallback();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment