mirror of
https://github.com/autistic-symposium/web3-starter-sol.git
synced 2025-07-22 22:50:38 -04:00
Clean up
This commit is contained in:
parent
c2d0866191
commit
db37d209ab
58 changed files with 4663 additions and 1043 deletions
5
advanced_knowledge/proxies/README.md
Normal file
5
advanced_knowledge/proxies/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
## a place to dump proxy examples
|
||||
|
||||
<br>
|
||||
|
||||
* disclaimer: these boilerplates might not be my code, but rather well-known references.
|
64
advanced_knowledge/proxies/deploy.sol
Normal file
64
advanced_knowledge/proxies/deploy.sol
Normal file
|
@ -0,0 +1,64 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
contract Proxy {
|
||||
event Deploy(address);
|
||||
|
||||
receive() external payable {}
|
||||
|
||||
function deploy(bytes memory _code) external payable returns (address addr) {
|
||||
assembly {
|
||||
// create(v, p, n)
|
||||
// v = amount of ETH to send
|
||||
// p = pointer in memory to start of code
|
||||
// n = size of code
|
||||
addr := create(callvalue(), add(_code, 0x20), mload(_code))
|
||||
}
|
||||
// return address 0 on error
|
||||
require(addr != address(0), "deploy failed");
|
||||
|
||||
emit Deploy(addr);
|
||||
}
|
||||
|
||||
function execute(address _target, bytes memory _data) external payable {
|
||||
(bool success, ) = _target.call{value: msg.value}(_data);
|
||||
require(success, "failed");
|
||||
}
|
||||
}
|
||||
|
||||
contract TestContract1 {
|
||||
address public owner = msg.sender;
|
||||
|
||||
function setOwner(address _owner) public {
|
||||
require(msg.sender == owner, "not owner");
|
||||
owner = _owner;
|
||||
}
|
||||
}
|
||||
|
||||
contract TestContract2 {
|
||||
address public owner = msg.sender;
|
||||
uint public value = msg.value;
|
||||
uint public x;
|
||||
uint public y;
|
||||
|
||||
constructor(uint _x, uint _y) payable {
|
||||
x = _x;
|
||||
y = _y;
|
||||
}
|
||||
}
|
||||
|
||||
contract Helper {
|
||||
function getBytecode1() external pure returns (bytes memory) {
|
||||
bytes memory bytecode = type(TestContract1).creationCode;
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
function getBytecode2(uint _x, uint _y) external pure returns (bytes memory) {
|
||||
bytes memory bytecode = type(TestContract2).creationCode;
|
||||
return abi.encodePacked(bytecode, abi.encode(_x, _y));
|
||||
}
|
||||
|
||||
function getCalldata(address _owner) external pure returns (bytes memory) {
|
||||
return abi.encodeWithSignature("setOwner(address)", _owner);
|
||||
}
|
||||
}
|
70
advanced_knowledge/proxies/minimum_proxy.sol
Normal file
70
advanced_knowledge/proxies/minimum_proxy.sol
Normal file
|
@ -0,0 +1,70 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
|
||||
|
||||
contract MinimalProxy {
|
||||
function clone(address target) external returns (address result) {
|
||||
// convert address to 20 bytes
|
||||
bytes20 targetBytes = bytes20(target);
|
||||
|
||||
// actual code //
|
||||
// 3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3
|
||||
|
||||
// creation code //
|
||||
// copy runtime code into memory and return it
|
||||
// 3d602d80600a3d3981f3
|
||||
|
||||
// runtime code //
|
||||
// code to delegatecall to address
|
||||
// 363d3d373d3d3d363d73 address 5af43d82803e903d91602b57fd5bf3
|
||||
|
||||
assembly {
|
||||
/*
|
||||
reads the 32 bytes of memory starting at pointer stored in 0x40
|
||||
|
||||
In solidity, the 0x40 slot in memory is special: it contains the "free memory pointer"
|
||||
which points to the end of the currently allocated memory.
|
||||
*/
|
||||
let clone := mload(0x40)
|
||||
// store 32 bytes to memory starting at "clone"
|
||||
mstore(
|
||||
clone,
|
||||
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
|
||||
)
|
||||
|
||||
/*
|
||||
| 20 bytes |
|
||||
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
|
||||
^
|
||||
pointer
|
||||
*/
|
||||
// store 32 bytes to memory starting at "clone" + 20 bytes
|
||||
// 0x14 = 20
|
||||
mstore(add(clone, 0x14), targetBytes)
|
||||
|
||||
/*
|
||||
| 20 bytes | 20 bytes |
|
||||
0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe
|
||||
^
|
||||
pointer
|
||||
*/
|
||||
// store 32 bytes to memory starting at "clone" + 40 bytes
|
||||
// 0x28 = 40
|
||||
mstore(
|
||||
add(clone, 0x28),
|
||||
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
|
||||
)
|
||||
|
||||
/*
|
||||
| 20 bytes | 20 bytes | 15 bytes |
|
||||
0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3
|
||||
*/
|
||||
// create new contract
|
||||
// send 0 Ether
|
||||
// code starts at pointer stored in "clone"
|
||||
// code size 0x37 (55 bytes)
|
||||
result := create(0, clone, 0x37)
|
||||
}
|
||||
}
|
||||
}
|
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