├── .gitignore
├── .idea
├── .gitignore
├── cetus-amm.iml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── aptos
├── Move.toml
└── sources
│ ├── amm_config.move
│ ├── amm_math.move
│ ├── amm_router.move
│ ├── amm_script.move
│ ├── amm_swap.move
│ ├── amm_utils.move
│ └── u256.move
└── sui
├── Move.toml
└── sources
├── amm_config.move
├── amm_math.move
├── amm_router.move
├── amm_script.move
├── amm_swap.move
└── amm_utils.move
/.gitignore:
--------------------------------------------------------------------------------
1 | aptos/build/
2 | sui/build/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/cetus-amm.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cetus Amm
2 |
3 |
--------------------------------------------------------------------------------
/aptos/Move.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = 'Cetue-AMM'
3 | version = '1.0.0'
4 | [dependencies.AptosFramework]
5 | git = 'https://github.com/aptos-labs/aptos-core.git'
6 | rev = '8399cd1c7b9662d3a6a09c28363c5f66f0839c41'
7 | subdir = 'aptos-move/framework/aptos-framework'
8 |
9 | [addresses]
10 | cetus_amm = "_"
11 |
--------------------------------------------------------------------------------
/aptos/sources/amm_config.move:
--------------------------------------------------------------------------------
1 | module cetus_amm::amm_config {
2 | use std::error;
3 | use std::signer;
4 | //
5 | // Errors
6 | //
7 | const ENOT_HAS_PRIVILEGE: u64 = 1001;
8 | const EPOOL_PAUSE: u64 = 1002;
9 |
10 | const DEFAULT_TRADE_FEE_NUMERATOR: u64 = 2;
11 | const DEFAULT_TRADE_FEE_DENOMINATOR: u64 = 1000;
12 | const DEFAULT_PROTOCOL_FEE_NUMERATOR: u64 = 2;
13 | const DEFAULT_PROTOCOL_FEE_DENOMINATOR: u64 = 10;
14 |
15 | struct PoolFeeConfig has key {
16 | trade_fee_numerator: u64,
17 | trade_fee_denominator: u64,
18 |
19 | protocol_fee_numerator: u64,
20 | protocol_fee_denominator: u64,
21 | }
22 |
23 | struct PoolPauseStatus has key {
24 | pause: bool,
25 | }
26 |
27 | public fun set_pool_fee_config(
28 | account: &signer,
29 | trade_fee_numerator: u64,
30 | trade_fee_denominator: u64,
31 | protocol_fee_numerator: u64,
32 | protocol_fee_denominator: u64) acquires PoolFeeConfig {
33 |
34 | assert_admin(account);
35 | let addr = signer::address_of(account);
36 | if (!exists>(addr)) {
37 | move_to(account, PoolFeeConfig{
38 | trade_fee_numerator,
39 | trade_fee_denominator,
40 | protocol_fee_numerator,
41 | protocol_fee_denominator,
42 | });
43 | } else {
44 | let fee_config = borrow_global_mut>(addr);
45 | fee_config.trade_fee_numerator = trade_fee_numerator;
46 | fee_config.trade_fee_denominator = trade_fee_denominator;
47 | fee_config.protocol_fee_numerator = protocol_fee_numerator;
48 | fee_config.protocol_fee_denominator = protocol_fee_denominator;
49 | }
50 |
51 | }
52 |
53 | public fun set_pool_pause(
54 | account: &signer,
55 | pause: bool) acquires PoolPauseStatus {
56 |
57 | assert_admin(account);
58 | let addr = signer::address_of(account);
59 | if (!exists(addr)) {
60 | move_to(account, PoolPauseStatus{ pause });
61 | } else {
62 | let status = borrow_global_mut(addr);
63 | status.pause = pause;
64 | }
65 | }
66 |
67 | public fun get_trade_fee(): (u64, u64) acquires PoolFeeConfig {
68 | if (exists>(admin_address())) {
69 | let fee_config = borrow_global>(admin_address());
70 | (fee_config.trade_fee_numerator, fee_config.trade_fee_denominator)
71 | } else {
72 | (DEFAULT_TRADE_FEE_NUMERATOR, DEFAULT_TRADE_FEE_DENOMINATOR)
73 | }
74 | }
75 |
76 | public fun get_protocol_fee(): (u64, u64) acquires PoolFeeConfig {
77 | if (exists>(admin_address())) {
78 | let fee_config = borrow_global>(admin_address());
79 | (fee_config.protocol_fee_numerator, fee_config.protocol_fee_denominator)
80 | } else {
81 | (DEFAULT_PROTOCOL_FEE_NUMERATOR, DEFAULT_PROTOCOL_FEE_DENOMINATOR)
82 | }
83 | }
84 |
85 | public fun get_pool_pause(): bool acquires PoolPauseStatus {
86 | borrow_global(admin_address()).pause
87 | }
88 |
89 | public fun admin_address(): address {
90 | @cetus_amm
91 | }
92 |
93 | public fun assert_admin(account: &signer) {
94 | assert!(
95 | signer::address_of(account) == admin_address(),
96 | error::permission_denied(ENOT_HAS_PRIVILEGE));
97 | }
98 |
99 | public fun assert_pause() acquires PoolPauseStatus {
100 | assert!(
101 | !get_pool_pause(),
102 | error::unavailable(EPOOL_PAUSE));
103 | }
104 | }
--------------------------------------------------------------------------------
/aptos/sources/amm_math.move:
--------------------------------------------------------------------------------
1 | module cetus_amm::amm_math {
2 | use cetus_amm::u256::{Self, U256};
3 | use std::error;
4 |
5 | //
6 | // Errors
7 | //
8 | const EDIVIDE_BY_ZERO: u64 = 2001;
9 | const EPARAMETER_INVALID: u64 = 2001;
10 | const U128_MAX:u128 = 340282366920938463463374607431768211455;
11 | const U64_MAX: u128 = 18446744073709551615;
12 |
13 | public fun safe_compare_mul_u128(a1: u128, b1: u128, a2: u128, b2: u128): u8 {
14 | let left = u256::mul(u256::from_u128(a1), u256::from_u128(b1));
15 | let right = u256::mul(u256::from_u128(a2), u256::from_u128(b2));
16 | u256::compare(&left, &right)
17 | }
18 |
19 | public fun safe_mul_div_u128(x: u128, y: u128, z: u128): u128 {
20 | assert!(z != 0, EDIVIDE_BY_ZERO);
21 | assert!(x <= U64_MAX, EPARAMETER_INVALID);
22 | assert!(y <= U64_MAX, EPARAMETER_INVALID);
23 | x * y / z
24 | }
25 |
26 | public fun mul_div_u128(x: u128, y: u128, z: u128): U256 {
27 | if (z == 0) {
28 | abort error::aborted(EDIVIDE_BY_ZERO)
29 | };
30 |
31 | let x_u256 = u256::from_u128(x);
32 | let y_u256 = u256::from_u128(y);
33 | let z_u256 = u256::from_u128(z);
34 | u256::div(u256::mul(x_u256, y_u256), z_u256)
35 | }
36 |
37 | public fun quote(amount_a: u128, reserve_a: u128, reserve_b: u128): u128 {
38 | assert!(amount_a > 0, error::invalid_argument(EPARAMETER_INVALID));
39 | assert!(reserve_a > 0 && reserve_b> 0, error::invalid_argument(EPARAMETER_INVALID));
40 | let amount_b = safe_mul_div_u128(amount_a,reserve_b,reserve_a);
41 | amount_b
42 | }
43 |
44 | public fun sqrt(y: u128): u128 {
45 | if (y > 3) {
46 | let z = y;
47 | let x = y / 2 + 1;
48 | while (x < z) {
49 | z = x;
50 | x = (y / x + x) / 2;
51 | };
52 | return z
53 | };
54 | if (y > 0) 1 else 0
55 | }
56 |
57 | public fun min(x: u128, y: u128): u128 {
58 | if (x < y) x else y
59 | }
60 |
61 | #[test]
62 | public entry fun test_safe_mul_div_u128() {
63 | let x: u128 = 9446744073709551615;
64 | let y: u128 = 1009855555;
65 | let z: u128 = 3979;
66 | let _r_expected:u128 = 2397548876476230247541334;
67 | let r = Self::safe_mul_div_u128(x, y, z);
68 | assert!(r == _r_expected, 3001);
69 | }
70 |
71 | #[test]
72 | public entry fun test_sqrt_by_max_u128() {
73 | let _r_expected:u128 = 18446744073709551615;
74 | let r = Self::sqrt(U128_MAX);
75 | assert!(r == _r_expected, 3004);
76 | }
77 | }
--------------------------------------------------------------------------------
/aptos/sources/amm_router.move:
--------------------------------------------------------------------------------
1 | module cetus_amm::amm_router {
2 | use std::error;
3 | use std::signer;
4 | use cetus_amm::amm_config;
5 | use cetus_amm::amm_swap::{PoolLiquidityCoin,Self};
6 | use cetus_amm::amm_utils;
7 | use aptos_framework::coin::{Self, Coin};
8 | use aptos_std::comparator;
9 | use cetus_amm::amm_math::{Self, quote};
10 |
11 |
12 | const ESWAP_B_OUT_LESSTHAN_EXPECTED: u64 = 3001;
13 | const ESWAP_A_IN_OVER_LIMIT_MAX: u64 = 3002;
14 | const EINVALID_COIN_PAIR: u64 = 3003;
15 | const ELIQUIDITY_INSUFFICIENT_B_AMOUNT: u64 = 3004;
16 | const ELIQUIDITY_OVERLIMIT_X_DESIRED: u64 = 3005;
17 | const ELIQUIDITY_INSUFFICIENT_A_AMOUNT: u64 = 3006;
18 | const ELIQUIDITY_ADD_LIQUIDITY_FAILED: u64 = 3007;
19 |
20 | public fun set_pool_fee_config(
21 | account: &signer,
22 | trade_fee_numerator: u64,
23 | trade_fee_denominator: u64,
24 | protocol_fee_numerator: u64,
25 | protocol_fee_denominator: u64
26 | ) {
27 | if (amm_swap::get_pool_direction()) {
28 | amm_config::set_pool_fee_config(
29 | account,
30 | trade_fee_numerator,
31 | trade_fee_denominator,
32 | protocol_fee_numerator,
33 | protocol_fee_denominator);
34 | } else {
35 | amm_config::set_pool_fee_config(
36 | account,
37 | trade_fee_numerator,
38 | trade_fee_denominator,
39 | protocol_fee_numerator,
40 | protocol_fee_denominator);
41 | }
42 | }
43 |
44 | public fun set_pause_status(account: &signer, pause:bool) {
45 | amm_config::set_pool_pause(account, pause);
46 | }
47 |
48 | public fun init_pool(account: &signer, protocol_fee_to: address) {
49 | //compare coins
50 | assert!(
51 | !comparator::is_equal(&amm_utils::compare_coin()),
52 | error::internal(EINVALID_COIN_PAIR));
53 |
54 | amm_swap::init_pool_v2 (
55 | account,
56 | protocol_fee_to);
57 | }
58 |
59 | /// Add liquidity for user
60 | public fun add_liquidity(
61 | account: &signer,
62 | amount_a_desired: u128,
63 | amount_b_desired: u128,
64 | amount_a_min: u128,
65 | amount_b_min: u128) {
66 | if (amm_swap::get_pool_direction()) {
67 | add_liquidity_internal(
68 | account,
69 | amount_a_desired,
70 | amount_b_desired,
71 | amount_a_min,
72 | amount_b_min,
73 | );
74 | } else {
75 | add_liquidity_internal(
76 | account,
77 | amount_b_desired,
78 | amount_a_desired,
79 | amount_b_min,
80 | amount_a_min,
81 | );
82 | }
83 | }
84 |
85 | fun add_liquidity_internal(
86 | account: &signer,
87 | amount_a_desired: u128,
88 | amount_b_desired: u128,
89 | amount_a_min: u128,
90 | amount_b_min: u128) {
91 | let (amount_a, amount_b) = calculate_amount_for_liquidity_internal(
92 | amount_a_desired,
93 | amount_b_desired,
94 | amount_a_min,
95 | amount_b_min);
96 | let coinA = coin::withdraw(account,(amount_a as u64));
97 | let coinB = coin::withdraw(account,(amount_b as u64));
98 | let sender = signer::address_of(account);
99 | let liquidity_coin = amm_swap::mint_and_emit_event_v2(
100 | sender,
101 | coinA,
102 | coinB);
103 | assert!(coin::value(&liquidity_coin) > 0, error::invalid_argument(ELIQUIDITY_ADD_LIQUIDITY_FAILED));
104 | if (!coin::is_account_registered>(sender)) coin::register>(account);
105 | coin::deposit(sender,liquidity_coin);
106 | }
107 |
108 | fun calculate_amount_for_liquidity_internal(
109 | amount_a_desired: u128,
110 | amount_b_desired: u128,
111 | amount_a_min: u128,
112 | amount_b_min: u128,): (u128, u128) {
113 | let (reserve_a, reserve_b) = amm_swap::get_reserves();
114 | if (reserve_a == 0 && reserve_b == 0) {
115 | return (amount_a_desired, amount_b_desired)
116 | } else {
117 | let amount_b_optimal = amm_math::quote(amount_a_desired, reserve_a, reserve_b);
118 | if (amount_b_optimal <= amount_b_desired) {
119 | assert!(amount_b_optimal >= amount_b_min, error::internal(ELIQUIDITY_INSUFFICIENT_B_AMOUNT));
120 | return (amount_a_desired, amount_b_optimal)
121 | } else {
122 | let amount_a_optimal = quote(amount_b_desired, reserve_b, reserve_a);
123 | assert!(amount_a_optimal <= amount_a_desired, error::internal(ELIQUIDITY_OVERLIMIT_X_DESIRED));
124 | assert!(amount_a_optimal >= amount_a_min, error::internal(ELIQUIDITY_INSUFFICIENT_A_AMOUNT));
125 | return (amount_a_optimal, amount_b_desired)
126 | }
127 | }
128 | }
129 |
130 | /// Remove liquidity for user
131 | public fun remove_liquidity(
132 | account: &signer,
133 | liquidity: u128,
134 | amount_a_min: u128,
135 | amount_b_min: u128) {
136 | if (amm_swap::get_pool_direction()) {
137 | remove_liquidity_internal(
138 | account,
139 | liquidity,
140 | amount_a_min,
141 | amount_b_min);
142 | } else {
143 | remove_liquidity_internal(
144 | account,
145 | liquidity,
146 | amount_b_min,
147 | amount_a_min);
148 | }
149 | }
150 |
151 | fun remove_liquidity_internal(
152 | account: &signer,
153 | liquidity: u128,
154 | amount_a_min: u128,
155 | amount_b_min: u128) {
156 | let liquidity_coin = coin::withdraw>(account,(liquidity as u64));
157 | let sender = signer::address_of(account);
158 | let (coin_a, coin_b) = amm_swap::burn_and_emit_event_v2(
159 | sender,
160 | liquidity_coin);
161 | assert!((coin::value(&coin_a) as u128) >= amount_a_min, error::internal(ELIQUIDITY_INSUFFICIENT_A_AMOUNT));
162 | assert!((coin::value(&coin_b) as u128) >= amount_b_min, error::internal(ELIQUIDITY_INSUFFICIENT_B_AMOUNT));
163 | coin::deposit(sender,coin_a);
164 | coin::deposit(sender,coin_b);
165 | }
166 |
167 | public fun swap_exact_coin_for_coin(
168 | account: &signer,
169 | amount_a_in: u128,
170 | amount_b_out_min: u128,
171 | ) {
172 | assert!(
173 | !comparator::is_equal(&amm_utils::compare_coin()),
174 | error::invalid_argument(EINVALID_COIN_PAIR));
175 |
176 | let sender = signer::address_of(account);
177 | if (!coin::is_account_registered(sender)) coin::register(account);
178 |
179 | let (coin_a_out, coin_b_out);
180 | let (coin_a_fee, coin_b_fee);
181 | let is_forward = amm_swap::get_pool_direction();
182 | if (is_forward) {
183 | //calc b out amount
184 | let b_out = compute_b_out(amount_a_in, true);
185 | assert!(b_out >= amount_b_out_min, error::internal(ESWAP_B_OUT_LESSTHAN_EXPECTED));
186 | //withdraw coin a
187 | let coin_a = coin::withdraw(account, (amount_a_in as u64));
188 | // swap
189 | (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = amm_swap::swap_and_emit_event_v2(sender, coin_a, b_out, coin::zero(), 0);
190 | } else {
191 | //calc b out amount
192 | let b_out = compute_b_out(amount_a_in, false);
193 | assert!(b_out >= amount_b_out_min, error::internal(ESWAP_B_OUT_LESSTHAN_EXPECTED));
194 | //withdraw coin a
195 | let coin_a = coin::withdraw(account, (amount_a_in as u64));
196 | // sawp
197 | (coin_b_out, coin_a_out, coin_b_fee, coin_a_fee) = amm_swap::swap_and_emit_event_v2(sender, coin::zero(), 0, coin_a, b_out);
198 | };
199 | //destroy
200 | coin::destroy_zero(coin_a_out);
201 | coin::deposit(sender, coin_b_out);
202 | coin::destroy_zero(coin_b_fee);
203 | //swap protocol fee
204 | amm_swap::handle_swap_protocol_fee_v2(sender, coin_a_fee, is_forward);
205 | }
206 |
207 | public fun swap_exact_coin_for_coin_router2(
208 | account: &signer,
209 | amount_a_in: u128,
210 | amount_b_out_min: u128,
211 | ) {
212 | assert!(
213 | !comparator::is_equal(&amm_utils::compare_coin()),
214 | error::invalid_argument(EINVALID_COIN_PAIR));
215 |
216 | assert!(
217 | !comparator::is_equal(&amm_utils::compare_coin()),
218 | error::invalid_argument(EINVALID_COIN_PAIR));
219 |
220 | let (x_out, b_out) = get_amount_out_router2(amount_a_in);
221 | assert!(b_out >= amount_b_out_min, error::internal(ESWAP_B_OUT_LESSTHAN_EXPECTED));
222 |
223 | swap_exact_coin_for_coin(account, amount_a_in, x_out);
224 | swap_exact_coin_for_coin(account, x_out, amount_b_out_min);
225 | }
226 |
227 | public fun swap_exact_coin_for_coin_router3(
228 | account: &signer,
229 | amount_a_in: u128,
230 | amount_b_out_min: u128
231 | ) {
232 | assert!(
233 | !comparator::is_equal(&amm_utils::compare_coin()),
234 | error::invalid_argument(EINVALID_COIN_PAIR));
235 |
236 | assert!(
237 | !comparator::is_equal(&amm_utils::compare_coin()),
238 | error::invalid_argument(EINVALID_COIN_PAIR));
239 | assert!(
240 | !comparator::is_equal(&amm_utils::compare_coin()),
241 | error::invalid_argument(EINVALID_COIN_PAIR));
242 |
243 | let (x_out, y_out, b_out) = get_amount_out_router3(amount_a_in);
244 | assert!(b_out >= amount_b_out_min, error::internal(ESWAP_B_OUT_LESSTHAN_EXPECTED));
245 | swap_exact_coin_for_coin(account, amount_a_in, x_out);
246 | swap_exact_coin_for_coin(account, x_out, y_out);
247 | swap_exact_coin_for_coin(account, y_out, amount_b_out_min);
248 | }
249 |
250 | public fun swap_coin_for_exact_coin(
251 | account: &signer,
252 | amount_a_in_max: u128,
253 | amount_b_out: u128,
254 | ) {
255 | assert!(
256 | !comparator::is_equal(&amm_utils::compare_coin()),
257 | error::invalid_argument(EINVALID_COIN_PAIR));
258 |
259 | let sender = signer::address_of(account);
260 | if (!coin::is_account_registered(sender)) coin::register(account);
261 |
262 | let (coin_a_out, coin_b_out);
263 | let (coin_a_fee, coin_b_fee);
264 | let is_forward = amm_swap::get_pool_direction();
265 | if(is_forward) {
266 | let a_in = compute_a_in(amount_b_out, true);
267 | assert!(a_in <= amount_a_in_max, error::internal(ESWAP_A_IN_OVER_LIMIT_MAX));
268 |
269 | let coin_a = coin::withdraw(account, (a_in as u64));
270 |
271 | (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = amm_swap::swap_and_emit_event_v2(sender, coin_a, amount_b_out, coin::zero(), 0);
272 | } else {
273 | let a_in = compute_a_in(amount_b_out, false);
274 | assert!(a_in <= amount_a_in_max, error::internal(ESWAP_A_IN_OVER_LIMIT_MAX));
275 |
276 | let coin_a = coin::withdraw(account, (a_in as u64));
277 |
278 | (coin_b_out, coin_a_out, coin_b_fee, coin_a_fee) = amm_swap::swap_and_emit_event_v2(sender, coin::zero(), 0, coin_a, amount_b_out);
279 | };
280 |
281 | coin::destroy_zero(coin_a_out);
282 | coin::deposit(sender, coin_b_out);
283 | coin::destroy_zero(coin_b_fee);
284 |
285 | amm_swap::handle_swap_protocol_fee_v2(sender, coin_a_fee, is_forward);
286 | }
287 |
288 | public fun swap_coin_for_exact_coin_router2(
289 | account: &signer,
290 | amount_a_in_max: u128,
291 | amount_b_out: u128
292 | ) {
293 | assert!(
294 | !comparator::is_equal(&amm_utils::compare_coin()),
295 | error::invalid_argument(EINVALID_COIN_PAIR));
296 |
297 | assert!(
298 | !comparator::is_equal(&amm_utils::compare_coin()),
299 | error::invalid_argument(EINVALID_COIN_PAIR));
300 |
301 | let(x_in, a_in) = get_amount_in_router2(amount_b_out);
302 | assert!(a_in <= amount_a_in_max, error::internal(ESWAP_A_IN_OVER_LIMIT_MAX));
303 |
304 | swap_coin_for_exact_coin (account,amount_a_in_max,x_in);
305 | swap_coin_for_exact_coin (account,x_in,amount_b_out);
306 | }
307 |
308 | public fun swap_coin_for_exact_coin_router3(
309 | account: &signer,
310 | amount_a_in_max: u128,
311 | amount_b_out: u128
312 | ) {
313 | assert!(
314 | !comparator::is_equal(&amm_utils::compare_coin()),
315 | error::invalid_argument(EINVALID_COIN_PAIR));
316 |
317 | assert!(
318 | !comparator::is_equal(&amm_utils::compare_coin()),
319 | error::invalid_argument(EINVALID_COIN_PAIR));
320 | assert!(
321 | !comparator::is_equal(&amm_utils::compare_coin()),
322 | error::invalid_argument(EINVALID_COIN_PAIR));
323 |
324 | let(y_in, x_in, a_in) = get_amount_in_router3(amount_b_out);
325 | assert!(a_in <= amount_a_in_max, error::internal(ESWAP_A_IN_OVER_LIMIT_MAX));
326 |
327 | swap_coin_for_exact_coin (account,amount_a_in_max,x_in);
328 | swap_coin_for_exact_coin (account,x_in,y_in);
329 | swap_coin_for_exact_coin (account,y_in,amount_b_out);
330 | }
331 |
332 | public fun compute_b_out(amount_a_in: u128, is_forward: bool): u128 {
333 | if (is_forward) {
334 | let (fee_numerator, fee_denominator) = amm_config::get_trade_fee();
335 | let (reserve_a, reserve_b) = amm_swap::get_reserves();
336 | amm_utils::get_amount_out(amount_a_in, (reserve_a as u128), (reserve_b as u128), fee_numerator, fee_denominator)
337 | } else {
338 | let (fee_numerator, fee_denominator) = amm_config::get_trade_fee();
339 | let (reserve_b, reserve_a) = amm_swap::get_reserves();
340 | amm_utils::get_amount_out(amount_a_in, (reserve_a as u128), (reserve_b as u128), fee_numerator, fee_denominator)
341 | }
342 | }
343 |
344 | public fun compute_a_in(amount_b_out: u128, is_forward: bool): u128 {
345 | if (is_forward) {
346 | let (fee_numerator, fee_denominator) = amm_config::get_trade_fee();
347 | let (reserve_a, reserve_b) = amm_swap::get_reserves();
348 | amm_utils::get_amount_in(amount_b_out, reserve_a, reserve_b, fee_numerator, fee_denominator)
349 | } else {
350 | let (fee_numerator, fee_denominator) = amm_config::get_trade_fee();
351 | let (reserve_b, reserve_a) = amm_swap::get_reserves();
352 | amm_utils::get_amount_in(amount_b_out, reserve_a, reserve_b, fee_numerator, fee_denominator)
353 | }
354 | }
355 |
356 | public fun get_amount_out_router2(amount_a_in: u128): (u128, u128) {
357 | let is_forward = amm_swap::get_pool_direction();
358 | let x_out = compute_b_out(amount_a_in, is_forward);
359 | let is_forward = amm_swap::get_pool_direction();
360 | let b_out = compute_b_out(x_out, is_forward);
361 | (x_out, b_out)
362 | }
363 |
364 | public fun get_amount_in_router2(amount_b_out: u128): (u128, u128) {
365 | let is_forward = amm_swap::get_pool_direction();
366 | let x_in = compute_a_in(amount_b_out, is_forward);
367 | let is_forward = amm_swap::get_pool_direction();
368 | let a_in = compute_a_in(x_in, is_forward);
369 | (x_in, a_in)
370 | }
371 |
372 | public fun get_amount_out_router3(amount_a_in: u128): (u128, u128, u128) {
373 | let (x_out, y_out) = get_amount_out_router2(amount_a_in);
374 | let is_forward = amm_swap::get_pool_direction();
375 | let b_out = compute_b_out(y_out, is_forward);
376 | (x_out, y_out, b_out)
377 | }
378 |
379 | public fun get_amount_in_router3(amount_b_out: u128): (u128, u128, u128) {
380 | let (y_in, x_in) = get_amount_in_router2(amount_b_out);
381 | let is_forward = amm_swap::get_pool_direction();
382 | let a_in = compute_a_in(x_in, is_forward);
383 | (y_in, x_in, a_in)
384 | }
385 |
386 | public fun swap(account: address, coin_in: Coin): Coin {
387 | assert!(
388 | !comparator::is_equal(&amm_utils::compare_coin()),
389 | error::invalid_argument(EINVALID_COIN_PAIR));
390 |
391 | let coin_in_value = coin::value(&coin_in);
392 |
393 | let (coin_a_out, coin_b_out);
394 | let (coin_a_fee, coin_b_fee);
395 | let is_forward = amm_swap::get_pool_direction();
396 | if (is_forward) {
397 | //calc b out amount
398 | let b_out = compute_b_out((coin_in_value as u128), true);
399 | // swap
400 | (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = amm_swap::swap_and_emit_event_v2(account, coin_in, b_out, coin::zero(), 0);
401 | } else {
402 | //calc b out amount
403 | let b_out = compute_b_out((coin_in_value as u128), false);
404 | // sawp
405 | (coin_b_out, coin_a_out, coin_b_fee, coin_a_fee) = amm_swap::swap_and_emit_event_v2(account, coin::zero(), 0, coin_in, b_out);
406 | };
407 | //destroy
408 | coin::destroy_zero(coin_a_out);
409 | coin::destroy_zero(coin_b_fee);
410 | //swap protocol fee
411 | amm_swap::handle_swap_protocol_fee_v2(account, coin_a_fee, is_forward);
412 | coin_b_out
413 | }
414 |
415 | public fun swap_into(account: address, coin_in: &mut Coin, amount_out: u128): Coin {
416 | assert!(
417 | !comparator::is_equal(&amm_utils::compare_coin()),
418 | error::invalid_argument(EINVALID_COIN_PAIR));
419 |
420 | let amount_in_max = coin::value(coin_in);
421 |
422 | let (coin_a_out, coin_b_out);
423 | let (coin_a_fee, coin_b_fee);
424 | let is_forward = amm_swap::get_pool_direction();
425 | if(is_forward) {
426 | let a_in = compute_a_in(amount_out, true);
427 | assert!(a_in <= (amount_in_max as u128), error::internal(ESWAP_A_IN_OVER_LIMIT_MAX));
428 |
429 | let coin_a = coin::extract(coin_in, (a_in as u64));
430 |
431 | (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = amm_swap::swap_and_emit_event_v2(account, coin_a, amount_out, coin::zero(), 0);
432 | } else {
433 | let a_in = compute_a_in(amount_out, false);
434 | assert!(a_in <= (amount_in_max as u128), error::internal(ESWAP_A_IN_OVER_LIMIT_MAX));
435 |
436 | let coin_a = coin::extract(coin_in, (a_in as u64));
437 |
438 | (coin_b_out, coin_a_out, coin_b_fee, coin_a_fee) = amm_swap::swap_and_emit_event_v2(account, coin::zero(), 0, coin_a, amount_out);
439 | };
440 |
441 | coin::destroy_zero(coin_a_out);
442 | coin::destroy_zero(coin_b_fee);
443 |
444 | amm_swap::handle_swap_protocol_fee_v2(account, coin_a_fee, is_forward);
445 | coin_b_out
446 | }
447 | }
--------------------------------------------------------------------------------
/aptos/sources/amm_script.move:
--------------------------------------------------------------------------------
1 | module cetus_amm::amm_script {
2 | use cetus_amm::amm_router;
3 |
4 | public entry fun set_pool_fee_config(
5 | account: signer,
6 | trade_fee_numerator: u64,
7 | trade_fee_denominator: u64,
8 | protocol_fee_numerator: u64,
9 | protocol_fee_denominator: u64
10 | ) {
11 | amm_router::set_pool_fee_config(
12 | &account,
13 | trade_fee_numerator,
14 | trade_fee_denominator,
15 | protocol_fee_numerator,
16 | protocol_fee_denominator);
17 | }
18 |
19 | public entry fun init_pool(account: signer, protocol_fee_to: address) {
20 | amm_router::init_pool (
21 | &account,
22 | protocol_fee_to);
23 | }
24 |
25 | public entry fun add_liquidity(
26 | account: signer,
27 | amount_a_desired: u128,
28 | amount_b_desired: u128,
29 | amount_a_min: u128,
30 | amount_b_min: u128) {
31 | amm_router::add_liquidity(
32 | &account,
33 | amount_a_desired,
34 | amount_b_desired,
35 | amount_a_min,
36 | amount_b_min);
37 | }
38 |
39 | public entry fun remove_liquidity(
40 | account: signer,
41 | liquidity: u128,
42 | amount_a_min: u128,
43 | amount_b_min: u128) {
44 | amm_router::remove_liquidity(
45 | &account,
46 | liquidity,
47 | amount_a_min,
48 | amount_b_min);
49 | }
50 |
51 | public entry fun swap_exact_coin_for_coin(
52 | account: signer,
53 | amount_a_in: u128,
54 | amount_b_out_min: u128,
55 | ) {
56 | amm_router::swap_exact_coin_for_coin (
57 | &account,
58 | amount_a_in,
59 | amount_b_out_min);
60 | }
61 |
62 | public entry fun swap_exact_coin_for_coin_router2(
63 | account: signer,
64 | amount_a_in: u128,
65 | amount_b_out_min: u128
66 | ) {
67 | amm_router::swap_exact_coin_for_coin_router2 (
68 | &account,
69 | amount_a_in,
70 | amount_b_out_min);
71 | }
72 |
73 | public entry fun swap_exact_coin_for_coin_router3(
74 | account: signer,
75 | amount_a_in: u128,
76 | amount_b_out_min: u128
77 | ) {
78 | amm_router::swap_exact_coin_for_coin_router3 (
79 | &account,
80 | amount_a_in,
81 | amount_b_out_min);
82 | }
83 |
84 | public entry fun swap_coin_for_exact_coin(
85 | account: signer,
86 | amount_a_in_max: u128,
87 | amount_b_out: u128,
88 | ) {
89 | amm_router::swap_coin_for_exact_coin (
90 | &account,
91 | amount_a_in_max,
92 | amount_b_out);
93 | }
94 |
95 | public entry fun swap_coin_for_exact_coin_router2(
96 | account: signer,
97 | amount_a_in_max: u128,
98 | amount_b_out: u128,
99 | ) {
100 | amm_router::swap_coin_for_exact_coin_router2 (
101 | &account,
102 | amount_a_in_max,
103 | amount_b_out);
104 | }
105 |
106 | public entry fun swap_coin_for_exact_coin_router3(
107 | account: signer,
108 | amount_a_in_max: u128,
109 | amount_b_out: u128
110 | ) {
111 | amm_router::swap_coin_for_exact_coin_router3 (
112 | &account,
113 | amount_a_in_max,
114 | amount_b_out);
115 | }
116 |
117 | public entry fun set_pause_status(account: signer, pause:bool) {
118 | amm_router::set_pause_status(&account, pause);
119 | }
120 | }
--------------------------------------------------------------------------------
/aptos/sources/amm_swap.move:
--------------------------------------------------------------------------------
1 | module cetus_amm::amm_swap {
2 | use std::string;
3 | use std::error;
4 | use std::signer;
5 | use std::option;
6 |
7 | use aptos_framework::event::{Self, EventHandle};
8 | use aptos_framework::account::{Self, new_event_handle};
9 | use aptos_framework::coin::{Self, Coin, BurnCapability, MintCapability};
10 | use cetus_amm::amm_utils;
11 | use cetus_amm::amm_config::{Self, assert_admin};
12 | use cetus_amm::amm_math::{Self, sqrt, min};
13 | use aptos_std::type_info;
14 |
15 | friend cetus_amm::amm_router;
16 |
17 | const MINIMUM_LIQUIDITY: u128 = 10;
18 |
19 | //
20 | // Errors
21 | //
22 |
23 | const EINVALID_COIN_PAIR: u64 = 4001;
24 | const EACCOUNT_NOT_EXISTED: u64 = 4002;
25 | const ELIQUIDITY_INSUFFICIENT_MINTED: u64 = 4003;
26 | const ELIQUIDITY_SWAP_BURN_CALC_INVALID: u64 = 4004;
27 | const ECOIN_INSUFFICIENT: u64 = 4005;
28 | const ESWAPOUT_CALC_INVALID: u64 = 4006;
29 | const EPOOL_DOSE_NOT_EXIST: u64 = 4007;
30 | const EPOOL_ALREADY_EXISTS: u64 = 4008;
31 | const ELIQUIDITY_CALC_INVALID: u64 = 4009;
32 | const EFUNCTION_DEPRECATED: u64 = 4010;
33 |
34 | const EQUAL: u8 = 0;
35 | const LESS_THAN: u8 = 1;
36 | const GREATER_THAN: u8 = 2;
37 |
38 | struct PoolLiquidityCoin {}
39 |
40 | struct Pool has key {
41 | coin_a: Coin,
42 | coin_b: Coin,
43 |
44 | mint_capability: MintCapability>,
45 | burn_capability: BurnCapability>,
46 |
47 | locked_liquidity: Coin>,
48 |
49 | protocol_fee_to: address
50 | }
51 |
52 | struct InitPoolEvent has store, drop {
53 | coin_a_info: type_info::TypeInfo,
54 | coin_b_info: type_info::TypeInfo,
55 | account: address,
56 | protocol_fee_to: address,
57 | }
58 |
59 | struct AddLiquidityEvent has store, drop {
60 | liquidity: u128,
61 | account: address,
62 | coin_a_info: type_info::TypeInfo,
63 | coin_b_info: type_info::TypeInfo,
64 | amount_a: u128,
65 | amount_b: u128,
66 | }
67 |
68 | struct RemoveLiquidityEvent has store, drop {
69 | liquidity: u128,
70 | account: address,
71 | coin_a_info: type_info::TypeInfo,
72 | coin_b_info: type_info::TypeInfo,
73 | amount_a: u128,
74 | amount_b: u128,
75 | }
76 |
77 | struct SwapEvent has store, drop {
78 | coin_a_info: type_info::TypeInfo,
79 | coin_b_info: type_info::TypeInfo,
80 | account: address,
81 | a_in: u128,
82 | a_out: u128,
83 | b_in: u128,
84 | b_out: u128,
85 | }
86 |
87 | struct SwapFeeEvent has store, drop {
88 | coin_a_info: type_info::TypeInfo,
89 | coin_b_info: type_info::TypeInfo,
90 | account: address,
91 | fee_address: address,
92 | fee_a_out: u128,
93 | fee_b_out: u128,
94 | }
95 |
96 | struct PoolSwapEventHandle has key {
97 | init_pool_events: EventHandle,
98 | add_liquidity_events: EventHandle,
99 | remove_liquidity_events: EventHandle,
100 | swap_events: EventHandle,
101 | swap_fee_events: EventHandle,
102 | }
103 |
104 | public fun init_pool(_account: &signer, _protocol_fee_to: address){
105 | abort EFUNCTION_DEPRECATED
106 | }
107 |
108 | public(friend) fun init_pool_v2(account: &signer, protocol_fee_to: address) acquires PoolSwapEventHandle {
109 | //check coin type
110 | amm_utils::assert_is_coin();
111 | amm_utils::assert_is_coin();
112 |
113 | assert!(!exists>(amm_config::admin_address()), EPOOL_ALREADY_EXISTS);
114 | assert!(!exists>(amm_config::admin_address()), EPOOL_ALREADY_EXISTS);
115 |
116 | //check admin
117 | assert_admin(account);
118 |
119 | //check protocol_fee_to existed
120 | assert!(
121 | account::exists_at(protocol_fee_to),
122 | error::not_found(EACCOUNT_NOT_EXISTED));
123 |
124 | //reigister lp coin
125 | let(burn_capability, mint_capability) = register_liquidity_coin(account);
126 |
127 | //make pool
128 | let pool = make_pool(protocol_fee_to, burn_capability, mint_capability);
129 | move_to(account, pool);
130 |
131 | //init event handle
132 | init_event_handle(account);
133 |
134 | //emit init pool event
135 | emit_init_pool_event(signer::address_of(account), protocol_fee_to);
136 | }
137 |
138 | public fun mint_and_emit_event(
139 | _account: &signer,
140 | _coinA: Coin,
141 | _coinB: Coin): Coin>{
142 | abort EFUNCTION_DEPRECATED
143 | }
144 |
145 | public(friend) fun mint_and_emit_event_v2(
146 | account: address,
147 | coinA: Coin,
148 | coinB: Coin): Coin> acquires Pool, PoolSwapEventHandle {
149 | let amount_a = (coin::value(&coinA) as u128);
150 | let amount_b = (coin::value(&coinB) as u128);
151 | let liquidity_coin = mint(coinA,coinB);
152 | let event_handle = borrow_global_mut(amm_config::admin_address());
153 | event::emit_event(&mut event_handle.add_liquidity_events,AddLiquidityEvent{
154 | liquidity: (coin::value>(&liquidity_coin) as u128),
155 | account: account,
156 | coin_a_info:type_info::type_of(),
157 | coin_b_info:type_info::type_of(),
158 | amount_a,
159 | amount_b
160 | });
161 | liquidity_coin
162 | }
163 |
164 | fun mint(
165 | coinA: Coin,
166 | coinB: Coin): Coin> acquires Pool {
167 | amm_config::assert_pause();
168 |
169 | let (reserve_a, reserve_b) = get_reserves();
170 |
171 | let pool = borrow_global_mut>(amm_config::admin_address());
172 |
173 | // get deposited amounts
174 | let amountA = (coin::value(&coinA) as u128);
175 | let amountB = (coin::value(&coinB) as u128);
176 |
177 | let total_supply = (*option::borrow(&coin::supply>()) as u128);
178 | let liquidity : u128;
179 | if (total_supply == 0) {
180 | liquidity = sqrt(amountA * amountB) - MINIMUM_LIQUIDITY;
181 | let locked_liquidity = coin::mint>((MINIMUM_LIQUIDITY as u64), &pool.mint_capability); // permanently lock the first MINIMUM_LIQUIDITY tokens
182 | coin::merge(&mut pool.locked_liquidity, locked_liquidity);
183 | } else {
184 | assert!(amountB == amm_math::quote(amountA, reserve_a, reserve_b)
185 | || amountA == amm_math::quote(amountB, reserve_b, reserve_a), error::internal(ELIQUIDITY_CALC_INVALID));
186 | liquidity = min(amm_math::safe_mul_div_u128(amountA,total_supply,reserve_a),
187 | amm_math::safe_mul_div_u128(amountB,total_supply,reserve_b));
188 | };
189 |
190 | assert!(liquidity > 0, error::invalid_argument(ELIQUIDITY_INSUFFICIENT_MINTED));
191 |
192 | coin::merge(&mut pool.coin_a, coinA);
193 | coin::merge(&mut pool.coin_b, coinB);
194 |
195 | coin::mint>((liquidity as u64), &pool.mint_capability)
196 | }
197 |
198 | public fun burn_and_emit_event(
199 | _account: &signer,
200 | _to_burn: Coin>) : (Coin, Coin) {
201 | abort EFUNCTION_DEPRECATED
202 | }
203 |
204 | public(friend) fun burn_and_emit_event_v2(
205 | account: address,
206 | to_burn: Coin>) : (Coin, Coin) acquires Pool, PoolSwapEventHandle {
207 | let liquidity = (coin::value>(&to_burn) as u128);
208 | let (a_token, b_token) = burn(to_burn);
209 | let event_handle = borrow_global_mut(amm_config::admin_address());
210 | let amount_a = (coin::value(&a_token) as u128);
211 | let amount_b = (coin::value(&b_token) as u128);
212 | event::emit_event(&mut event_handle.remove_liquidity_events, RemoveLiquidityEvent {
213 | liquidity,
214 | account: account,
215 | coin_a_info:type_info::type_of(),
216 | coin_b_info:type_info::type_of(),
217 | amount_a,
218 | amount_b,
219 | });
220 | (a_token, b_token)
221 | }
222 |
223 | fun burn(to_burn: Coin>): (Coin, Coin) acquires Pool {
224 | amm_config::assert_pause();
225 |
226 | let to_burn_value = (coin::value(&to_burn) as u128);
227 | let pool = borrow_global_mut>(amm_config::admin_address());
228 | let reserve_a = (coin::value(&pool.coin_a) as u128);
229 | let reserve_b = (coin::value(&pool.coin_b) as u128);
230 | let total_supply = *option::borrow(&coin::supply>());
231 | let amount0 = (amm_math::safe_mul_div_u128(to_burn_value,reserve_a,total_supply) as u64);
232 | let amount1 = (amm_math::safe_mul_div_u128(to_burn_value,reserve_b,total_supply) as u64);
233 | assert!(amount0 > 0 && amount1 > 0, error::internal(ELIQUIDITY_SWAP_BURN_CALC_INVALID));
234 |
235 | coin::burn(to_burn, &pool.burn_capability);
236 |
237 | (coin::extract(&mut pool.coin_a , amount0), coin::extract(&mut pool.coin_b, amount1))
238 | }
239 |
240 | public fun swap_and_emit_event(
241 | _account: &signer,
242 | _coin_a_in: Coin,
243 | _coin_b_out: u128,
244 | _coin_b_in: Coin,
245 | _coin_a_out: u128
246 | ) :(Coin, Coin, Coin, Coin) {
247 | abort EFUNCTION_DEPRECATED
248 | }
249 |
250 | public(friend) fun swap_and_emit_event_v2(
251 | account: address,
252 | coin_a_in: Coin,
253 | coin_b_out: u128,
254 | coin_b_in: Coin,
255 | coin_a_out: u128
256 | ) :(Coin, Coin, Coin, Coin) acquires Pool, PoolSwapEventHandle {
257 | let coin_a_in_value = (coin::value(&coin_a_in) as u128);
258 | let coin_b_in_value = (coin::value(&coin_b_in) as u128);
259 | let (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee) = swap_v2(coin_a_in, coin_b_out, coin_b_in, coin_a_out);
260 | let event_handle = borrow_global_mut(amm_config::admin_address());
261 | event::emit_event(
262 | &mut event_handle.swap_events,
263 | SwapEvent {
264 | coin_a_info: type_info::type_of(),
265 | coin_b_info: type_info::type_of(),
266 | account: account,
267 | a_in: coin_a_in_value,
268 | a_out: (coin::value(&coin_a_out) as u128),
269 | b_in: coin_b_in_value,
270 | b_out: (coin::value(&coin_b_out) as u128)
271 | }
272 | );
273 | (coin_a_out, coin_b_out, coin_a_fee, coin_b_fee)
274 | }
275 |
276 | public fun swap(
277 | _coin_a_in: Coin,
278 | _coin_b_out: u128,
279 | _coin_b_in: Coin,
280 | _coin_a_out: u128,
281 | ): (Coin, Coin, Coin