mirror of
https://github.com/autistic-symposium/web3-starter-sol.git
synced 2025-07-22 14:40:35 -04:00
Clean up
This commit is contained in:
parent
c2d0866191
commit
db37d209ab
58 changed files with 4663 additions and 1043 deletions
236
advanced_knowledge/proxies/upgradeable_proxy.sol
Normal file
236
advanced_knowledge/proxies/upgradeable_proxy.sol
Normal file
|
@ -0,0 +1,236 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
// Transparent upgradeable proxy pattern
|
||||
|
||||
contract CounterV1 {
|
||||
uint public count;
|
||||
|
||||
function inc() external {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
contract CounterV2 {
|
||||
uint public count;
|
||||
|
||||
function inc() external {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
function dec() external {
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
contract BuggyProxy {
|
||||
address public implementation;
|
||||
address public admin;
|
||||
|
||||
constructor() {
|
||||
admin = msg.sender;
|
||||
}
|
||||
|
||||
function _delegate() private {
|
||||
(bool ok, ) = implementation.delegatecall(msg.data);
|
||||
require(ok, "delegatecall failed");
|
||||
}
|
||||
|
||||
fallback() external payable {
|
||||
_delegate();
|
||||
}
|
||||
|
||||
receive() external payable {
|
||||
_delegate();
|
||||
}
|
||||
|
||||
function upgradeTo(address _implementation) external {
|
||||
require(msg.sender == admin, "not authorized");
|
||||
implementation = _implementation;
|
||||
}
|
||||
}
|
||||
|
||||
contract Dev {
|
||||
function selectors() external view returns (bytes4, bytes4, bytes4) {
|
||||
return (
|
||||
Proxy.admin.selector,
|
||||
Proxy.implementation.selector,
|
||||
Proxy.upgradeTo.selector
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
contract Proxy {
|
||||
// All functions / variables should be private, forward all calls to fallback
|
||||
|
||||
// -1 for unknown preimage
|
||||
// 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
|
||||
bytes32 private constant IMPLEMENTATION_SLOT =
|
||||
bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1);
|
||||
// 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
|
||||
bytes32 private constant ADMIN_SLOT =
|
||||
bytes32(uint(keccak256("eip1967.proxy.admin")) - 1);
|
||||
|
||||
constructor() {
|
||||
_setAdmin(msg.sender);
|
||||
}
|
||||
|
||||
modifier ifAdmin() {
|
||||
if (msg.sender == _getAdmin()) {
|
||||
_;
|
||||
} else {
|
||||
_fallback();
|
||||
}
|
||||
}
|
||||
|
||||
function _getAdmin() private view returns (address) {
|
||||
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
|
||||
}
|
||||
|
||||
function _setAdmin(address _admin) private {
|
||||
require(_admin != address(0), "admin = zero address");
|
||||
StorageSlot.getAddressSlot(ADMIN_SLOT).value = _admin;
|
||||
}
|
||||
|
||||
function _getImplementation() private view returns (address) {
|
||||
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
|
||||
}
|
||||
|
||||
function _setImplementation(address _implementation) private {
|
||||
require(_implementation.code.length > 0, "implementation is not contract");
|
||||
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = _implementation;
|
||||
}
|
||||
|
||||
// Admin interface //
|
||||
function changeAdmin(address _admin) external ifAdmin {
|
||||
_setAdmin(_admin);
|
||||
}
|
||||
|
||||
// 0x3659cfe6
|
||||
function upgradeTo(address _implementation) external ifAdmin {
|
||||
_setImplementation(_implementation);
|
||||
}
|
||||
|
||||
// 0xf851a440
|
||||
function admin() external ifAdmin returns (address) {
|
||||
return _getAdmin();
|
||||
}
|
||||
|
||||
// 0x5c60da1b
|
||||
function implementation() external ifAdmin returns (address) {
|
||||
return _getImplementation();
|
||||
}
|
||||
|
||||
// User interface //
|
||||
function _delegate(address _implementation) internal virtual {
|
||||
assembly {
|
||||
// Copy msg.data. We take full control of memory in this inline assembly
|
||||
// block because it will not return to Solidity code. We overwrite the
|
||||
// Solidity scratch pad at memory position 0.
|
||||
|
||||
// calldatacopy(t, f, s) - copy s bytes from calldata at position f to mem at position t
|
||||
// calldatasize() - size of call data in bytes
|
||||
calldatacopy(0, 0, calldatasize())
|
||||
|
||||
// Call the implementation.
|
||||
// out and outsize are 0 because we don't know the size yet.
|
||||
|
||||
// delegatecall(g, a, in, insize, out, outsize) -
|
||||
// - call contract at address a
|
||||
// - with input mem[in…(in+insize))
|
||||
// - providing g gas
|
||||
// - and output area mem[out…(out+outsize))
|
||||
// - returning 0 on error (eg. out of gas) and 1 on success
|
||||
let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0)
|
||||
|
||||
// Copy the returned data.
|
||||
// returndatacopy(t, f, s) - copy s bytes from returndata at position f to mem at position t
|
||||
// returndatasize() - size of the last returndata
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
|
||||
switch result
|
||||
// delegatecall returns 0 on error.
|
||||
case 0 {
|
||||
// revert(p, s) - end execution, revert state changes, return data mem[p…(p+s))
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
default {
|
||||
// return(p, s) - end execution, return data mem[p…(p+s))
|
||||
return(0, returndatasize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _fallback() private {
|
||||
_delegate(_getImplementation());
|
||||
}
|
||||
|
||||
fallback() external payable {
|
||||
_fallback();
|
||||
}
|
||||
|
||||
receive() external payable {
|
||||
_fallback();
|
||||
}
|
||||
}
|
||||
|
||||
contract ProxyAdmin {
|
||||
address public owner;
|
||||
|
||||
constructor() {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
modifier onlyOwner() {
|
||||
require(msg.sender == owner, "not owner");
|
||||
_;
|
||||
}
|
||||
|
||||
function getProxyAdmin(address proxy) external view returns (address) {
|
||||
(bool ok, bytes memory res) = proxy.staticcall(abi.encodeCall(Proxy.admin, ()));
|
||||
require(ok, "call failed");
|
||||
return abi.decode(res, (address));
|
||||
}
|
||||
|
||||
function getProxyImplementation(address proxy) external view returns (address) {
|
||||
(bool ok, bytes memory res) = proxy.staticcall(
|
||||
abi.encodeCall(Proxy.implementation, ())
|
||||
);
|
||||
require(ok, "call failed");
|
||||
return abi.decode(res, (address));
|
||||
}
|
||||
|
||||
function changeProxyAdmin(address payable proxy, address admin) external onlyOwner {
|
||||
Proxy(proxy).changeAdmin(admin);
|
||||
}
|
||||
|
||||
function upgrade(address payable proxy, address implementation) external onlyOwner {
|
||||
Proxy(proxy).upgradeTo(implementation);
|
||||
}
|
||||
}
|
||||
|
||||
library StorageSlot {
|
||||
struct AddressSlot {
|
||||
address value;
|
||||
}
|
||||
|
||||
function getAddressSlot(
|
||||
bytes32 slot
|
||||
) internal pure returns (AddressSlot storage r) {
|
||||
assembly {
|
||||
r.slot := slot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract TestSlot {
|
||||
bytes32 public constant slot = keccak256("TEST_SLOT");
|
||||
|
||||
function getSlot() external view returns (address) {
|
||||
return StorageSlot.getAddressSlot(slot).value;
|
||||
}
|
||||
|
||||
function writeSlot(address _addr) external {
|
||||
StorageSlot.getAddressSlot(slot).value = _addr;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue