-
Notifications
You must be signed in to change notification settings - Fork 4
/
ERC20.sol
338 lines (278 loc) · 11.2 KB
/
ERC20.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.25;
// ## ERC20 Type Wrapper
//
// This type wraps the address primitive type and contains functions to call the core ERC20 interface
// without allocating new memory. Functions perform returndata validation.
//
// All external calls that fail will revert internally. This is to simplify the API.
type ERC20 is address;
using {
totalSupply,
balanceOf,
allowance,
transfer,
transferFrom,
approve,
// -- operators
eq as ==,
neq as !=,
gt as >,
gte as >=,
lt as <,
lte as <=,
add as +,
sub as -,
mul as *,
div as /,
mod as %,
and as &,
or as |,
xor as ^,
not as ~
} for ERC20 global;
// -------------------------------------------------------------------------------------------------
// Query ERC20.totalSupply without allocating new memory.
//
// Procedures:
// 01. store totalSupply selector in memory
// 02. staticcall totalSupply; cache as ok
// 03. check that the return value is 32 bytes; compose with ok
// 04. if ok is false, revert
// 05. assign returned value to output
function totalSupply(ERC20 erc20) view returns (uint256 output) {
assembly ("memory-safe") {
mstore(0x00, 0x18160ddd00000000000000000000000000000000000000000000000000000000)
let ok := staticcall(gas(), erc20, 0x00, 0x04, 0x00, 0x20)
ok := and(ok, eq(returndatasize(), 0x20))
if iszero(ok) { revert(0x00, 0x00) }
output := mload(0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Query ERC20.balanceOf without allocating new memory.
//
// Procedures:
// 01. store balanceOf selector in memory
// 02. store owner address in memory
// 03. staticcall balanceOf; cache as ok
// 04. check that the return value is 32 bytes; compose with ok
// 05. if ok is false, revert
// 06. assign returned value to output
function balanceOf(ERC20 erc20, address owner) view returns (uint256 output) {
assembly ("memory-safe") {
mstore(0x00, 0x70a0823100000000000000000000000000000000000000000000000000000000)
mstore(0x04, owner)
let ok := staticcall(gas(), erc20, 0x00, 0x24, 0x00, 0x20)
ok := and(ok, eq(returndatasize(), 0x20))
if iszero(ok) { revert(0x00, 0x00) }
output := mload(0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Query ERC20.allowance without allocating new memory.
//
// Procedures:
// 01. store allowance selector in memory
// 02. store owner address in memory
// 03. store spender address in memory
// 04. staticcall allowance; cache as ok
// 05. check that the return value is 32 bytes; compose with ok
// 06. if ok is false, revert
// 07. restore the upper bits of the free memory pointer to zero
function allowance(ERC20 erc20, address owner, address spender) view returns (uint256 output) {
assembly {
mstore(0x00, 0xdd62ed3e00000000000000000000000000000000000000000000000000000000)
mstore(0x04, owner)
mstore(0x24, spender)
let ok := staticcall(gas(), erc20, 0x00, 0x44, 0x00, 0x20)
ok := and(ok, eq(returndatasize(), 0x20))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x24, 0x00)
output := mload(0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Call ERC20.transfer without allocating new memory.
//
// Procedures:
// 01. store transfer selector in memory
// 02. store recipient address in memory
// 03. store amount in memory
// 04. call transfer; cache as ok
// 05. check that either no data was returned or the data is 32 bytes and true; compose with ok
// 06. revert if ok is false
// 07. restore the upper bits of the free memory pointer to zero
function transfer(ERC20 erc20, address receiver, uint256 amount) {
assembly {
mstore(0x00, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(0x04, receiver)
mstore(0x24, amount)
let ok := call(gas(), erc20, 0x00, 0x00, 0x44, 0x00, 0x20)
ok := and(ok, or(iszero(returndatasize()), and(eq(returndatasize(), 0x20), mload(0x00))))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x24, 0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Call ERC20.transferFrom without allocating new memory.
//
// Procedures:
// 01. load the free memory pointer; cache as fmp
// 02. store transferFrom selector in memory
// 03. store sender address in memory
// 04. store recipient address in memory
// 05. store amount in memory
// 06. call transferFrom; cache as ok
// 07. check that either no data was returned or the data is 32 bytes and true; compose with ok
// 08. revert if ok is false
// 09. restore the free memory pointer to fmp
// 10. restore the zero slot to zero
function transferFrom(ERC20 erc20, address sender, address receiver, uint256 amount) {
assembly {
let fmp := mload(0x40)
mstore(0x00, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(0x04, sender)
mstore(0x24, receiver)
mstore(0x44, amount)
let ok := call(gas(), erc20, 0x00, 0x00, 0x64, 0x00, 0x20)
ok := and(ok, or(iszero(returndatasize()), and(eq(returndatasize(), 0x20), mload(0x00))))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x40, fmp)
mstore(0x60, 0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Call ERC20.approve without allocating new memory.
//
// Procedures:
// 01. store approve selector in memory
// 02. store spender address in memory
// 03. store amount in memory
// 04. call approve; cache as ok
// 05. check that either no data was returned or the data is 32 bytes and true; compose with ok
// 06. revert if ok is false
// 07. restore the upper bits of the free memory pointer to zero
function approve(ERC20 erc20, address spender, uint256 amount) {
assembly {
mstore(0x00, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(0x04, spender)
mstore(0x24, amount)
let ok := call(gas(), erc20, 0x00, 0x00, 0x44, 0x00, 0x20)
ok := and(ok, or(iszero(returndatasize()), and(eq(returndatasize(), 0x20), mload(0x00))))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x24, 0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if the two ERC20 instances are equal, `false` otherwise.
function eq(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := eq(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if the two ERC20 instances are not equal, `false` otherwise.
function neq(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := iszero(eq(lhs, rhs))
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is greater than `rhs`, `false` otherwise.
function gt(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := gt(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is greater than or equal to `rhs`, `false` otherwise.
function gte(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := iszero(lt(lhs, rhs))
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is less than `rhs`, `false` otherwise.
function lt(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := lt(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is less than or equal to `rhs`, `false` otherwise.
function lte(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := iszero(gt(lhs, rhs))
}
}
// -------------------------------------------------------------------------------------------------
// Returns the sum of two ERC20 instances.
function add(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := add(lhs, rhs)
if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) }
}
}
// -------------------------------------------------------------------------------------------------
// Returns the difference of two ERC20 instances.
function sub(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := sub(lhs, rhs)
if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) }
}
}
// -------------------------------------------------------------------------------------------------
// Returns the product of two ERC20 instances.
function mul(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
if lhs {
output := and(mul(lhs, rhs), 0xffffffffffffffffffffffffffffffffffffffff)
if iszero(eq(div(output, lhs), rhs)) { revert(0x00, 0x00) }
}
}
}
// -------------------------------------------------------------------------------------------------
// Returns the division of two ERC20 instances.
function div(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
if iszero(rhs) { revert(0x00, 0x00) }
output := div(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the modulus of two ERC20 instances.
function mod(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
if iszero(rhs) { revert(0x00, 0x00) }
output := mod(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise AND of two ERC20 instances.
function and(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := and(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise OR of two ERC20 instances.
function or(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := or(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise XOR of two ERC20 instances.
function xor(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := xor(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise NOT of an ERC20 instance.
function not(ERC20 lhs) pure returns (ERC20 output) {
assembly {
output := and(not(lhs), 0xffffffffffffffffffffffffffffffffffffffff)
}
}