Repository navigation
Expand file tree
/
Copy pathamm_pool.cell
More file actions
160 lines (147 loc) · 7.5 KB
/
Copy pathamm_pool.cell
File metadata and controls
160 lines (147 loc) · 7.5 KB
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
module cellscript::amm_pool
use cellscript::fungible_token::Token
const U64_MAX: u64 = 18446744073709551615
const MAX_FEE_RATE_BPS: u16 = 1000
receipt LPReceipt has store, create, consume {
pool_id: Hash,
lp_amount: u64,
provider: Address,
}
shared Pool has store, create, replace {
token_a_type: Hash,
token_b_type: Hash,
token_a_symbol: [u8; 8],
token_b_symbol: [u8; 8],
reserve_a: u64,
reserve_b: u64,
total_lp: u64,
fee_rate_bps: u16,
}
action seed_pool(token_a: Token, token_b: Token, fee_rate_bps: u16, provider: Address) -> (pool: Pool, receipt: LPReceipt) {
verification
require token_a.symbol != token_b.symbol, "same token"
require token_a.amount > 0 && token_b.amount > 0, "zero liquidity"
require fee_rate_bps <= MAX_FEE_RATE_BPS, "fee too high"
require token_a.type_hash() != token_b.type_hash(), "same token type"
require token_a.amount <= U64_MAX / token_b.amount, "liquidity overflow"
require token_a.amount * token_b.amount < U64_MAX, "liquidity root overflow"
let initial_lp = isqrt(token_a.amount * token_b.amount)
require initial_lp > 0, "zero lp supply"
consume token_a
consume token_b
create pool = Pool { token_a_type: token_a.type_hash(), token_b_type: token_b.type_hash(), token_a_symbol: token_a.symbol, token_b_symbol: token_b.symbol, reserve_a: token_a.amount, reserve_b: token_b.amount, total_lp: initial_lp, fee_rate_bps }
create receipt = LPReceipt { pool_id: pool.type_hash(), lp_amount: initial_lp, provider } with_lock(provider)
}
action swap_a_for_b(pool_before: Pool, input: Token, min_output: u64, to: Address) -> (pool_after: Pool, token_out: Token) {
transition pool_before -> pool_after
verification
require input.symbol == pool_before.token_a_symbol, "wrong input token"
require input.type_hash() == pool_before.token_a_type, "wrong input token type"
require input.amount > 0, "zero input"
require pool_before.reserve_a > 0 && pool_before.reserve_b > 0, "empty pool"
require pool_before.total_lp > 0, "zero lp supply"
require pool_before.fee_rate_bps <= MAX_FEE_RATE_BPS, "fee too high"
require input.amount <= U64_MAX / 10000, "fee overflow"
require input.amount <= U64_MAX - pool_before.reserve_a, "reserve overflow"
let fee = input.amount * pool_before.fee_rate_bps as u64 / 10000
let net_input = input.amount - fee
require net_input > 0, "zero net input"
require pool_before.reserve_b <= U64_MAX / net_input, "pricing overflow"
let amount_out = pool_before.reserve_b * net_input / (pool_before.reserve_a + net_input)
require amount_out >= min_output, "slippage exceeded"
require amount_out < pool_before.reserve_b, "insufficient reserves"
require amount_out > 0, "zero output"
preserve pool_after from pool_before {
token_a_type
token_b_type
token_a_symbol
token_b_symbol
total_lp
fee_rate_bps
}
require pool_after.reserve_a == pool_before.reserve_a + input.amount
require pool_after.reserve_b == pool_before.reserve_b - amount_out
require token_out.type_hash() == pool_before.token_b_type, "wrong output token type"
consume input
create token_out = Token { amount: amount_out, symbol: pool_before.token_b_symbol } with_lock(to)
}
action add_liquidity(pool_before: Pool, token_a: Token, token_b: Token, provider: Address) -> (pool_after: Pool, receipt: LPReceipt) {
transition pool_before -> pool_after
verification
require token_a.symbol == pool_before.token_a_symbol, "wrong token a"
require token_b.symbol == pool_before.token_b_symbol, "wrong token b"
require token_a.type_hash() == pool_before.token_a_type, "wrong token a type"
require token_b.type_hash() == pool_before.token_b_type, "wrong token b type"
require token_a.amount > 0 && token_b.amount > 0, "zero deposit"
require pool_before.reserve_a > 0 && pool_before.reserve_b > 0 && pool_before.total_lp > 0, "empty pool"
require token_a.amount <= U64_MAX - pool_before.reserve_a, "reserve a overflow"
require token_b.amount <= U64_MAX - pool_before.reserve_b, "reserve b overflow"
require token_a.amount <= U64_MAX / pool_before.total_lp, "lp a overflow"
require token_b.amount <= U64_MAX / pool_before.total_lp, "lp b overflow"
let lp_from_a = token_a.amount * pool_before.total_lp / pool_before.reserve_a
let lp_from_b = token_b.amount * pool_before.total_lp / pool_before.reserve_b
let lp_amount = min(lp_from_a, lp_from_b)
require lp_amount > 0, "zero lp minted"
require lp_amount <= U64_MAX - pool_before.total_lp, "lp supply overflow"
preserve pool_after from pool_before {
token_a_type
token_b_type
token_a_symbol
token_b_symbol
fee_rate_bps
}
require pool_after.reserve_a == pool_before.reserve_a + token_a.amount
require pool_after.reserve_b == pool_before.reserve_b + token_b.amount
require pool_after.total_lp == pool_before.total_lp + lp_amount
consume token_a
consume token_b
create receipt = LPReceipt { pool_id: pool_before.type_hash(), lp_amount, provider } with_lock(provider)
}
action remove_liquidity(pool_before: Pool, receipt: LPReceipt, provider: Address) -> (pool_after: Pool, token_a_out: Token, token_b_out: Token) {
transition pool_before -> pool_after
verification
require receipt.pool_id == pool_before.type_hash(), "wrong pool"
require receipt.provider == provider, "wrong provider"
require receipt.lp_amount > 0, "zero lp burn"
require pool_before.reserve_a > 0 && pool_before.reserve_b > 0 && pool_before.total_lp > 0, "empty pool"
require receipt.lp_amount <= pool_before.total_lp, "lp burn exceeds supply"
require receipt.lp_amount <= U64_MAX / pool_before.reserve_a, "amount a overflow"
require receipt.lp_amount <= U64_MAX / pool_before.reserve_b, "amount b overflow"
let amount_a = receipt.lp_amount * pool_before.reserve_a / pool_before.total_lp
let amount_b = receipt.lp_amount * pool_before.reserve_b / pool_before.total_lp
require amount_a > 0 && amount_b > 0, "zero withdrawal"
preserve pool_after from pool_before {
token_a_type
token_b_type
token_a_symbol
token_b_symbol
fee_rate_bps
}
require pool_after.reserve_a == pool_before.reserve_a - amount_a
require pool_after.reserve_b == pool_before.reserve_b - amount_b
require pool_after.total_lp == pool_before.total_lp - receipt.lp_amount
require token_a_out.type_hash() == pool_before.token_a_type, "wrong token a output type"
require token_b_out.type_hash() == pool_before.token_b_type, "wrong token b output type"
consume receipt
create token_a_out = Token { amount: amount_a, symbol: pool_before.token_a_symbol } with_lock(provider)
create token_b_out = Token { amount: amount_b, symbol: pool_before.token_b_symbol } with_lock(provider)
}
fn isqrt(n: u64) -> u64 {
if n == 0 {
return 0
}
let mut x = n
let mut y = (x + 1) / 2
while y < x {
x = y
y = (x + n / x) / 2
}
x
}
fn min(a: u64, b: u64) -> u64 {
if a < b {
a
} else {
b
}
}