Skip to content

Instantly share code, notes, and snippets.

@td-bn
Created December 13, 2021 12:34
Show Gist options
  • Save td-bn/d455fb780991c239bd26e80436c75833 to your computer and use it in GitHub Desktop.
Save td-bn/d455fb780991c239bd26e80436c75833 to your computer and use it in GitHub Desktop.
Simple Proxy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Registry.sol";
contract Logic {
bytes32 constant SUPEROWNER = keccak256("superowner");
bytes32 constant OWNER = keccak256("owner");
uint public value;
address public registry;
bool locked = false;
modifier onlyOwner() {
require(Registry(registry).hasRole(OWNER, msg.sender), "!owner");
_;
}
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function initialize(address _reg) public {
registry = _reg;
}
function setValue(uint _val) public onlyOwner nonReentrant {
value += _val;
}
function getValue() public onlyOwner nonReentrant returns (uint) {
return value;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Registry.sol";
contract Logic2 {
bytes32 constant SUPEROWNER = keccak256("superowner");
bytes32 constant OWNER = keccak256("owner");
uint public value;
address public registry;
bool locked = false;
event SetValue(uint);
event GotValue(uint);
modifier onlyOwner() {
require(Registry(registry).hasRole(OWNER, msg.sender), "!owner");
_;
}
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function initialize(address _reg) public {
registry = _reg;
}
function setValue(uint _val) public onlyOwner nonReentrant {
value += _val;
emit SetValue(value);
}
function getValue() public onlyOwner nonReentrant returns (uint) {
emit GotValue(value);
return value;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// For debugging
import "hardhat/console.sol";
import "./Registry.sol";
contract Proxy {
bytes32 constant IMPL_SLOT = keccak256("my.impl.slot");
bytes32 constant SUPEROWNER = keccak256("superowner");
constructor(address impl, address reg) {
(bool success, ) = impl.delegatecall(
abi.encodeWithSignature("initialize(address)", reg)
);
require(success, "Failed to initialize");
_setImpl(impl);
}
function changeImpl(address _newImpl) public {
address reg;
assembly {
reg := sload(1)
}
require(Registry(reg).hasRole(SUPEROWNER, msg.sender), "!authorized");
_setImpl(_newImpl);
}
function getImpl() public view returns (address) {
bytes32 slot = IMPL_SLOT;
address impl;
assembly {
impl := sload(slot)
}
return impl;
}
function _setImpl(address impl) internal {
bytes32 slot = IMPL_SLOT;
assembly {
sstore(slot, impl)
}
}
fallback() external payable {
address implementation = getImpl();
console.log("implementation:", implementation);
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
receive() external payable {
}
// For help with debugging
function keccak(string memory str) public pure returns (bytes32) {
return keccak256(abi.encodePacked(str));
}
function getStorageAtSlot(uint i) public view returns (bytes32) {
bytes32 data;
assembly {
data := sload(i)
}
return data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.4.0/contracts/access/AccessControl.sol";
// For debugging
import "hardhat/console.sol";
contract Registry is AccessControl {
bytes32 constant SUPEROWNER = keccak256("superowner");
bytes32 constant OWNER = keccak256("owner");
constructor() {
_grantRole(SUPEROWNER, msg.sender);
}
function addOwner(address _owner) public {
require(hasRole(SUPEROWNER, msg.sender), "!authorized");
_grantRole(OWNER, _owner);
}
function removeOwner(address _owner) public {
require(hasRole(SUPEROWNER, msg.sender), "!authorized");
_revokeRole(OWNER, _owner);
}
function transferOwner(address _newOwner) public {
require(hasRole(OWNER, msg.sender), "!authorized");
_revokeRole(OWNER, msg.sender);
_grantRole(OWNER, _newOwner);
}
function renounceOwner() public {
require(hasRole(OWNER, msg.sender), "!authorized");
_revokeRole(OWNER, msg.sender);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// For debugging
import "hardhat/console.sol";
contract Test {
address public proxy;
function setProxy(address _proxy) public {
proxy = _proxy;
}
function testSetValue(uint _val) public {
(bool success,) = proxy.call(
abi.encodeWithSignature("setValue(uint256)", _val)
);
require(success, "Call through proxy failed!");
}
function testGetValue() public returns(uint) {
(bool success, bytes memory data) = proxy.call(
abi.encodeWithSignature("getValue()")
);
require(success, "Call through proxy failed!");
uint ret;
(ret) = abi.decode(data, (uint256));
return ret;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment