forked from d-xo/weird-erc20
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Proxied.sol
134 lines (113 loc) · 4.77 KB
/
Proxied.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.6.12;
/*
Provides two contracts:
1. ProxiedToken: The underlying token, state modifications must be made through a proxy
2. TokenProxy: Proxy contract, appends the original msg.sender to any calldata provided by the user
The ProxiedToken can have many trusted frontends (TokenProxy's).
The frontends should append the address of their caller to calldata when calling into the backend.
Admin users of the ProxiedToken can add new delegators.
*/
contract ProxiedToken {
// --- ERC20 Data ---
string public constant name = "Token";
string public constant symbol = "TKN";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping (address => uint) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
// --- Math ---
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
// --- Init ---
constructor(uint _totalSupply) public {
admin[msg.sender] = true;
totalSupply = _totalSupply;
balanceOf[msg.sender] = _totalSupply;
emit Transfer(address(0), msg.sender, _totalSupply);
}
// --- Access Control ---
mapping(address => bool) public admin;
function rely(address usr) external auth { admin[usr] = true; }
function deny(address usr) external auth { admin[usr] = false; }
modifier auth() { require(admin[msg.sender], "non-admin-call"); _; }
mapping(address => bool) public delegators;
modifier delegated() { require(delegators[msg.sender], "non-delegator-call"); _; }
function setDelegator(address delegator, bool status) external auth {
delegators[delegator] = status;
}
// --- Token ---
function transfer(address dst, uint wad) delegated external returns (bool) {
return _transferFrom(_getCaller(), _getCaller(), dst, wad);
}
function transferFrom(address src, address dst, uint wad) delegated external returns (bool) {
return _transferFrom(_getCaller(), src, dst, wad);
}
function approve(address usr, uint wad) delegated external returns (bool) {
return _approve(_getCaller(), usr, wad);
}
// --- Internals ---
function _transferFrom(
address caller, address src, address dst, uint wad
) internal returns (bool) {
require(balanceOf[src] >= wad, "insufficient-balance");
if (src != caller && allowance[src][caller] != type(uint).max) {
require(allowance[src][caller] >= wad, "insufficient-allowance");
allowance[src][caller] = sub(allowance[src][caller], wad);
}
balanceOf[src] = sub(balanceOf[src], wad);
balanceOf[dst] = add(balanceOf[dst], wad);
emit Transfer(src, dst, wad);
return true;
}
function _approve(address caller, address usr, uint wad) internal returns (bool) {
allowance[caller][usr] = wad;
emit Approval(caller, usr, wad);
return true;
}
// grabs the first word after the calldata and masks it with 20bytes of 1's
// to turn it into an address
function _getCaller() internal pure returns (address result) {
bytes memory array = msg.data;
uint256 index = msg.data.length;
assembly {
result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
return result;
}
}
contract TokenProxy {
address payable immutable public impl;
constructor(address _impl) public {
impl = payable(_impl);
}
receive() external payable { revert("don't send me ETH!"); }
fallback() external payable {
address _impl = impl; // pull impl onto the stack
assembly {
// get free data pointer
let ptr := mload(0x40)
// write calldata to ptr
calldatacopy(ptr, 0, calldatasize())
// store msg.sender after the calldata
mstore(add(ptr, calldatasize()), caller())
// call impl with the contents of ptr as caldata
let result := call(gas(), _impl, callvalue(), ptr, add(calldatasize(), 32), 0, 0)
// copy the returndata to ptr
let size := returndatasize()
returndatacopy(ptr, 0, size)
switch result
// revert if the call failed
case 0 { revert(ptr, size) }
// return ptr otherwise
default { return(ptr, size) }
}
}
}