-
Notifications
You must be signed in to change notification settings - Fork 182
/
uniswap.fe
392 lines (330 loc) · 13.8 KB
/
uniswap.fe
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
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
// Poor man's interface because current Fe has no interfaces yet
contract ERC20 {
pub fn balanceOf(self, _ account: address) -> u256 {
revert
}
pub fn transfer(self, to: address, _ amount: u256) -> bool {
revert
}
}
struct Approval {
#indexed
pub owner: address
#indexed
pub spender: address
pub value: u256
}
struct Transfer {
#indexed
pub from: address
#indexed
pub to: address
pub value: u256
}
struct Mint {
#indexed
pub sender: address
pub amount0: u256
pub amount1: u256
}
struct Burn {
#indexed
pub sender: address
pub amount0: u256
pub amount1: u256
#indexed
pub to: address
}
struct Swap {
#indexed
pub sender: address
pub amount0_in: u256
pub amount1_in: u256
pub amount0_out: u256
pub amount1_out: u256
#indexed
pub to: address
}
struct Sync {
pub reserve0: u256
pub reserve1: u256
}
const MINIMUM_LIQUIDITY: u256 = 1000
contract UniswapV2Pair {
balances: Map<address, u256>
allowances: Map<address, Map<address, u256>>
total_supply: u256
nonces: Map<address, u256>
factory: address
token0: ERC20
token1: ERC20
reserve0: u256
reserve1: u256
block_timestamp_last: u256
price0_cumulative_last: u256
price1_cumulative_last: u256
k_last: u256
pub fn __init__(mut self, ctx: Context) {
self.factory = ctx.msg_sender()
}
pub fn factory(self) -> address {
return self.factory
}
pub fn token0(self) -> address {
return address(self.token0)
}
pub fn token1(self) -> address {
return address(self.token1)
}
fn _mint(mut self, mut ctx: Context, to: address, value: u256) {
self.total_supply = self.total_supply + value
self.balances[to] = self.balances[to] + value
ctx.emit(Transfer(from: address(0), to, value))
}
fn _burn(mut self, mut ctx: Context, from: address, value: u256) {
self.balances[from] = self.balances[from] - value
self.total_supply = self.total_supply - value
ctx.emit(Transfer(from, to: address(0), value))
}
fn _approve(mut self, mut ctx: Context, owner: address, spender: address, value: u256) {
self.allowances[owner][spender] = value
ctx.emit(Approval(owner, spender, value))
}
fn _transfer(mut self, mut ctx: Context, from: address, to: address, value: u256) {
self.balances[from] = self.balances[from] - value
self.balances[to] = self.balances[to] + value
ctx.emit(Transfer(from, to, value))
}
pub fn approve(mut self, mut ctx: Context, spender: address, value: u256) -> bool {
self._approve(ctx, owner: ctx.msg_sender(), spender, value)
return true
}
pub fn transfer(mut self, mut ctx: Context, to: address, value: u256) -> bool {
self._transfer(ctx, from: ctx.msg_sender(), to, value)
return true
}
pub fn transferFrom(mut self, mut ctx: Context, from: address, to: address, value: u256) -> bool {
assert self.allowances[from][ctx.msg_sender()] >= value
self.allowances[from][ctx.msg_sender()] = self.allowances[from][ctx.msg_sender()] - value
self._transfer(ctx, from, to, value)
return true
}
pub fn balanceOf(self, _ account: address) -> u256 {
return self.balances[account]
}
pub fn get_reserves(self) -> (u256, u256, u256) {
return (self.reserve0, self.reserve1, self.block_timestamp_last)
}
// called once by the factory at time of deployment
pub fn initialize(mut self, ctx: Context, token0: ERC20, token1: ERC20) {
assert ctx.msg_sender() == self.factory, "UniswapV2: FORBIDDEN"
self.token0 = token0
self.token1 = token1
}
// update reserves and, on the first call per block, price accumulators
fn _update(mut self, mut ctx: Context, balance0: u256, balance1: u256, reserve0: u256, reserve1: u256) {
// changed from u32s
// TODO: reproduce desired overflow (https://github.com/ethereum/fe/issues/286)
let block_timestamp: u256 = ctx.block_timestamp() % 2 ** 32
let time_elapsed: u256 = block_timestamp - self.block_timestamp_last
if time_elapsed > 0 and reserve0 != 0 and reserve1 != 0 {
self.price0_cumulative_last = self.price0_cumulative_last + reserve1 / reserve0 * time_elapsed
self.price1_cumulative_last = self.price1_cumulative_last + reserve0 / reserve1 * time_elapsed
}
self.reserve0 = balance0
self.reserve1 = balance1
self.block_timestamp_last = block_timestamp
ctx.emit(Sync(reserve0: self.reserve0, reserve1: self.reserve1))
}
fn _mint_fee(mut self, mut ctx: Context, reserve0: u256, reserve1: u256) -> bool {
let fee_to: address = UniswapV2Factory(self.factory).fee_to()
let fee_on: bool = fee_to != address(0)
let k_last: u256 = self.k_last
if fee_on {
if k_last != 0 {
let root_k: u256 = sqrt(reserve0 * reserve1)
let root_k_last: u256 = sqrt(k_last)
if root_k > root_k_last {
let numerator: u256 = self.total_supply * root_k - root_k_last
let denominator: u256 = root_k * 5 + root_k_last
let liquidity: u256 = numerator / denominator
if liquidity > 0 {
self._mint(ctx, to: fee_to, value: liquidity)
}
}
}
} else if k_last != 0 {
self.k_last = 0
}
return fee_on
}
// this low-level function should be called from a contract which performs important safety checks
pub fn mint(mut self, mut ctx: Context, to: address) -> u256 {
let reserve0: u256 = self.reserve0
let reserve1: u256 = self.reserve1
let balance0: u256 = self.token0.balanceOf(ctx.self_address())
let balance1: u256 = self.token1.balanceOf(ctx.self_address())
let amount0: u256 = balance0 - self.reserve0
let amount1: u256 = balance1 - self.reserve1
let fee_on: bool = self._mint_fee(ctx, reserve0, reserve1)
let total_supply: u256 = self.total_supply // gas savings, must be defined here since totalSupply can update in _mintFee
let mut liquidity: u256 = 0
if total_supply == 0 {
liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY
self._mint(ctx, to: address(0), value: MINIMUM_LIQUIDITY) // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = min(amount0 * total_supply / reserve0, amount1 * total_supply / reserve1)
}
assert liquidity > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED"
self._mint(ctx, to, value: liquidity)
self._update(ctx, balance0, balance1, reserve0, reserve1)
if fee_on {
self.k_last = reserve0 * reserve1 // reserve0 and reserve1 are up-to-date
}
ctx.emit(Mint(sender: ctx.msg_sender(), amount0, amount1))
return liquidity
}
// this low-level function should be called from a contract which performs important safety checks
pub fn burn(mut self, mut ctx: Context, to: address) -> (u256, u256) {
let reserve0: u256 = self.reserve0
let reserve1: u256 = self.reserve1
let token0: ERC20 = self.token0
let token1: ERC20 = self.token1
let mut balance0: u256 = token0.balanceOf(ctx.self_address())
let mut balance1: u256 = token1.balanceOf(ctx.self_address())
let liquidity: u256 = self.balances[ctx.self_address()]
let fee_on: bool = self._mint_fee(ctx, reserve0, reserve1)
let total_supply: u256 = self.total_supply // gas savings, must be defined here since total_supply can update in _mintFee
let amount0: u256 = (liquidity * balance0) / total_supply // using balances ensures pro-rata distribution
let amount1: u256 = (liquidity * balance1) / total_supply // using balances ensures pro-rata distribution
assert amount0 > 0 and amount1 > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED"
self._burn(ctx, from: ctx.self_address(), value: liquidity)
token0.transfer(to, amount0)
token1.transfer(to, amount1)
balance0 = token0.balanceOf(ctx.self_address())
balance1 = token1.balanceOf(ctx.self_address())
self._update(ctx, balance0, balance1, reserve0, reserve1)
if fee_on {
self.k_last = reserve0 * reserve1
}
ctx.emit(Burn(sender: ctx.msg_sender(), amount0, amount1, to))
return (amount0, amount1)
}
// this low-level function should be called from a contract which performs important safety checks
// TODO: add support for the bytes type (https://github.com/ethereum/fe/issues/280)
// pub fn swap(amount0_out: u256, amount1_out: u256, to: address, data: bytes):
pub fn swap(mut self, mut ctx: Context, amount0_out: u256, amount1_out: u256, to: address) {
assert amount0_out > 0 or amount1_out > 0, "UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT"
let reserve0: u256 = self.reserve0
let reserve1: u256 = self.reserve1
assert amount0_out < reserve0 and amount1_out < reserve1, "UniswapV2: INSUFFICIENT_LIQUIDITY"
let token0: ERC20 = self.token0
let token1: ERC20 = self.token1
// TODO: we should be using `token0.address` (https://github.com/ethereum/fe/issues/287)
assert to != address(token0) and to != address(token1), "UniswapV2: INVALID_TO"
if amount0_out > 0 {
token0.transfer(to, amount0_out) // optimistically transfer tokens
}
if amount1_out > 0 {
token1.transfer(to, amount1_out) // optimistically transfer tokens
}
// TODO: bytes support
// if data.length > 0:
// IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0_out, amount1_out, data)
let balance0: u256 = token0.balanceOf(ctx.self_address())
let balance1: u256 = token1.balanceOf(ctx.self_address())
let amount0_in: u256 = balance0 - (reserve0 - amount0_out) if balance0 > reserve0 - amount0_out else 0
let amount1_in: u256 = balance1 - (reserve1 - amount1_out) if balance1 > reserve1 - amount1_out else 0
assert amount0_in > 0 or amount1_in > 0, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT"
let balance0_adjusted: u256 = balance0 * 1000 - amount0_in * 3
let balance1_adjusted: u256 = balance1 * 1000 - amount1_in * 3
assert balance0_adjusted * balance1_adjusted >= reserve0 * reserve1 * 1000000, "UniswapV2: K"
self._update(ctx, balance0, balance1, reserve0, reserve1)
ctx.emit(Swap(sender: ctx.msg_sender(), amount0_in, amount1_in, amount0_out, amount1_out, to))
}
// force balances to match reserves
pub fn skim(self, ctx: Context, to: address) {
let token0: ERC20 = self.token0 // gas savings
let token1: ERC20 = self.token1 // gas savings
token0.transfer(to, token0.balanceOf(ctx.self_address()) - self.reserve0)
token1.transfer(to, token1.balanceOf(ctx.self_address()) - self.reserve1)
}
// force reserves to match balances
pub fn sync(mut self, mut ctx: Context) {
let token0: ERC20 = self.token0
let token1: ERC20 = self.token1
self._update(ctx,
balance0: token0.balanceOf(ctx.self_address()),
balance1: token1.balanceOf(ctx.self_address()),
reserve0: self.reserve0,
reserve1: self.reserve1)
}
}
struct PairCreated {
#indexed
pub token0: address
#indexed
pub token1: address
pub pair: address
pub index: u256
}
contract UniswapV2Factory {
fee_to: address
fee_to_setter: address
pairs: Map<address, Map<address, address>>
all_pairs: Array<address, 100>
pair_counter: u256
pub fn __init__(mut self, _ fee_to_setter: address) {
self.fee_to_setter = fee_to_setter
}
pub fn fee_to(self) -> address {
return self.fee_to
}
pub fn fee_to_setter(self) -> address {
return self.fee_to_setter
}
pub fn all_pairs_length(self) -> u256 {
return self.pair_counter
}
pub fn create_pair(mut self, mut ctx: Context, _ token_a: address, _ token_b: address) -> address {
assert token_a != token_b, "UniswapV2: IDENTICAL_ADDRESSES"
let token0: address = token_a if token_a < token_b else token_b
let token1: address = token_a if token_a > token_b else token_b
assert token0 != address(0), "UniswapV2: ZERO_ADDRESS"
assert self.pairs[token0][token1] == address(0), "UniswapV2: PAIR_EXISTS"
let salt: u256 = keccak256((token0, token1).abi_encode())
let mut pair: UniswapV2Pair = UniswapV2Pair.create2(ctx, 0, salt)
pair.initialize(ctx, token0: ERC20(token0), token1: ERC20(token1))
self.pairs[token0][token1] = address(pair)
self.pairs[token1][token0] = address(pair)
self.all_pairs[self.pair_counter] = address(pair)
self.pair_counter = self.pair_counter + 1
ctx.emit(PairCreated(token0, token1, pair: address(pair), index: self.pair_counter))
return address(pair)
}
pub fn set_fee_to(mut self, ctx: Context, fee_to: address) {
assert ctx.msg_sender() == self.fee_to_setter, "UniswapV2: FORBIDDEN"
self.fee_to = fee_to
}
pub fn set_fee_to_setter(mut self, ctx: Context, fee_to_setter: address) {
assert ctx.msg_sender() == fee_to_setter, "UniswapV2: FORBIDDEN"
self.fee_to_setter = fee_to_setter
}
}
fn sqrt(_ val: u256) -> u256 {
let mut z: u256
if val > 3 {
z = val
let mut x: u256 = val / 2 + 1
while x < z {
z = x
x = (val / x + x) / 2
}
} else if val != 0 {
z = 1
}
return z
}
fn min(_ x: u256, _ y: u256) -> u256 {
return x if x < y else y
}