├── .gitignore ├── logo.png ├── sui ├── contracts │ ├── sources │ │ ├── admin.move │ │ ├── utils.move │ │ ├── test │ │ │ ├── utils_tests.move │ │ │ ├── test_coins.move │ │ │ ├── test_utils.move │ │ │ ├── fees_tests.move │ │ │ ├── quote_tests.move │ │ │ └── amm_tests.move │ │ ├── errors.move │ │ ├── events.move │ │ ├── quote.move │ │ ├── fees.move │ │ └── amm.move │ ├── Move.toml │ └── Move.lock └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | output.bpl 3 | .coverage_map.* 4 | .trace -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interest-protocol/memez-swap/HEAD/logo.png -------------------------------------------------------------------------------- /sui/contracts/sources/admin.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm_admin { 2 | 3 | public struct Admin has key, store { 4 | id: UID 5 | } 6 | 7 | #[allow(unused_function)] 8 | fun init(ctx: &mut TxContext) { 9 | transfer::transfer(Admin { id: object::new(ctx) }, tx_context::sender(ctx)); 10 | } 11 | 12 | #[test_only] 13 | public fun init_for_testing(ctx: &mut TxContext) { 14 | init(ctx); 15 | } 16 | } -------------------------------------------------------------------------------- /sui/README.md: -------------------------------------------------------------------------------- 1 | ## Sui Contracts 2 | 3 | ### Quick Start 4 | 5 | Make sure you have the latest version of the Sui binaries installed on your machine 6 | 7 | [Instructions here](https://docs.sui.io/devnet/build/install) 8 | 9 | #### Run tests 10 | 11 | **To run the tests on the dex directory** 12 | 13 | ```bash 14 | cd contracts 15 | sui move test 16 | ``` 17 | 18 | ### Publish 19 | 20 | ```bash 21 | cd contracts 22 | sui client publish --gas-budget 500000000 23 | ``` 24 | -------------------------------------------------------------------------------- /sui/contracts/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "MemezSwap" 3 | edition = "2024.beta" 4 | license = "MIT" 5 | authors = ["Jose Cerqueira (jose@interestprotocol.com)"] 6 | version = "1.0.0" 7 | 8 | [dependencies] 9 | Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet", override = true } 10 | SuiTears = { git = "https://github.com/interest-protocol/suitears.git", subdir = "contracts", rev = "develop" } 11 | MemezV2Invariant = { git = "https://github.com/interest-protocol/memez-v2-invariant.git", rev = "main" } 12 | 13 | [addresses] 14 | amm = "0x0" 15 | std = "0x1" 16 | sui = "0x2" 17 | -------------------------------------------------------------------------------- /sui/contracts/sources/utils.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm_utils { 2 | 3 | use std::type_name; 4 | 5 | use suitears::comparator; 6 | 7 | use amm::memez_amm_errors as errors; 8 | 9 | public(package) fun are_coins_ordered(): bool { 10 | let coin_a_type_name = type_name::get(); 11 | let coin_b_type_name = type_name::get(); 12 | 13 | assert!(coin_a_type_name != coin_b_type_name, errors::select_different_coins()); 14 | 15 | comparator::compare(&coin_a_type_name, &coin_b_type_name).lt() 16 | } 17 | 18 | public(package) fun is_coin_x(): bool { 19 | comparator::compare(&type_name::get(), &type_name::get()).lt() 20 | } 21 | } -------------------------------------------------------------------------------- /sui/contracts/sources/test/utils_tests.move: -------------------------------------------------------------------------------- 1 | #[test_only] 2 | module amm::utils_tests { 3 | 4 | use sui::{ 5 | sui::SUI, 6 | test_utils::assert_eq, 7 | }; 8 | 9 | use amm::{ 10 | eth::ETH, 11 | memez_amm_utils::{ 12 | is_coin_x, 13 | are_coins_ordered 14 | } 15 | }; 16 | 17 | public struct ABC {} 18 | 19 | public struct CAB {} 20 | 21 | #[test] 22 | fun test_are_coins_ordered() { 23 | assert_eq(are_coins_ordered(), true); 24 | assert_eq(are_coins_ordered(), false); 25 | assert_eq(are_coins_ordered(), true); 26 | assert_eq(are_coins_ordered(), false); 27 | } 28 | 29 | #[test] 30 | fun test_is_coin_x() { 31 | assert_eq(is_coin_x(), true); 32 | assert_eq(is_coin_x(), false); 33 | assert_eq(is_coin_x(), true); 34 | assert_eq(is_coin_x(), false); 35 | // does not throw 36 | assert_eq(is_coin_x(), false); 37 | } 38 | 39 | #[test] 40 | #[expected_failure] 41 | fun test_are_coins_ordered_same_coin() { 42 | are_coins_ordered(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sui/contracts/Move.lock: -------------------------------------------------------------------------------- 1 | # @generated by Move, please check-in and do not edit manually. 2 | 3 | [move] 4 | version = 0 5 | manifest_digest = "DBF3D8765309D3F5D1C382FB1A643D0D5CCADB266AA5F4663D39BAAE33FAF6E0" 6 | deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600" 7 | 8 | dependencies = [ 9 | { name = "Sui" }, 10 | { name = "SuiTears" }, 11 | ] 12 | 13 | [[move.package]] 14 | name = "MoveStdlib" 15 | source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/testnet", subdir = "crates/sui-framework/packages/move-stdlib" } 16 | 17 | [[move.package]] 18 | name = "Sui" 19 | source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/testnet", subdir = "crates/sui-framework/packages/sui-framework" } 20 | 21 | dependencies = [ 22 | { name = "MoveStdlib" }, 23 | ] 24 | 25 | [[move.package]] 26 | name = "SuiTears" 27 | source = { git = "https://github.com/interest-protocol/suitears.git", rev = "develop", subdir = "contracts" } 28 | 29 | dependencies = [ 30 | { name = "MoveStdlib" }, 31 | { name = "Sui" }, 32 | ] 33 | 34 | [move.toolchain-version] 35 | compiler-version = "1.27.2" 36 | edition = "2024.beta" 37 | flavor = "sui" 38 | 39 | [env] 40 | 41 | [env.devnet-m2] 42 | chain-id = "ba63bf7f" 43 | original-published-id = "0x096e99cfb6d4443ea673745081d268a00e982652e4b8d836218e4452fd0bdc3d" 44 | latest-published-id = "0x096e99cfb6d4443ea673745081d268a00e982652e4b8d836218e4452fd0bdc3d" 45 | published-version = "1" 46 | -------------------------------------------------------------------------------- /sui/contracts/sources/errors.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm_errors { 2 | 3 | const ENotEnoughFundsToLend: u64 = 0; 4 | const EFeeIsTooHigh: u64 = 1; 5 | const ESelectDifferentCoins: u64 = 2; 6 | const EProvideBothCoins: u64 = 3; 7 | const ECoinsMustBeOrdered: u64 = 4; 8 | const EPoolAlreadyDeployed: u64 = 5; 9 | const ESlippage: u64 = 6; 10 | const ENoZeroCoin: u64 = 7; 11 | const EInvalidInvariant: u64 = 8; 12 | const EPoolIsLocked: u64 = 9; 13 | const EWrongRepayAmount: u64 = 10; 14 | const EInsufficientLiquidity: u64 = 11; 15 | const EWrongPool: u64 = 12; 16 | const EInvalidBurnCoin: u64 = 13; 17 | 18 | public(package) fun not_enough_funds_to_lend(): u64 { 19 | ENotEnoughFundsToLend 20 | } 21 | 22 | public(package) fun fee_is_too_high(): u64 { 23 | EFeeIsTooHigh 24 | } 25 | 26 | public(package) fun select_different_coins(): u64 { 27 | ESelectDifferentCoins 28 | } 29 | 30 | public(package) fun provide_both_coins(): u64 { 31 | EProvideBothCoins 32 | } 33 | 34 | public(package) fun coins_must_be_ordered(): u64 { 35 | ECoinsMustBeOrdered 36 | } 37 | 38 | public(package) fun pool_already_deployed(): u64 { 39 | EPoolAlreadyDeployed 40 | } 41 | 42 | public(package) fun slippage(): u64 { 43 | ESlippage 44 | } 45 | 46 | public(package) fun no_zero_coin(): u64 { 47 | ENoZeroCoin 48 | } 49 | 50 | public(package) fun invalid_invariant(): u64 { 51 | EInvalidInvariant 52 | } 53 | 54 | public(package) fun pool_is_locked(): u64 { 55 | EPoolIsLocked 56 | } 57 | 58 | public(package) fun wrong_repay_amount(): u64 { 59 | EWrongRepayAmount 60 | } 61 | 62 | public(package) fun insufficient_liquidity(): u64 { 63 | EInsufficientLiquidity 64 | } 65 | 66 | public(package) fun wrong_pool(): u64 { 67 | EWrongPool 68 | } 69 | 70 | public(package) fun burn_coin(): u64 { 71 | EInvalidBurnCoin 72 | } 73 | } -------------------------------------------------------------------------------- /sui/contracts/sources/test/test_coins.move: -------------------------------------------------------------------------------- 1 | #[test_only] 2 | module amm::eth { 3 | use sui::coin; 4 | 5 | public struct ETH has drop {} 6 | 7 | #[lint_allow(share_owned)] 8 | fun init(witness: ETH, ctx: &mut TxContext) { 9 | let (treasury_cap, metadata) = coin::create_currency( 10 | witness, 11 | 9, 12 | b"ETH", 13 | b"Ether", 14 | b"Ethereum Native Coin", 15 | option::none(), 16 | ctx 17 | ); 18 | 19 | transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); 20 | transfer::public_share_object(metadata); 21 | } 22 | 23 | #[test_only] 24 | public fun init_for_testing(ctx: &mut TxContext) { 25 | init(ETH {}, ctx); 26 | } 27 | } 28 | 29 | #[test_only] 30 | module amm::btc { 31 | use sui::coin; 32 | 33 | public struct BTC has drop {} 34 | 35 | #[lint_allow(share_owned)] 36 | fun init(witness: BTC, ctx: &mut TxContext) { 37 | let (treasury_cap, metadata) = coin::create_currency( 38 | witness, 39 | 9, 40 | b"BTC", 41 | b"Bitcoin", 42 | b"Bitcoin Native Coin", 43 | option::none(), 44 | ctx 45 | ); 46 | 47 | transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); 48 | transfer::public_share_object(metadata); 49 | } 50 | 51 | #[test_only] 52 | public fun init_for_testing(ctx: &mut TxContext) { 53 | init(BTC {}, ctx); 54 | } 55 | } 56 | 57 | #[test_only] 58 | module amm::usdc { 59 | use sui::coin; 60 | 61 | public struct USDC has drop {} 62 | 63 | #[lint_allow(share_owned)] 64 | fun init(witness: USDC, ctx: &mut TxContext) { 65 | let (treasury_cap, metadata) = coin::create_currency( 66 | witness, 67 | 6, 68 | b"USDC", 69 | b"USD Coin", 70 | b"USD Stable Coin by Circle", 71 | option::none(), 72 | ctx 73 | ); 74 | 75 | transfer::public_transfer(treasury_cap, tx_context::sender(ctx)); 76 | transfer::public_share_object(metadata); 77 | } 78 | 79 | #[test_only] 80 | public fun init_for_testing(ctx: &mut TxContext) { 81 | init(USDC {}, ctx); 82 | } 83 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Memez Swap](https://www.memez.gg/) 2 | 3 |

4 | 5 | An AMM designed to safely pump Meme coins. 6 | 7 | ## Functionality 8 | 9 | ### Memez Swap 10 | 11 | The Memez Swap allows users to deploy Meme Pools and earn trading fees. The pools only support swapping. They do not expose liquidity management functions. This means that it is impossible to rug as there is no LpCoin. The liquidity is always locked forever at deployment. 12 | 13 | The DEX uses the famous `k = x * y` popularized by [Uniswap](https://uniswap.org/whitepaper.pdf). 14 | 15 | It supports the following operations: 16 | 17 | - Create Pool to get the fun going. 18 | - Swap to retire or jeet. 19 | - Referrals to pump together. 20 | - Flash loans for extra bling bling! 21 | 22 | Thats it!! 23 | 24 | ### Deployer 25 | 26 | The deployer is the user who creates the pool. He receives a `DeployerNFT` that earns trading fees for life. Meme coin devs can earn revenue to fund their marketing activities for life. 27 | 28 | ### Referrals 29 | 30 | The more apes the better. Memez has a native referral system. Degens can refer other degens to earn extra money. We are ALL gonna make it. 31 | 32 | ### Fees 33 | 34 | Swap fees play a crucial role in Memez Swap. Fees are configurable per pool by the protocol admin. 35 | 36 | #### Liquidity Fee 37 | 38 | The pool takes a liquidity fee to ensure that base liquidity increases over time. This is to ensure pumps and dumps do not hitler the chart. 39 | 40 | #### Burn Fee 41 | 42 | The pool can burn a % of a coin in every swap to increase its market cap. We send the coin to the 0x0 address instead of shrinking the coin supply for pumpnomics. 43 | 44 | #### Admin Fee 45 | 46 | The protocol takes a small percentage in each swap to fund development to deploy more degen dApps. 47 | 48 | #### Deployer Fee 49 | 50 | The deployer of the pool earns trading fees eternally. Since he is the only liquidity provider, this can fund the `dev` to keep cooking and buy pay KOLs and shillers. 51 | 52 | ## Contracts 53 | 54 | - [Sui Contracts](./sui) 55 | 56 | ## Contact Us 57 | 58 | - Twitter: [@interest_dinero](https://twitter.com/interest_dinero) 59 | - Discord: https://discord.gg/interest 60 | - Telegram: https://t.me/interestprotocol 61 | - Email: [contact@interestprotocol.com](mailto:contact@interestprotocol.com) 62 | - Medium: [@interestprotocol](https://medium.com/@interestprotocol) 63 | -------------------------------------------------------------------------------- /sui/contracts/sources/events.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm_events { 2 | 3 | use std::type_name::{Self, TypeName}; 4 | 5 | use sui::event::emit; 6 | 7 | use amm::memez_amm_fees::Fees; 8 | 9 | public struct NewPool has copy, drop { 10 | pool: address, 11 | deployer: address, 12 | amount_x: u64, 13 | amount_y: u64, 14 | coin_x: TypeName, 15 | coin_y: TypeName 16 | } 17 | 18 | public struct Swap has copy, drop { 19 | pool: address, 20 | amount_in: u64, 21 | swap_amount: T 22 | } 23 | 24 | public struct UpdateFees has copy, drop { 25 | pool: address, 26 | fees: Fees 27 | } 28 | 29 | public struct TakeAdminFees has copy, drop { 30 | pool: address, 31 | amount_x: u64, 32 | amount_y: u64 33 | } 34 | 35 | public struct TakeDeployerFees has copy, drop { 36 | pool: address, 37 | amount_x: u64, 38 | amount_y: u64 39 | } 40 | 41 | public struct AddBurnCoin has copy, drop { 42 | pool: address, 43 | burn_coin: TypeName 44 | } 45 | 46 | public struct RemoveBurnCoin has copy, drop { 47 | pool: address 48 | } 49 | 50 | public(package) fun new_pool( 51 | pool: address, 52 | deployer: address, 53 | amount_x: u64, 54 | amount_y: u64 55 | ) { 56 | emit(NewPool{ pool, deployer, amount_x, amount_y, coin_x: type_name::get(), coin_y: type_name::get() }); 57 | } 58 | 59 | public(package) fun swap( 60 | pool: address, 61 | amount_in: u64, 62 | swap_amount: T 63 | ) { 64 | emit(Swap { pool, amount_in, swap_amount }); 65 | } 66 | 67 | public(package) fun update_fees(pool: address, fees: Fees) { 68 | emit(UpdateFees { pool, fees }); 69 | } 70 | 71 | public(package) fun take_admin_fees(pool: address, amount_x: u64, amount_y: u64) { 72 | emit(TakeAdminFees { pool, amount_x, amount_y }); 73 | } 74 | 75 | public(package) fun take_deployer_fees(pool: address, amount_x: u64, amount_y: u64) { 76 | emit(TakeDeployerFees { pool, amount_x, amount_y }); 77 | } 78 | 79 | public(package) fun add_burn_coin(pool: address, burn_coin: TypeName) { 80 | emit(AddBurnCoin { pool, burn_coin }); 81 | } 82 | 83 | public(package) fun remove_burn_coin(pool: address) { 84 | emit(RemoveBurnCoin { pool }); 85 | } 86 | } -------------------------------------------------------------------------------- /sui/contracts/sources/quote.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm_quote { 2 | 3 | use std::type_name::{Self, TypeName}; 4 | 5 | use amm::{ 6 | memez_amm_fees::Fees, 7 | memez_amm::MemezPool, 8 | memez_amm_utils::is_coin_x 9 | }; 10 | 11 | use memez_v2_invariant::memez_v2_invariant; 12 | 13 | public fun amount_out(pool: &MemezPool, amount_in: u64): u64 { 14 | if (is_coin_x()) { 15 | let (balance_x, balance_y, fees, burn_coin) = get_pool_data(pool); 16 | 17 | memez_v2_invariant::get_amount_out(sub_fees_out(fees, amount_in, burn_coin), balance_x, balance_y) 18 | } else { 19 | let (balance_x, balance_y, fees, burn_coin) = get_pool_data(pool); 20 | 21 | memez_v2_invariant::get_amount_out(sub_fees_out(fees, amount_in, burn_coin), balance_y, balance_x) 22 | } 23 | } 24 | 25 | public fun amount_in(pool: &MemezPool, amount_out: u64): u64 { 26 | 27 | if (is_coin_x()) { 28 | let (balance_x, balance_y, fees, burn_coin) = get_pool_data(pool); 29 | 30 | sub_fees_in(fees, memez_v2_invariant::get_amount_in(amount_out, balance_x, balance_y), burn_coin) 31 | } else { 32 | let (balance_x, balance_y, fees, burn_coin) = get_pool_data(pool); 33 | 34 | sub_fees_in(fees, memez_v2_invariant::get_amount_in(amount_out, balance_y, balance_x), burn_coin) 35 | } 36 | } 37 | 38 | fun sub_fees_in(fees: Fees, amount: u64, burn_coin: Option): u64 { 39 | if (is_burn_coin(burn_coin)) 40 | fees.get_burn_amount_initial_amount( 41 | fees.get_swap_amount_initial_amount(amount) 42 | ) 43 | else 44 | fees.get_swap_amount_initial_amount(amount) 45 | } 46 | 47 | fun sub_fees_out(fees: Fees, amount: u64, burn_coin: Option): u64 { 48 | let burn_fee = if (is_burn_coin(burn_coin)) fees.get_burn_amount(amount) else 0; 49 | let swap_fee = fees.get_swap_amount(amount - burn_fee); 50 | amount - burn_fee - swap_fee 51 | } 52 | 53 | fun is_burn_coin(mut burn_coin: Option): bool { 54 | if (burn_coin.is_some()) 55 | type_name::get() == burn_coin.extract() 56 | else 57 | false 58 | } 59 | 60 | fun get_pool_data(pool: &MemezPool): (u64, u64, Fees, Option) { 61 | let fees = pool.fees(); 62 | let balance_x = pool.balance_x(); 63 | let balance_y = pool.balance_y(); 64 | let burn_coin = pool.burn_coin(); 65 | 66 | (balance_x, balance_y, fees, burn_coin) 67 | } 68 | } -------------------------------------------------------------------------------- /sui/contracts/sources/test/test_utils.move: -------------------------------------------------------------------------------- 1 | #[test_only] 2 | module amm::deploy_utils { 3 | 4 | use sui::{ 5 | coin::mint_for_testing, 6 | test_scenario::{Self as test, Scenario, next_tx, ctx} 7 | }; 8 | 9 | use amm::{ 10 | btc::{Self, BTC}, 11 | eth::{Self, ETH}, 12 | usdc::{Self, USDC}, 13 | memez_amm_admin::{Self, Admin}, 14 | memez_amm::{Self, MemezPool, Registry}, 15 | }; 16 | 17 | const BURN_FEE: u256 = 150_000_000_000_000_000; // 15% 18 | 19 | public fun set_up(test: &mut Scenario) { 20 | let (alice, _) = people(); 21 | 22 | next_tx(test, alice); 23 | { 24 | btc::init_for_testing(ctx(test)); 25 | eth::init_for_testing(ctx(test)); 26 | usdc::init_for_testing(ctx(test)); 27 | memez_amm_admin::init_for_testing(ctx(test)); 28 | }; 29 | } 30 | 31 | public fun deploy_eth_usdc_pool(test: &mut Scenario, eth_amount: u64, usdc_amount: u64) { 32 | let (alice, _) = people(); 33 | 34 | set_up(test); 35 | 36 | next_tx(test, alice); 37 | 38 | { 39 | let mut registry = test::take_shared(test); 40 | 41 | let deployer_nft = memez_amm::new( 42 | &mut registry, 43 | mint_for_testing(eth_amount, ctx(test)), 44 | mint_for_testing(usdc_amount, ctx(test)), 45 | ctx(test) 46 | ); 47 | 48 | transfer::public_transfer(deployer_nft, alice); 49 | 50 | test::return_shared(registry); 51 | }; 52 | } 53 | 54 | public fun deploy_btc_eth_pool(test: &mut Scenario, btc_amount: u64, eth_amount: u64) { 55 | let (alice, _) = people(); 56 | 57 | set_up(test); 58 | 59 | next_tx(test, alice); 60 | { 61 | let mut registry = test::take_shared(test); 62 | 63 | let deployer_nft = memez_amm::new( 64 | &mut registry, 65 | mint_for_testing(btc_amount, ctx(test)), 66 | mint_for_testing(eth_amount, ctx(test)), 67 | ctx(test) 68 | ); 69 | 70 | transfer::public_transfer(deployer_nft, alice); 71 | 72 | test::return_shared(registry); 73 | }; 74 | 75 | next_tx(test, alice); 76 | { 77 | let admin = test.take_from_sender(); 78 | 79 | let mut pool = test.take_shared(); 80 | 81 | pool.update_fees(&admin, option::none(), option::some(BURN_FEE), option::none(), option::none()); 82 | 83 | test.return_to_sender(admin); 84 | test::return_shared(pool); 85 | }; 86 | } 87 | 88 | public fun scenario(): Scenario { test::begin(@0x1) } 89 | 90 | public fun people():(address, address) { (@0xBEEF, @0x1337)} 91 | } -------------------------------------------------------------------------------- /sui/contracts/sources/fees.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm_fees { 2 | 3 | use suitears::math256::mul_div_up; 4 | 5 | use amm::memez_amm_errors as errors; 6 | 7 | const PRECISION: u256 = 1_000_000_000_000_000_000; 8 | const MAX_BURN_FEE: u256 = 500_000_000_000_000_000; // 50% 9 | const MAX_SWAP_FEE: u256 = 25_000_000_000_000_000; // 2.5% 10 | const MAX_ADMIN_FEE: u256 = 300_000_000_000_000_000; // 30% 11 | const MAX_LIQUIDITY_FEE: u256 = 500_000_000_000_000_000; // 50% 12 | 13 | public struct Fees has store, copy, drop { 14 | swap: u256, 15 | // Applied before the swap fee 16 | burn: u256, 17 | // They are a % of the swap fee 18 | admin: u256, 19 | liquidity: u256, 20 | } 21 | 22 | public fun new( 23 | swap: u256, 24 | burn: u256, 25 | admin: u256, 26 | liquidity: u256 27 | ): Fees { 28 | Fees { 29 | swap, 30 | burn, 31 | admin, 32 | liquidity 33 | } 34 | } 35 | 36 | public(package) fun swap(self: &Fees): u256 { 37 | self.swap 38 | } 39 | 40 | public(package) fun burn(self: &Fees): u256 { 41 | self.burn 42 | } 43 | 44 | public(package) fun admin(self: &Fees): u256 { 45 | self.admin 46 | } 47 | 48 | public(package) fun liquidity(self: &Fees): u256 { 49 | self.liquidity 50 | } 51 | 52 | public(package) fun update_swap(self: &mut Fees, mut fee: Option) { 53 | if (option::is_none(&fee)) return; 54 | let fee = option::extract(&mut fee); 55 | 56 | assert!(MAX_SWAP_FEE >= fee, errors::fee_is_too_high()); 57 | self.swap = fee; 58 | } 59 | 60 | public(package) fun update_burn(self: &mut Fees, mut fee: Option) { 61 | if (option::is_none(&fee)) return; 62 | let fee = option::extract(&mut fee); 63 | 64 | assert!(MAX_BURN_FEE >= fee, errors::fee_is_too_high()); 65 | self.burn = fee; 66 | } 67 | 68 | public(package) fun update_admin(self: &mut Fees, mut fee: Option) { 69 | if (option::is_none(&fee)) return; 70 | let fee = option::extract(&mut fee); 71 | 72 | assert!(MAX_ADMIN_FEE >= fee, errors::fee_is_too_high()); 73 | self.admin = fee; 74 | } 75 | 76 | public(package) fun update_liquidity(self: &mut Fees, mut fee: Option) { 77 | if (option::is_none(&fee)) return; 78 | let fee = option::extract(&mut fee); 79 | 80 | assert!(MAX_LIQUIDITY_FEE >= fee, errors::fee_is_too_high()); 81 | self.liquidity = fee; 82 | } 83 | 84 | public(package) fun get_swap_amount(self: &Fees, amount: u64): u64 { 85 | get_fee_amount(amount, self.swap) 86 | } 87 | 88 | public(package) fun get_burn_amount(self: &Fees, amount: u64): u64 { 89 | get_fee_amount(amount, self.burn) 90 | } 91 | 92 | public(package) fun get_admin_amount(self: &Fees, amount: u64): u64 { 93 | get_fee_amount(amount, self.admin) 94 | } 95 | 96 | public(package) fun get_liquidity_amount(self: &Fees, amount: u64): u64 { 97 | get_fee_amount(amount, self.liquidity) 98 | } 99 | 100 | public(package) fun get_swap_amount_initial_amount(self: &Fees, amount: u64): u64 { 101 | get_initial_amount(amount, self.swap) 102 | } 103 | 104 | public(package) fun get_burn_amount_initial_amount(self: &Fees, amount: u64): u64 { 105 | get_initial_amount(amount, self.burn) 106 | } 107 | 108 | fun get_fee_amount(x: u64, percent: u256): u64 { 109 | (mul_div_up((x as u256), percent, PRECISION) as u64) 110 | } 111 | 112 | fun get_initial_amount(x: u64, percent: u256): u64 { 113 | (mul_div_up((x as u256), PRECISION, PRECISION - percent) as u64) 114 | } 115 | } -------------------------------------------------------------------------------- /sui/contracts/sources/test/fees_tests.move: -------------------------------------------------------------------------------- 1 | #[test_only] 2 | module amm::fees_tests { 3 | use sui::{ 4 | test_utils::assert_eq, 5 | test_scenario::{Self as test, next_tx} 6 | }; 7 | 8 | use amm::memez_amm_fees as fees; 9 | 10 | use amm::deploy_utils::{people, scenario}; 11 | 12 | const MAX_BURN_FEE: u256 = 500_000_000_000_000_000; // 50% 13 | const MAX_SWAP_FEE: u256 = 25_000_000_000_000_000; // 2.5% 14 | const MAX_ADMIN_FEE: u256 = 300_000_000_000_000_000; // 30% 15 | const MAX_LIQUIDITY_FEE: u256 = 500_000_000_000_000_000; // 30% 16 | const ONE_PER_CENT: u256 = 10_000_000_000_000_000; 17 | 18 | #[test] 19 | fun sets_initial_state_correctly() { 20 | let mut scenario = scenario(); 21 | let (alice, _) = people(); 22 | 23 | let test = &mut scenario; 24 | 25 | next_tx(test, alice); 26 | { 27 | 28 | let fees = fees::new( 29 | ONE_PER_CENT * 2, 30 | ONE_PER_CENT * 20, 31 | ONE_PER_CENT * 30, 32 | ONE_PER_CENT * 25 33 | ); 34 | 35 | assert_eq(fees.swap(), ONE_PER_CENT * 2); 36 | assert_eq(fees.burn(), ONE_PER_CENT * 20); 37 | assert_eq(fees.admin(), ONE_PER_CENT * 30); 38 | assert_eq(fees.liquidity(), ONE_PER_CENT * 25); 39 | 40 | }; 41 | test::end(scenario); 42 | } 43 | 44 | #[test] 45 | fun updates_fees_correctly() { 46 | let mut scenario = scenario(); 47 | let (alice, _) = people(); 48 | 49 | let test = &mut scenario; 50 | 51 | next_tx(test, alice); 52 | { 53 | let mut fees = fees::new( 54 | ONE_PER_CENT * 2, 55 | ONE_PER_CENT * 20, 56 | ONE_PER_CENT * 30, 57 | ONE_PER_CENT * 25 58 | ); 59 | 60 | fees::update_swap(&mut fees, option::some(MAX_SWAP_FEE)); 61 | fees::update_burn(&mut fees, option::some(MAX_BURN_FEE)); 62 | fees::update_admin(&mut fees, option::some(MAX_ADMIN_FEE)); 63 | fees::update_liquidity(&mut fees, option::some(MAX_LIQUIDITY_FEE)); 64 | 65 | 66 | assert_eq(fees.swap(), MAX_SWAP_FEE); 67 | assert_eq(fees.burn(), MAX_BURN_FEE); 68 | assert_eq(fees.admin(), MAX_ADMIN_FEE); 69 | assert_eq(fees.liquidity(), MAX_LIQUIDITY_FEE); 70 | 71 | fees::update_swap(&mut fees, option::none()); 72 | fees::update_burn(&mut fees, option::none()); 73 | fees::update_admin(&mut fees, option::none()); 74 | fees::update_liquidity(&mut fees, option::none()); 75 | 76 | assert_eq(fees.swap(), MAX_SWAP_FEE); 77 | assert_eq(fees.burn(), MAX_BURN_FEE); 78 | assert_eq(fees.admin(), MAX_ADMIN_FEE); 79 | assert_eq(fees.liquidity(), MAX_LIQUIDITY_FEE); 80 | 81 | fees::update_swap(&mut fees, option::some(0)); 82 | fees::update_burn(&mut fees, option::some(0)); 83 | fees::update_admin(&mut fees, option::some(0)); 84 | fees::update_liquidity(&mut fees, option::some(0)); 85 | 86 | assert_eq(fees.swap(), 0); 87 | assert_eq(fees.burn(), 0); 88 | assert_eq(fees.admin(), 0); 89 | assert_eq(fees.liquidity(), 0); 90 | 91 | fees::update_swap(&mut fees, option::some(1)); 92 | fees::update_burn(&mut fees, option::some(2)); 93 | fees::update_admin(&mut fees, option::some(3)); 94 | fees::update_liquidity(&mut fees, option::some(4)); 95 | 96 | assert_eq(fees.swap(), 1); 97 | assert_eq(fees.burn(), 2); 98 | assert_eq(fees.admin(), 3); 99 | assert_eq(fees.liquidity(), 4); 100 | }; 101 | test::end(scenario); 102 | } 103 | 104 | #[test] 105 | fun calculates_fees_properly() { 106 | let mut scenario = scenario(); 107 | let (alice, _) = people(); 108 | 109 | let test = &mut scenario; 110 | 111 | next_tx(test, alice); 112 | { 113 | let fees = fees::new( 114 | ONE_PER_CENT * 10, 115 | ONE_PER_CENT * 20, 116 | ONE_PER_CENT * 30, 117 | ONE_PER_CENT * 25 118 | ); 119 | 120 | let amount = 100; 121 | 122 | assert_eq(fees::get_swap_amount(&fees, amount), 10); 123 | assert_eq(fees::get_burn_amount(&fees, amount), 20); 124 | assert_eq(fees::get_admin_amount(&fees, amount), 30); 125 | assert_eq(fees::get_liquidity_amount(&fees, amount), 25); 126 | 127 | assert_eq(fees::get_swap_amount_initial_amount(&fees, amount), 112); // rounds up 128 | assert_eq(fees::get_burn_amount_initial_amount(&fees, amount), 125); // rounds up 129 | }; 130 | test::end(scenario); 131 | } 132 | 133 | #[test] 134 | #[expected_failure(abort_code = amm::memez_amm_errors::EFeeIsTooHigh, location = amm::memez_amm_fees)] 135 | fun aborts_max_swap_fee() { 136 | let mut scenario = scenario(); 137 | let (alice, _) = people(); 138 | 139 | let test = &mut scenario; 140 | 141 | next_tx(test, alice); 142 | { 143 | let mut fees = fees::new( 144 | ONE_PER_CENT * 10, 145 | ONE_PER_CENT * 20, 146 | ONE_PER_CENT * 30, 147 | ONE_PER_CENT * 25 148 | ); 149 | 150 | fees.update_swap(option::some(MAX_SWAP_FEE + 1)); 151 | }; 152 | test::end(scenario); 153 | } 154 | 155 | #[test] 156 | #[expected_failure(abort_code = amm::memez_amm_errors::EFeeIsTooHigh, location = amm::memez_amm_fees)] 157 | fun aborts_max_burn_fee() { 158 | let mut scenario = scenario(); 159 | let (alice, _) = people(); 160 | 161 | let test = &mut scenario; 162 | 163 | next_tx(test, alice); 164 | { 165 | let mut fees = fees::new( 166 | ONE_PER_CENT * 10, 167 | ONE_PER_CENT * 20, 168 | ONE_PER_CENT * 30, 169 | ONE_PER_CENT * 25 170 | ); 171 | 172 | fees.update_burn(option::some(MAX_BURN_FEE + 1)); 173 | }; 174 | test::end(scenario); 175 | } 176 | 177 | #[test] 178 | #[expected_failure(abort_code = amm::memez_amm_errors::EFeeIsTooHigh, location = amm::memez_amm_fees)] 179 | fun aborts_max_admin_fee() { 180 | let mut scenario = scenario(); 181 | let (alice, _) = people(); 182 | 183 | let test = &mut scenario; 184 | 185 | next_tx(test, alice); 186 | { 187 | let mut fees = fees::new( 188 | ONE_PER_CENT * 10, 189 | ONE_PER_CENT * 20, 190 | ONE_PER_CENT * 30, 191 | ONE_PER_CENT * 25 192 | ); 193 | 194 | fees.update_admin(option::some(MAX_ADMIN_FEE + 1)); 195 | }; 196 | test::end(scenario); 197 | } 198 | 199 | #[test] 200 | #[expected_failure(abort_code = amm::memez_amm_errors::EFeeIsTooHigh, location = amm::memez_amm_fees)] 201 | fun aborts_max_liquidity_fee() { 202 | let mut scenario = scenario(); 203 | let (alice, _) = people(); 204 | 205 | let test = &mut scenario; 206 | 207 | next_tx(test, alice); 208 | { 209 | let mut fees = fees::new( 210 | ONE_PER_CENT * 10, 211 | ONE_PER_CENT * 20, 212 | ONE_PER_CENT * 30, 213 | ONE_PER_CENT * 25 214 | ); 215 | 216 | fees.update_liquidity(option::some(MAX_ADMIN_FEE + 1)); 217 | }; 218 | test::end(scenario); 219 | } 220 | } -------------------------------------------------------------------------------- /sui/contracts/sources/test/quote_tests.move: -------------------------------------------------------------------------------- 1 | #[test_only] 2 | module amm::quote_tests { 3 | 4 | use sui::{ 5 | test_utils::assert_eq, 6 | test_scenario::{Self as test, Scenario, next_tx, ctx} 7 | }; 8 | 9 | use amm::{ 10 | btc::BTC, 11 | eth::ETH, 12 | usdc::USDC, 13 | memez_amm_admin::Admin, 14 | memez_amm_quote as quote, 15 | memez_amm_fees::{Self as fees, Fees}, 16 | memez_amm::{Self, Registry, MemezPool}, 17 | deploy_utils::{people, scenario, deploy_btc_eth_pool, deploy_eth_usdc_pool} 18 | }; 19 | 20 | use memez_v2_invariant::memez_v2_invariant; 21 | 22 | const USDC_DECIMAL_SCALAR: u64 = 1_000_000; 23 | const BTC_DECIMAL_SCALAR: u64 = 1_000_000_000; 24 | const ETH_DECIMAL_SCALAR: u64 = 1_000_000_000; 25 | 26 | #[test] 27 | fun test_quote_amount_out_no_burn_fee() { 28 | let mut scenario = scenario(); 29 | let (alice, _) = people(); 30 | 31 | let scenario_mut = &mut scenario; 32 | 33 | set_up_test(scenario_mut); 34 | deploy_eth_usdc_pool(scenario_mut, 15 * ETH_DECIMAL_SCALAR, 37500 * USDC_DECIMAL_SCALAR); 35 | 36 | next_tx(scenario_mut, alice); 37 | { 38 | let request = request(scenario_mut); 39 | 40 | let amount_in = 3 * ETH_DECIMAL_SCALAR; 41 | let burn_fee = fees::get_burn_amount(&request.pool_fees, amount_in); 42 | let swap_fee = fees::get_swap_amount(&request.pool_fees, amount_in - burn_fee); 43 | 44 | let expected_amount_out = memez_v2_invariant::get_amount_out(amount_in - burn_fee - swap_fee, 15 * ETH_DECIMAL_SCALAR, 37500 * USDC_DECIMAL_SCALAR); 45 | 46 | assert_eq(quote::amount_out(&request.pool, amount_in), expected_amount_out); 47 | 48 | destroy_request(request); 49 | }; 50 | 51 | next_tx(scenario_mut, alice); 52 | { 53 | let request = request(scenario_mut); 54 | 55 | let amount_in = 14637 * USDC_DECIMAL_SCALAR; 56 | 57 | let burn_fee = fees::get_burn_amount(&request.pool_fees, amount_in); 58 | let swap_fee = fees::get_swap_amount(&request.pool_fees, amount_in - burn_fee); 59 | 60 | let expected_amount_out = memez_v2_invariant::get_amount_out(amount_in - burn_fee - swap_fee, 37500 * USDC_DECIMAL_SCALAR, 15 * ETH_DECIMAL_SCALAR); 61 | 62 | assert_eq(quote::amount_out(&request.pool, amount_in), expected_amount_out); 63 | 64 | destroy_request(request); 65 | }; 66 | test::end(scenario); 67 | } 68 | 69 | #[test] 70 | fun test_quote_amount_in_no_burn_fee() { 71 | let mut scenario = scenario(); 72 | let (alice, _) = people(); 73 | 74 | let scenario_mut = &mut scenario; 75 | 76 | set_up_test(scenario_mut); 77 | deploy_eth_usdc_pool(scenario_mut, 15 * ETH_DECIMAL_SCALAR, 37500 * USDC_DECIMAL_SCALAR); 78 | 79 | next_tx(scenario_mut, alice); 80 | { 81 | let request = request(scenario_mut); 82 | 83 | let amount_out = 6 * ETH_DECIMAL_SCALAR; 84 | let amount_in = memez_v2_invariant::get_amount_in(amount_out, 15 * ETH_DECIMAL_SCALAR, 37500 * USDC_DECIMAL_SCALAR); 85 | 86 | let amount_in_before_swap_fee = fees::get_swap_amount_initial_amount(&request.pool_fees, amount_in); 87 | let amount_in_before_burn_fee = fees::get_burn_amount_initial_amount(&request.pool_fees, amount_in_before_swap_fee); 88 | 89 | assert_eq(quote::amount_in(&request.pool, amount_out), amount_in_before_burn_fee); 90 | 91 | destroy_request(request); 92 | }; 93 | 94 | next_tx(scenario_mut, alice); 95 | { 96 | let request = request(scenario_mut); 97 | 98 | let amount_out = 2999 * USDC_DECIMAL_SCALAR; 99 | let amount_in = memez_v2_invariant::get_amount_in(amount_out, 37500 * USDC_DECIMAL_SCALAR, 15 * ETH_DECIMAL_SCALAR); 100 | 101 | let amount_in_before_swap_fee = fees::get_swap_amount_initial_amount(&request.pool_fees, amount_in); 102 | let amount_in_before_burn_fee = fees::get_burn_amount_initial_amount(&request.pool_fees, amount_in_before_swap_fee); 103 | 104 | assert_eq(amount_in_before_burn_fee, amount_in_before_swap_fee); 105 | assert_eq(quote::amount_in(&request.pool, amount_out), amount_in_before_burn_fee); 106 | 107 | destroy_request(request); 108 | }; 109 | 110 | test::end(scenario); 111 | } 112 | 113 | #[test] 114 | fun test_quote_amount_out_burn_fee() { 115 | let mut scenario = scenario(); 116 | let (alice, _) = people(); 117 | 118 | let scenario_mut = &mut scenario; 119 | 120 | set_up_test(scenario_mut); 121 | deploy_btc_eth_pool(scenario_mut, 3 * BTC_DECIMAL_SCALAR, 15 * ETH_DECIMAL_SCALAR); 122 | 123 | next_tx(scenario_mut, alice); 124 | { 125 | let mut request = request(scenario_mut); 126 | 127 | let admin = test::take_from_sender(scenario_mut); 128 | 129 | memez_amm::add_burn_coin(&mut request.pool, &admin); 130 | 131 | let amount_in = 3 * ETH_DECIMAL_SCALAR; 132 | let swap_fee = fees::get_swap_amount(&request.pool_fees, amount_in); 133 | 134 | let expected_amount_out = memez_v2_invariant::get_amount_out(amount_in - swap_fee, 15 * ETH_DECIMAL_SCALAR, 3 * BTC_DECIMAL_SCALAR); 135 | 136 | assert_eq(quote::amount_out(&request.pool, amount_in), expected_amount_out); 137 | 138 | test::return_to_sender(scenario_mut, admin); 139 | destroy_request(request); 140 | }; 141 | 142 | next_tx(scenario_mut, alice); 143 | { 144 | let request = request(scenario_mut); 145 | 146 | let amount_in = 1 * BTC_DECIMAL_SCALAR / 10; 147 | 148 | let burn_fee = fees::get_burn_amount(&request.pool_fees, amount_in); 149 | let swap_fee = fees::get_swap_amount(&request.pool_fees, amount_in - burn_fee); 150 | 151 | let expected_amount_out = memez_v2_invariant::get_amount_out(amount_in - burn_fee - swap_fee, 3 * BTC_DECIMAL_SCALAR, 15 * ETH_DECIMAL_SCALAR); 152 | 153 | assert_eq(quote::amount_out(&request.pool, amount_in), expected_amount_out); 154 | 155 | destroy_request(request); 156 | }; 157 | test::end(scenario); 158 | } 159 | 160 | #[test] 161 | fun test_quote_amount_in_burn_fee() { 162 | let mut scenario = scenario(); 163 | let (alice, _) = people(); 164 | 165 | let scenario_mut = &mut scenario; 166 | 167 | set_up_test(scenario_mut); 168 | deploy_btc_eth_pool(scenario_mut, 3 * BTC_DECIMAL_SCALAR, 15 * ETH_DECIMAL_SCALAR); 169 | 170 | next_tx(scenario_mut, alice); 171 | { 172 | let mut request = request(scenario_mut); 173 | 174 | let admin = test::take_from_sender(scenario_mut); 175 | 176 | memez_amm::add_burn_coin(&mut request.pool, &admin); 177 | 178 | let amount_out = 3 * ETH_DECIMAL_SCALAR; 179 | 180 | let amount_in = memez_v2_invariant::get_amount_in(amount_out, 3 * BTC_DECIMAL_SCALAR, 15 * ETH_DECIMAL_SCALAR); 181 | let amount_in_before_swap_fee = fees::get_swap_amount_initial_amount(&request.pool_fees, amount_in); 182 | let amount_in_before_burn_fee = fees::get_burn_amount_initial_amount(&request.pool_fees, amount_in_before_swap_fee); 183 | 184 | assert_eq(quote::amount_in(&request.pool, amount_out), amount_in_before_burn_fee); 185 | 186 | test::return_to_sender(scenario_mut, admin); 187 | destroy_request(request); 188 | }; 189 | 190 | next_tx(scenario_mut, alice); 191 | { 192 | let request = request(scenario_mut); 193 | 194 | let amount_out = 1 * BTC_DECIMAL_SCALAR / 10; 195 | 196 | let amount_in = memez_v2_invariant::get_amount_in(amount_out, 15 * ETH_DECIMAL_SCALAR, 3 * BTC_DECIMAL_SCALAR); 197 | let amount_in_before_swap_fee = fees::get_swap_amount_initial_amount(&request.pool_fees, amount_in); 198 | 199 | assert_eq(quote::amount_in(&request.pool, amount_out), amount_in_before_swap_fee); 200 | 201 | destroy_request(request); 202 | }; 203 | test::end(scenario); 204 | } 205 | 206 | // Set up 207 | 208 | public struct Request { 209 | registry: Registry, 210 | pool: MemezPool, 211 | pool_fees: Fees, 212 | } 213 | 214 | fun set_up_test(scenario_mut: &mut Scenario) { 215 | let (alice, _) = people(); 216 | 217 | next_tx(scenario_mut, alice); 218 | { 219 | memez_amm::init_for_testing(ctx(scenario_mut)); 220 | }; 221 | } 222 | 223 | fun request(scenario_mut: &Scenario): Request { 224 | let registry = test::take_shared(scenario_mut); 225 | let pool_address = memez_amm::pool_address(®istry); 226 | let pool = test::take_shared_by_id( 227 | scenario_mut, object::id_from_address(option::destroy_some(pool_address)) 228 | ); 229 | let pool_fees = memez_amm::fees(&pool); 230 | 231 | Request { 232 | registry, 233 | pool, 234 | pool_fees, 235 | } 236 | } 237 | 238 | fun destroy_request(request: Request) { 239 | let Request { registry, pool, pool_fees: _ } = request; 240 | 241 | test::return_shared(registry); 242 | test::return_shared(pool); 243 | } 244 | } -------------------------------------------------------------------------------- /sui/contracts/sources/amm.move: -------------------------------------------------------------------------------- 1 | module amm::memez_amm { 2 | // === Imports === 3 | 4 | use std::type_name::{Self, TypeName}; 5 | 6 | use sui::{ 7 | coin::Coin, 8 | dynamic_field as df, 9 | table::{Self, Table}, 10 | transfer::share_object, 11 | balance::{Self, Balance}, 12 | }; 13 | 14 | use suitears::math256::mul_div_up; 15 | 16 | use amm::{ 17 | memez_amm_admin::Admin, 18 | memez_amm_utils as utils, 19 | memez_amm_errors as errors, 20 | memez_amm_events as events, 21 | memez_amm_fees::{Self as fees, Fees} 22 | }; 23 | 24 | use memez_v2_invariant::memez_v2_invariant::{invariant_, get_amount_out}; 25 | 26 | // === Constants === 27 | const PRECISION: u256 = 1_000_000_000_000_000_000; 28 | const INITIAL_SWAP_FEE: u256 = 1_000_000_000_000_000; // 1% 29 | const INITIAL_ADMIN_FEE: u256 = 200_000_000_000_000_000; // 20% 30 | const INITIAL_LIQUIDITY_FEE: u256 = 500_000_000_000_000_000; // 50% 31 | const FLASH_LOAN_FEE_PERCENT: u256 = 5_000_000_000_000_000; //0.5% 32 | const BURN_WALLET: address = @0x0; 33 | 34 | // === Structs === 35 | 36 | public struct Registry has key { 37 | id: UID, 38 | pools: Table, 39 | } 40 | 41 | public struct MemezPool has key { 42 | id: UID 43 | } 44 | 45 | public struct RegistryKey has drop {} 46 | 47 | public struct PoolStateKey has drop, copy, store {} 48 | 49 | public struct PoolState has store { 50 | fees: Fees, 51 | locked: bool, 52 | balance_x: Balance, 53 | balance_y: Balance, 54 | burn_coin: Option, 55 | admin_balance_x: Balance, 56 | admin_balance_y: Balance, 57 | deployer_balance_x: Balance, 58 | deployer_balance_y: Balance, 59 | } 60 | 61 | public struct Deployer has key, store { 62 | id: UID, 63 | pool: address 64 | } 65 | 66 | public struct SwapAmount has store, drop, copy { 67 | amount_out: u64, 68 | burn_fee: u64, 69 | admin_fee: u64, 70 | swap_fee: u64, 71 | liquidity_fee: u64, 72 | creator_fee: u64 73 | } 74 | 75 | public struct Invoice { 76 | pool_address: address, 77 | repay_amount_x: u64, 78 | repay_amount_y: u64, 79 | prev_k: u256 80 | } 81 | 82 | // === Public-Mutative Functions === 83 | 84 | #[allow(unused_function)] 85 | fun init(ctx: &mut TxContext) { 86 | share_object( 87 | Registry { 88 | id: object::new(ctx), 89 | pools: table::new(ctx), 90 | } 91 | ); 92 | } 93 | 94 | // === DEX === 95 | 96 | #[lint_allow(share_owned)] 97 | public fun new( 98 | registry: &mut Registry, 99 | coin_x: Coin, 100 | coin_y: Coin, 101 | ctx: &mut TxContext 102 | ): Deployer { 103 | new_pool_internal(registry, coin_x, coin_y, ctx) 104 | } 105 | 106 | public fun swap( 107 | pool: &mut MemezPool, 108 | coin_in: Coin, 109 | coin_min_value: u64, 110 | ctx: &mut TxContext 111 | ): Coin { 112 | assert!(coin_in.value() != 0, errors::no_zero_coin()); 113 | 114 | if (utils::is_coin_x()) 115 | swap_coin_x(pool, coin_in, coin_min_value, ctx) 116 | else 117 | swap_coin_y(pool, coin_in, coin_min_value, ctx) 118 | } 119 | 120 | // === Flash Loans === 121 | 122 | public fun flash_loan( 123 | pool: &mut MemezPool, 124 | amount_x: u64, 125 | amount_y: u64, 126 | ctx: &mut TxContext 127 | ): (Invoice, Coin, Coin) { 128 | let pool_state = pool_state_mut(pool); 129 | 130 | assert!(!pool_state.locked, errors::pool_is_locked()); 131 | 132 | pool_state.locked = true; 133 | 134 | let (balance_x, balance_y) = amounts(pool_state); 135 | 136 | let prev_k = invariant_(balance_x, balance_y); 137 | 138 | assert!(balance_x >= amount_x && balance_y >= amount_y, errors::not_enough_funds_to_lend()); 139 | 140 | let coin_x = pool_state.balance_x.split(amount_x).into_coin(ctx); 141 | let coin_y = pool_state.balance_y.split(amount_y).into_coin(ctx); 142 | 143 | let invoice = Invoice { 144 | pool_address: pool.id.uid_to_address(), 145 | repay_amount_x: amount_x + (mul_div_up((amount_x as u256), FLASH_LOAN_FEE_PERCENT, PRECISION) as u64), 146 | repay_amount_y: amount_y + (mul_div_up((amount_y as u256), FLASH_LOAN_FEE_PERCENT, PRECISION) as u64), 147 | prev_k 148 | }; 149 | 150 | (invoice, coin_x, coin_y) 151 | } 152 | 153 | public fun repay_flash_loan( 154 | pool: &mut MemezPool, 155 | invoice: Invoice, 156 | coin_x: Coin, 157 | coin_y: Coin 158 | ) { 159 | let Invoice { pool_address, repay_amount_x, repay_amount_y, prev_k } = invoice; 160 | 161 | assert!(pool.id.uid_to_address() == pool_address, errors::wrong_pool()); 162 | assert!(coin_x.value() >= repay_amount_x, errors::wrong_repay_amount()); 163 | assert!(coin_y.value() >= repay_amount_y, errors::wrong_repay_amount()); 164 | 165 | let pool_state = pool_state_mut(pool); 166 | 167 | pool_state.balance_x.join(coin_x.into_balance()); 168 | pool_state.balance_y.join(coin_y.into_balance()); 169 | 170 | let (balance_x, balance_y) = amounts(pool_state); 171 | 172 | let k = invariant_(balance_x, balance_y); 173 | 174 | assert!(k > prev_k, errors::invalid_invariant()); 175 | 176 | pool_state.locked = false; 177 | } 178 | 179 | // === Public-View Functions === 180 | 181 | public fun pools(registry: &Registry): &Table { 182 | ®istry.pools 183 | } 184 | 185 | public fun pool_address(registry: &Registry): Option
{ 186 | let registry_key = type_name::get>(); 187 | 188 | if (registry.pools.contains(registry_key)) 189 | option::some(*registry.pools.borrow(registry_key)) 190 | else 191 | option::none() 192 | } 193 | 194 | public fun exists_(registry: &Registry): bool { 195 | registry.pools.contains(type_name::get>()) 196 | } 197 | 198 | public fun burn_coin(pool: &MemezPool): Option { 199 | let pool_state = pool_state(pool); 200 | pool_state.burn_coin 201 | } 202 | 203 | public fun balance_x(pool: &MemezPool): u64 { 204 | let pool_state = pool_state(pool); 205 | pool_state.balance_x.value() 206 | } 207 | 208 | public fun balance_y(pool: &MemezPool): u64 { 209 | let pool_state = pool_state(pool); 210 | pool_state.balance_y.value() 211 | } 212 | 213 | public fun fees(pool: &MemezPool): Fees { 214 | let pool_state = pool_state(pool); 215 | pool_state.fees 216 | } 217 | 218 | public fun locked(pool: &MemezPool): bool { 219 | let pool_state = pool_state(pool); 220 | pool_state.locked 221 | } 222 | 223 | public fun admin_balance_x(pool: &MemezPool): u64 { 224 | let pool_state = pool_state(pool); 225 | pool_state.admin_balance_x.value() 226 | } 227 | 228 | public fun admin_balance_y(pool: &MemezPool): u64 { 229 | let pool_state = pool_state(pool); 230 | pool_state.admin_balance_y.value() 231 | } 232 | 233 | public fun deployer_balance_x(pool: &MemezPool): u64 { 234 | let pool_state = pool_state(pool); 235 | pool_state.deployer_balance_x.value() 236 | } 237 | 238 | public fun deployer_balance_y(pool: &MemezPool): u64 { 239 | let pool_state = pool_state(pool); 240 | pool_state.deployer_balance_y.value() 241 | } 242 | 243 | public fun repay_amount_x(invoice: &Invoice): u64 { 244 | invoice.repay_amount_x 245 | } 246 | 247 | public fun repay_amount_y(invoice: &Invoice): u64 { 248 | invoice.repay_amount_y 249 | } 250 | 251 | public fun previous_k(invoice: &Invoice): u256 { 252 | invoice.prev_k 253 | } 254 | 255 | // === Deployer Functions === 256 | 257 | public fun take_deployer_fees( 258 | deployer: &Deployer, 259 | pool: &mut MemezPool, 260 | ctx: &mut TxContext 261 | ): (Coin, Coin) { 262 | let pool_address = pool.id.uid_to_address(); 263 | 264 | assert!(deployer.pool == pool_address, errors::wrong_pool()); 265 | 266 | let pool_state = pool_state_mut(pool); 267 | 268 | let amount_x = pool_state.deployer_balance_x.value(); 269 | let amount_y = pool_state.deployer_balance_y.value(); 270 | 271 | events::take_deployer_fees(pool_address, amount_x, amount_y); 272 | 273 | ( 274 | pool_state.deployer_balance_x.withdraw_all().into_coin(ctx), 275 | pool_state.deployer_balance_y.withdraw_all().into_coin(ctx), 276 | ) 277 | } 278 | 279 | // === Admin Functions === 280 | 281 | public fun add_burn_coin( 282 | pool: &mut MemezPool, 283 | _: &Admin, 284 | ) { 285 | let pool_address = pool.id.uid_to_address(); 286 | 287 | assert!( 288 | type_name::get() == type_name::get() || 289 | type_name::get() == type_name::get(), 290 | errors::burn_coin() 291 | ); 292 | 293 | let pool_state = pool_state_mut(pool); 294 | 295 | pool_state.burn_coin.fill(type_name::get()); 296 | 297 | events::add_burn_coin(pool_address, type_name::get()); 298 | } 299 | 300 | public fun remove_burn_coin( 301 | pool: &mut MemezPool, 302 | _: &Admin, 303 | ) { 304 | let pool_address = pool.id.uid_to_address(); 305 | 306 | let pool_state = pool_state_mut(pool); 307 | 308 | pool_state.burn_coin = option::none(); 309 | events::remove_burn_coin(pool_address); 310 | } 311 | 312 | public fun update_fees( 313 | pool: &mut MemezPool, 314 | _: &Admin, 315 | swap: Option, 316 | burn: Option, 317 | admin: Option, 318 | liquidity: Option, 319 | ) { 320 | let pool_address = pool.id.uid_to_address(); 321 | let pool_state = pool_state_mut(pool); 322 | 323 | pool_state.fees.update_swap(swap); 324 | pool_state.fees.update_burn(burn); 325 | pool_state.fees.update_admin(admin); 326 | pool_state.fees.update_liquidity(liquidity); 327 | 328 | events::update_fees(pool_address, pool_state.fees); 329 | } 330 | 331 | public fun take_admin_fees( 332 | pool: &mut MemezPool, 333 | _: &Admin, 334 | ctx: &mut TxContext 335 | ): (Coin, Coin) { 336 | let pool_address = pool.id.uid_to_address(); 337 | let pool_state = pool_state_mut(pool); 338 | 339 | let amount_x = pool_state.admin_balance_x.value(); 340 | let amount_y = pool_state.admin_balance_y.value(); 341 | 342 | events::take_admin_fees(pool_address, amount_x, amount_y); 343 | 344 | ( 345 | pool_state.admin_balance_x.withdraw_all().into_coin(ctx), 346 | pool_state.admin_balance_y.withdraw_all().into_coin(ctx), 347 | ) 348 | } 349 | 350 | // === Private Functions === 351 | 352 | fun new_pool_internal( 353 | registry: &mut Registry, 354 | coin_x: Coin, 355 | coin_y: Coin, 356 | ctx: &mut TxContext 357 | ): Deployer { 358 | assert!(utils::are_coins_ordered(), errors::coins_must_be_ordered()); 359 | let coin_x_value = coin_x.value(); 360 | let coin_y_value = coin_y.value(); 361 | 362 | assert!(coin_x_value != 0 && coin_y_value != 0, errors::provide_both_coins()); 363 | 364 | let registry_key = type_name::get>(); 365 | 366 | assert!(!registry.pools.contains(registry_key), errors::pool_already_deployed()); 367 | 368 | let pool_state = PoolState { 369 | balance_x: coin_x.into_balance(), 370 | balance_y: coin_y.into_balance(), 371 | fees: new_fees(), 372 | locked: false, 373 | burn_coin: option::none(), 374 | deployer_balance_x: balance::zero(), 375 | deployer_balance_y: balance::zero(), 376 | admin_balance_x: balance::zero(), 377 | admin_balance_y: balance::zero(), 378 | }; 379 | 380 | let mut pool = MemezPool { 381 | id: object::new(ctx) 382 | }; 383 | 384 | let pool_address = pool.id.uid_to_address(); 385 | 386 | df::add(&mut pool.id, PoolStateKey {}, pool_state); 387 | 388 | registry.pools.add(registry_key, pool_address); 389 | 390 | let deployer = Deployer { 391 | id: object::new(ctx), 392 | pool: pool_address 393 | }; 394 | 395 | events::new_pool(pool_address, deployer.id.uid_to_address(), coin_x_value, coin_y_value); 396 | 397 | share_object(pool); 398 | 399 | deployer 400 | } 401 | 402 | fun swap_coin_x( 403 | pool: &mut MemezPool, 404 | mut coin_x: Coin, 405 | coin_y_min_value: u64, 406 | ctx: &mut TxContext 407 | ): Coin { 408 | let pool_address = object::uid_to_address(&pool.id); 409 | let pool_state = pool_state_mut(pool); 410 | assert!(!pool_state.locked, errors::pool_is_locked()); 411 | 412 | let coin_in_amount = coin_x.value(); 413 | 414 | let swap_amount = swap_amounts( 415 | pool_state, 416 | coin_in_amount, 417 | coin_y_min_value, 418 | true, 419 | ); 420 | 421 | if (swap_amount.burn_fee != 0) { 422 | let burn_coin = coin_x.split(swap_amount.burn_fee, ctx); 423 | transfer::public_transfer(burn_coin, BURN_WALLET); 424 | }; 425 | 426 | if (swap_amount.admin_fee != 0) { 427 | pool_state.admin_balance_x.join(coin_x.split(swap_amount.admin_fee, ctx).into_balance()); 428 | }; 429 | 430 | if (swap_amount.creator_fee != 0) { 431 | pool_state.deployer_balance_x.join(coin_x.split(swap_amount.creator_fee, ctx).into_balance()); 432 | }; 433 | 434 | pool_state.balance_x.join(coin_x.into_balance()); 435 | 436 | events::swap(pool_address, coin_in_amount, swap_amount); 437 | 438 | pool_state.balance_y.split(swap_amount.amount_out).into_coin(ctx) 439 | } 440 | 441 | fun swap_coin_y( 442 | pool: &mut MemezPool, 443 | mut coin_y: Coin, 444 | coin_x_min_value: u64, 445 | ctx: &mut TxContext 446 | ): Coin { 447 | let pool_address = pool.id.uid_to_address(); 448 | let pool_state = pool_state_mut(pool); 449 | assert!(!pool_state.locked, errors::pool_is_locked()); 450 | 451 | let coin_in_amount = coin_y.value(); 452 | 453 | let swap_amount = swap_amounts( 454 | pool_state, 455 | coin_in_amount, 456 | coin_x_min_value, 457 | false 458 | ); 459 | 460 | if (swap_amount.burn_fee != 0) { 461 | let burn_coin = coin_y.split(swap_amount.burn_fee, ctx); 462 | transfer::public_transfer(burn_coin, BURN_WALLET); 463 | }; 464 | 465 | if (swap_amount.admin_fee != 0) { 466 | pool_state.admin_balance_y.join(coin_y.split(swap_amount.admin_fee, ctx).into_balance()); 467 | }; 468 | 469 | if (swap_amount.creator_fee != 0) { 470 | pool_state.deployer_balance_y.join(coin_y.split(swap_amount.creator_fee, ctx).into_balance()); 471 | }; 472 | 473 | pool_state.balance_y.join(coin_y.into_balance()); 474 | 475 | events::swap(pool_address, coin_in_amount,swap_amount); 476 | 477 | pool_state.balance_x.split(swap_amount.amount_out).into_coin(ctx) 478 | } 479 | 480 | fun new_fees(): Fees { 481 | fees::new( 482 | INITIAL_SWAP_FEE, 483 | 0, 484 | INITIAL_ADMIN_FEE, 485 | INITIAL_LIQUIDITY_FEE 486 | ) 487 | } 488 | 489 | fun amounts(state: &PoolState): (u64, u64) { 490 | ( 491 | state.balance_x.value(), 492 | state.balance_y.value() 493 | ) 494 | } 495 | 496 | fun swap_amounts( 497 | pool_state: &PoolState, 498 | coin_in_amount: u64, 499 | coin_out_min_value: u64, 500 | is_x: bool 501 | ): SwapAmount { 502 | let (balance_x, balance_y) = amounts(pool_state); 503 | 504 | let prev_k = invariant_(balance_x, balance_y); 505 | 506 | let is_burn_coin = if (pool_state.burn_coin.is_some()) 507 | { 508 | let coin_in_type = if (is_x) type_name::get() else type_name::get(); 509 | coin_in_type == *pool_state.burn_coin.borrow() 510 | } 511 | else 512 | false; 513 | 514 | let burn_fee = if (is_burn_coin) pool_state.fees.get_burn_amount(coin_in_amount) else 0; 515 | let swap_fee = pool_state.fees.get_swap_amount(coin_in_amount - burn_fee); 516 | 517 | let liquidity_fee = pool_state.fees.get_liquidity_amount(swap_fee); 518 | let admin_fee = pool_state.fees.get_admin_amount(swap_fee); 519 | let creator_fee = swap_fee - admin_fee - liquidity_fee; 520 | 521 | let coin_in_amount = coin_in_amount - burn_fee - swap_fee; 522 | 523 | let amount_out = if (is_x) 524 | get_amount_out(coin_in_amount, balance_x, balance_y) 525 | else 526 | get_amount_out(coin_in_amount, balance_y, balance_x); 527 | 528 | assert!(amount_out >= coin_out_min_value, errors::slippage()); 529 | 530 | let new_k = if (is_x) 531 | invariant_(balance_x + coin_in_amount + liquidity_fee, balance_y - amount_out) 532 | else 533 | invariant_(balance_x - amount_out, balance_y + coin_in_amount + liquidity_fee); 534 | 535 | assert!(new_k >= prev_k, errors::invalid_invariant()); 536 | 537 | SwapAmount { 538 | amount_out, 539 | swap_fee, 540 | burn_fee, 541 | admin_fee, 542 | liquidity_fee, 543 | creator_fee 544 | } 545 | } 546 | 547 | fun pool_state(pool: &MemezPool): &PoolState { 548 | df::borrow(&pool.id, PoolStateKey {}) 549 | } 550 | 551 | fun pool_state_mut(pool: &mut MemezPool): &mut PoolState { 552 | df::borrow_mut(&mut pool.id, PoolStateKey {}) 553 | } 554 | 555 | // === Test Functions === 556 | 557 | #[test_only] 558 | public fun init_for_testing(ctx: &mut TxContext) { 559 | init(ctx); 560 | } 561 | } -------------------------------------------------------------------------------- /sui/contracts/sources/test/amm_tests.move: -------------------------------------------------------------------------------- 1 | // #[test_only] 2 | // module amm::interest_protocol_amm_tests { 3 | // use std::string::{utf8, to_ascii}; 4 | 5 | // use sui::{ 6 | // table, 7 | // test_utils::{destroy, assert_eq}, 8 | // test_scenario::{Self as test, Scenario, next_tx, ctx}, 9 | // coin::{Self, mint_for_testing, burn_for_testing, TreasuryCap, CoinMetadata} 10 | // }; 11 | 12 | // use suitears::math256; 13 | 14 | // use amm::{ 15 | // interest_amm_quote as quote, 16 | // btc::BTC, 17 | // eth::ETH, 18 | // usdc::USDC, 19 | // interest_amm_invariant, 20 | // ipx_btce_eth::{Self, IPX_BTCE_ETH}, 21 | // ipx_eth_usdc::{Self, IPX_ETH_USDC}, 22 | // interest_amm_fees::{Self as fees, Fees}, 23 | // interest_amm_admin::{Self as admin, Admin}, 24 | // interest_amm::{Self, Registry, InterestPool}, 25 | // deploy_utils::{people, scenario, deploy_coins, deploy_eth_usdc_pool, deploy_btc_eth_pool} 26 | // }; 27 | 28 | // const PRECISION: u256 = 1_000_000_000_000_000_000; 29 | // const MINIMUM_LIQUIDITY: u64 = 100; 30 | // const USDC_DECIMAL_SCALAR: u64 = 1_000_000; 31 | // const BTC_DECIMAL_SCALAR: u64 = 1_000_000_000; 32 | // const ETH_DECIMAL_SCALAR: u64 = 1_000_000_000; 33 | // const INITIAL_VOLATILE_FEE_PERCENT: u256 = 3_000_000_000_000_000; // 0.3% 34 | // const INITIAL_ADMIN_FEE: u256 = 200_000_000_000_000_000; // 20% 35 | // const FLASH_LOAN_FEE_PERCENT: u256 = 5_000_000_000_000_000; //0.5% 36 | // const MAX_FEE_PERCENT: u256 = 20_000_000_000_000_000; // 2% 37 | 38 | // #[test] 39 | // fun test_new_pool() { 40 | // let (mut scenario, alice, _) = start_test(); 41 | 42 | // let scenario_mut = &mut scenario; 43 | 44 | // next_tx(scenario_mut, alice); 45 | // { 46 | // ipx_eth_usdc::init_for_testing(ctx(scenario_mut)); 47 | // }; 48 | 49 | // let eth_amount = 10 * ETH_DECIMAL_SCALAR; 50 | // let usdc_amount = 25000 * USDC_DECIMAL_SCALAR; 51 | // let expected_shares = (math256::sqrt_down((eth_amount as u256) * (usdc_amount as u256)) as u64); 52 | 53 | // next_tx(scenario_mut, alice); 54 | // { 55 | // let mut registry = test::take_shared(scenario_mut); 56 | // let lp_coin_cap = test::take_from_sender>(scenario_mut); 57 | // let eth_metadata = test::take_shared>(scenario_mut); 58 | // let usdc_metadata = test::take_shared>(scenario_mut); 59 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 60 | 61 | // assert_eq(table::is_empty(interest_amm::pools(®istry)), true); 62 | 63 | // let lp_coin = interest_amm::new( 64 | // &mut registry, 65 | // mint_for_testing(eth_amount, ctx(scenario_mut)), 66 | // mint_for_testing(usdc_amount, ctx(scenario_mut)), 67 | // lp_coin_cap, 68 | // ð_metadata, 69 | // &usdc_metadata, 70 | // &mut lp_coin_metadata, 71 | // ctx(scenario_mut) 72 | // ); 73 | 74 | // assert_eq(coin::get_symbol(&lp_coin_metadata), to_ascii(utf8(b"ipx-ETH-USDC"))); 75 | // assert_eq(coin::get_name(&lp_coin_metadata), utf8(b"Interest AMM Ether USD Coin Lp Coin")); 76 | // assert_eq(interest_amm::exists_(®istry), true); 77 | // assert_eq(burn_for_testing(lp_coin), expected_shares); 78 | 79 | // test::return_shared(eth_metadata); 80 | // test::return_shared(usdc_metadata); 81 | // test::return_shared(lp_coin_metadata); 82 | // test::return_shared(registry); 83 | // }; 84 | 85 | // next_tx(scenario_mut, alice); 86 | // { 87 | // let request = request(scenario_mut); 88 | 89 | // assert_eq(interest_amm::lp_coin_supply(&request.pool), expected_shares + MINIMUM_LIQUIDITY); 90 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount); 91 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount); 92 | // assert_eq(interest_amm::seed_liquidity(&request.pool), MINIMUM_LIQUIDITY); 93 | // assert_eq(interest_amm::locked(&request.pool), false); 94 | // assert_eq(interest_amm::admin_balance_x(&request.pool), 0); 95 | // assert_eq(interest_amm::admin_balance_y(&request.pool), 0); 96 | 97 | // let fees = interest_amm::fees(&request.pool); 98 | 99 | // assert_eq(fees::fee_in_percent(&fees), INITIAL_VOLATILE_FEE_PERCENT); 100 | // assert_eq(fees::fee_out_percent(&fees), INITIAL_VOLATILE_FEE_PERCENT); 101 | // assert_eq(fees::admin_fee_percent(&fees), INITIAL_ADMIN_FEE); 102 | 103 | // destroy_request(request); 104 | // }; 105 | 106 | // test::end(scenario); 107 | // } 108 | 109 | // #[test] 110 | // fun test_volatile_swap() { 111 | // let (mut scenario, alice, _) = start_test(); 112 | 113 | // let scenario_mut = &mut scenario; 114 | 115 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 116 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 117 | 118 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 119 | 120 | // next_tx(scenario_mut, alice); 121 | // { 122 | // let mut request = request(scenario_mut); 123 | 124 | // let amount_in = 3 * ETH_DECIMAL_SCALAR; 125 | // let amount_in_fee = fees::get_fee_in_amount(&request.fees, amount_in); 126 | // let admin_in_fee = fees::get_admin_amount(&request.fees, amount_in_fee); 127 | // let expected_amount_out = interest_amm_invariant::get_amount_out(amount_in - amount_in_fee, eth_amount, usdc_amount); 128 | // let amount_out_fee = fees::get_fee_out_amount(&request.fees, expected_amount_out); 129 | // let admin_out_fee = fees::get_admin_amount(&request.fees, amount_out_fee); 130 | // let expected_amount_out = expected_amount_out - amount_out_fee; 131 | 132 | // let usdc_coin = interest_amm::swap( 133 | // &mut request.pool, 134 | // mint_for_testing(amount_in, ctx(scenario_mut)), 135 | // expected_amount_out, 136 | // ctx(scenario_mut) 137 | // ); 138 | 139 | // assert_eq(burn_for_testing(usdc_coin), expected_amount_out); 140 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount + amount_in - admin_in_fee); 141 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount - (expected_amount_out + admin_out_fee)); 142 | // assert_eq(interest_amm::admin_balance_x(&request.pool), admin_in_fee); 143 | // assert_eq(interest_amm::admin_balance_y(&request.pool), admin_out_fee); 144 | 145 | // destroy_request(request); 146 | // }; 147 | 148 | // next_tx(scenario_mut, alice); 149 | // { 150 | // let mut request = request(scenario_mut); 151 | 152 | // let eth_amount = interest_amm::balance_x(&request.pool); 153 | // let usdc_amount = interest_amm::balance_y(&request.pool); 154 | // let initial_admin_balance_x = interest_amm::admin_balance_x(&request.pool); 155 | // let initial_admin_balance_y = interest_amm::admin_balance_y(&request.pool); 156 | 157 | // let amount_in = 7777 * USDC_DECIMAL_SCALAR; 158 | // let amount_in_fee = fees::get_fee_in_amount(&request.fees, amount_in); 159 | // let admin_in_fee = fees::get_admin_amount(&request.fees, amount_in_fee); 160 | // let expected_amount_out = interest_amm_invariant::get_amount_out(amount_in - amount_in_fee, usdc_amount, eth_amount); 161 | // let amount_out_fee = fees::get_fee_out_amount(&request.fees, expected_amount_out); 162 | // let admin_out_fee = fees::get_admin_amount(&request.fees, amount_out_fee); 163 | // let expected_amount_out = expected_amount_out - amount_out_fee; 164 | 165 | // let eth_coin = interest_amm::swap( 166 | // &mut request.pool, 167 | // mint_for_testing(amount_in, ctx(scenario_mut)), 168 | // expected_amount_out, 169 | // ctx(scenario_mut) 170 | // ); 171 | 172 | // assert_eq(burn_for_testing(eth_coin), expected_amount_out); 173 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount - (expected_amount_out + admin_out_fee)); 174 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount + amount_in - admin_in_fee); 175 | // assert_eq(interest_amm::admin_balance_x(&request.pool), admin_out_fee + initial_admin_balance_x); 176 | // assert_eq(interest_amm::admin_balance_y(&request.pool), admin_in_fee + initial_admin_balance_y); 177 | 178 | // destroy_request(request); 179 | // }; 180 | 181 | // test::end(scenario); 182 | // } 183 | 184 | // #[test] 185 | // fun test_add_liquidity() { 186 | // let (mut scenario, alice, _) = start_test(); 187 | 188 | // let scenario_mut = &mut scenario; 189 | 190 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 191 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 192 | 193 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 194 | 195 | // next_tx(scenario_mut, alice); 196 | // { 197 | // let mut request = request(scenario_mut); 198 | 199 | // let amount_x = 20 * ETH_DECIMAL_SCALAR; 200 | // let amount_y = 27000 * USDC_DECIMAL_SCALAR; 201 | 202 | // let initial_lp_coin_supply = interest_amm::lp_coin_supply(&request.pool); 203 | 204 | // let (shares, optimal_x, optimal_y) = quote::add_liquidity(&request.pool, amount_x, amount_y); 205 | 206 | // let (lp_coin, eth_coin, usdc_coin) = interest_amm::add_liquidity( 207 | // &mut request.pool, 208 | // mint_for_testing(amount_x, ctx(scenario_mut)), 209 | // mint_for_testing(amount_y, ctx(scenario_mut)), 210 | // shares, 211 | // ctx(scenario_mut) 212 | // ); 213 | 214 | // assert_eq(burn_for_testing(lp_coin), shares); 215 | // assert_eq(burn_for_testing(eth_coin), amount_x - optimal_x); 216 | // assert_eq(burn_for_testing(usdc_coin), amount_y - optimal_y); 217 | // assert_eq(interest_amm::lp_coin_supply(&request.pool), shares + initial_lp_coin_supply); 218 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount + optimal_x); 219 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount + optimal_y); 220 | 221 | // destroy_request(request); 222 | // }; 223 | // test::end(scenario); 224 | // } 225 | 226 | // #[test] 227 | // fun test_remove_liquidity() { 228 | // let (mut scenario, alice, _) = start_test(); 229 | 230 | // let scenario_mut = &mut scenario; 231 | 232 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 233 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 234 | 235 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 236 | 237 | // next_tx(scenario_mut, alice); 238 | // { 239 | // let mut request = request(scenario_mut); 240 | 241 | // let initial_lp_coin_supply = interest_amm::lp_coin_supply(&request.pool); 242 | 243 | // let (expected_x, expected_y) = quote::remove_liquidity(&request.pool, initial_lp_coin_supply / 3); 244 | 245 | // let (eth_coin, usdc_coin) = interest_amm::remove_liquidity( 246 | // &mut request.pool, 247 | // mint_for_testing(initial_lp_coin_supply / 3, ctx(scenario_mut)), 248 | // expected_x, 249 | // expected_y, 250 | // ctx(scenario_mut) 251 | // ); 252 | 253 | // assert_eq(burn_for_testing(eth_coin), expected_x); 254 | // assert_eq(burn_for_testing(usdc_coin), expected_y); 255 | // assert_eq(interest_amm::lp_coin_supply(&request.pool), initial_lp_coin_supply - initial_lp_coin_supply / 3); 256 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount - expected_x); 257 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount - expected_y); 258 | 259 | // destroy_request(request); 260 | // }; 261 | // test::end(scenario); 262 | // } 263 | 264 | // #[test] 265 | // fun test_flash_loan() { 266 | // let (mut scenario, alice, _) = start_test(); 267 | 268 | // let scenario_mut = &mut scenario; 269 | 270 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 271 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 272 | 273 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 274 | 275 | // next_tx(scenario_mut, alice); 276 | // { 277 | // let mut request = request(scenario_mut); 278 | 279 | // let eth_coin_amount = 5 * ETH_DECIMAL_SCALAR; 280 | // let usdc_coin_amount = 1500 * USDC_DECIMAL_SCALAR; 281 | 282 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 283 | // &mut request.pool, 284 | // eth_coin_amount, 285 | // usdc_coin_amount, 286 | // ctx(scenario_mut) 287 | // ); 288 | 289 | // let invoice_repay_amount_x = interest_amm::repay_amount_x(&invoice); 290 | // let invoice_repay_amount_y = interest_amm::repay_amount_y(&invoice); 291 | 292 | // assert_eq(burn_for_testing(eth_coin), eth_coin_amount); 293 | // assert_eq(burn_for_testing(usdc_coin), usdc_coin_amount); 294 | // assert_eq(interest_amm::locked(&request.pool), true); 295 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount - eth_coin_amount); 296 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount - usdc_coin_amount); 297 | // assert_eq(invoice_repay_amount_x, eth_coin_amount + (math256::mul_div_up((eth_coin_amount as u256), FLASH_LOAN_FEE_PERCENT, PRECISION) as u64)); 298 | // assert_eq(invoice_repay_amount_y, usdc_coin_amount + (math256::mul_div_up((usdc_coin_amount as u256), FLASH_LOAN_FEE_PERCENT, PRECISION) as u64)); 299 | 300 | // interest_amm::repay_flash_loan( 301 | // &mut request.pool, 302 | // invoice, 303 | // mint_for_testing(invoice_repay_amount_x, ctx(scenario_mut)), 304 | // mint_for_testing(invoice_repay_amount_y, ctx(scenario_mut)) 305 | // ); 306 | 307 | // assert_eq(interest_amm::locked(&request.pool), false); 308 | // assert_eq(interest_amm::balance_x(&request.pool), eth_amount + invoice_repay_amount_x - eth_coin_amount); 309 | // assert_eq(interest_amm::balance_y(&request.pool), usdc_amount + invoice_repay_amount_y - usdc_coin_amount); 310 | 311 | // destroy_request(request); 312 | // }; 313 | 314 | // test::end(scenario); 315 | // } 316 | 317 | // #[test] 318 | // fun test_admin_fees_actions() { 319 | // let (mut scenario, alice, _) = start_test(); 320 | 321 | // let scenario_mut = &mut scenario; 322 | 323 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 324 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 325 | 326 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 327 | 328 | // next_tx(scenario_mut, alice); 329 | // { 330 | // let mut request = request(scenario_mut); 331 | // let admin_cap = test::take_from_sender(scenario_mut); 332 | 333 | // interest_amm::update_fees( 334 | // &admin_cap, 335 | // &mut request.pool, 336 | // option::some(MAX_FEE_PERCENT), 337 | // option::some(MAX_FEE_PERCENT), 338 | // option::none() 339 | // ); 340 | 341 | // let pool_fees = interest_amm::fees(&request.pool); 342 | // assert_eq(fees::fee_in_percent(&pool_fees), MAX_FEE_PERCENT); 343 | // assert_eq(fees::fee_out_percent(&pool_fees), MAX_FEE_PERCENT); 344 | 345 | // assert_eq(interest_amm::admin_balance_x(&request.pool), 0); 346 | // assert_eq(interest_amm::admin_balance_y(&request.pool), 0); 347 | 348 | // let mut i = 0; 349 | 350 | // while (10 > i) { 351 | // burn_for_testing(interest_amm::swap( 352 | // &mut request.pool, 353 | // mint_for_testing(eth_amount / 3, ctx(scenario_mut)), 354 | // 0, 355 | // ctx(scenario_mut) 356 | // )); 357 | 358 | // burn_for_testing(interest_amm::swap( 359 | // &mut request.pool, 360 | // mint_for_testing(usdc_amount / 3, ctx(scenario_mut)), 361 | // 0, 362 | // ctx(scenario_mut) 363 | // )); 364 | 365 | // i = i + 1; 366 | // }; 367 | 368 | // let admin_balance_x = interest_amm::admin_balance_x(&request.pool); 369 | // let admin_balance_y = interest_amm::admin_balance_y(&request.pool); 370 | 371 | // assert_eq(admin_balance_x != 0, true); 372 | // assert_eq(admin_balance_y != 0, true); 373 | 374 | // let (usdc_coin, eth_coin) = interest_amm::take_fees( 375 | // &admin_cap, 376 | // &mut request.pool, 377 | // ctx(scenario_mut) 378 | // ); 379 | 380 | // assert_eq(burn_for_testing(usdc_coin), admin_balance_x); 381 | // assert_eq(burn_for_testing(eth_coin), admin_balance_y); 382 | 383 | // assert_eq(interest_amm::admin_balance_x(&request.pool), 0); 384 | // assert_eq(interest_amm::admin_balance_y(&request.pool), 0); 385 | 386 | // test::return_to_sender(scenario_mut, admin_cap); 387 | // destroy_request(request); 388 | // }; 389 | // test::end(scenario); 390 | // } 391 | 392 | // #[test] 393 | // #[expected_failure(abort_code = amm::interest_amm_errors::ENotEnoughFundsToLend, location = amm::interest_amm)] 394 | // fun test_flash_loan_not_enough_balance_x() { 395 | // let (mut scenario, alice, _) = start_test(); 396 | 397 | // let scenario_mut = &mut scenario; 398 | 399 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 400 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 401 | 402 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 403 | 404 | // next_tx(scenario_mut, alice); 405 | // { 406 | // let mut request = request(scenario_mut); 407 | 408 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 409 | // &mut request.pool, 410 | // eth_amount + 1, 411 | // usdc_amount, 412 | // ctx(scenario_mut) 413 | // ); 414 | 415 | // interest_amm::repay_flash_loan( 416 | // &mut request.pool, 417 | // invoice, 418 | // eth_coin, 419 | // usdc_coin 420 | // ); 421 | 422 | // destroy_request(request); 423 | // }; 424 | // test::end(scenario); 425 | // } 426 | 427 | // #[test] 428 | // #[expected_failure(abort_code = amm::interest_amm_errors::ENotEnoughFundsToLend, location = amm::interest_amm)] 429 | // fun test_flash_loan_not_enough_balance_y() { 430 | // let (mut scenario, alice, _) = start_test(); 431 | 432 | // let scenario_mut = &mut scenario; 433 | 434 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 435 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 436 | 437 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 438 | 439 | // next_tx(scenario_mut, alice); 440 | // { 441 | // let mut request = request(scenario_mut); 442 | 443 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 444 | // &mut request.pool, 445 | // eth_amount, 446 | // usdc_amount + 1, 447 | // ctx(scenario_mut) 448 | // ); 449 | 450 | // interest_amm::repay_flash_loan( 451 | // &mut request.pool, 452 | // invoice, 453 | // eth_coin, 454 | // usdc_coin 455 | // ); 456 | 457 | // destroy_request(request); 458 | // }; 459 | 460 | // test::end(scenario); 461 | // } 462 | 463 | // #[test] 464 | // #[expected_failure(abort_code = amm::interest_amm_errors::EPoolIsLocked, location = amm::interest_amm)] 465 | // fun test_flash_loan_locked() { 466 | // let (mut scenario, alice, _) = start_test(); 467 | 468 | // let scenario_mut = &mut scenario; 469 | 470 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 471 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 472 | 473 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 474 | 475 | // next_tx(scenario_mut, alice); 476 | // { 477 | // let mut request = request(scenario_mut); 478 | 479 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 480 | // &mut request.pool, 481 | // 1, 482 | // 2, 483 | // ctx(scenario_mut) 484 | // ); 485 | 486 | // let (invoice2, eth_coin2, usdc_coin2) = interest_amm::flash_loan( 487 | // &mut request.pool, 488 | // 3, 489 | // 4, 490 | // ctx(scenario_mut) 491 | // ); 492 | 493 | // interest_amm::repay_flash_loan( 494 | // &mut request.pool, 495 | // invoice, 496 | // eth_coin, 497 | // usdc_coin 498 | // ); 499 | 500 | // interest_amm::repay_flash_loan( 501 | // &mut request.pool, 502 | // invoice2, 503 | // eth_coin2, 504 | // usdc_coin2 505 | // ); 506 | 507 | // destroy_request(request); 508 | // }; 509 | // test::end(scenario); 510 | // } 511 | 512 | // #[test] 513 | // #[expected_failure(abort_code = amm::interest_amm_errors::EWrongPool, location = amm::interest_amm)] 514 | // fun test_repay_wrong_pool() { 515 | // let (mut scenario, alice, _) = start_test(); 516 | 517 | // let scenario_mut = &mut scenario; 518 | 519 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 520 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 521 | 522 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 523 | // deploy_btc_eth_pool(scenario_mut, 2 * BTC_DECIMAL_SCALAR, 17 * ETH_DECIMAL_SCALAR); 524 | 525 | // next_tx(scenario_mut, alice); 526 | // { 527 | // let registry = test::take_shared(scenario_mut); 528 | // let pool_id = interest_amm::pool_address(®istry); 529 | // let mut pool_1 = test::take_shared_by_id(scenario_mut, object::id_from_address(option::destroy_some(pool_id))); 530 | // let pool_id = interest_amm::pool_address(®istry); 531 | // let mut pool_2 = test::take_shared_by_id(scenario_mut, object::id_from_address(option::destroy_some(pool_id))); 532 | 533 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 534 | // &mut pool_1, 535 | // 1, 536 | // 2, 537 | // ctx(scenario_mut) 538 | // ); 539 | 540 | // interest_amm::repay_flash_loan( 541 | // &mut pool_2, 542 | // invoice, 543 | // mint_for_testing(1000, scenario_mut.ctx()), 544 | // eth_coin, 545 | // ); 546 | 547 | // destroy(usdc_coin); 548 | // test::return_shared(registry); 549 | // test::return_shared(pool_1); 550 | // test::return_shared(pool_2); 551 | // }; 552 | // test::end(scenario); 553 | // } 554 | 555 | // #[test] 556 | // #[expected_failure(abort_code = amm::interest_amm_errors::EWrongRepayAmount, location = amm::interest_amm)] 557 | // fun test_repay_wrong_repay_amount_x() { 558 | // let (mut scenario, alice, _) = start_test(); 559 | 560 | // let scenario_mut = &mut scenario; 561 | 562 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 563 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 564 | 565 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 566 | // next_tx(scenario_mut, alice); 567 | // { 568 | // let mut request = request(scenario_mut); 569 | 570 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 571 | // &mut request.pool, 572 | // eth_amount, 573 | // usdc_amount, 574 | // ctx(scenario_mut) 575 | // ); 576 | 577 | // burn_for_testing(eth_coin); 578 | // burn_for_testing(usdc_coin); 579 | 580 | // let invoice_repay_amount_x = interest_amm::repay_amount_x(&invoice); 581 | // let invoice_repay_amount_y = interest_amm::repay_amount_y(&invoice); 582 | 583 | // interest_amm::repay_flash_loan( 584 | // &mut request.pool, 585 | // invoice, 586 | // mint_for_testing(invoice_repay_amount_x - 1, ctx(scenario_mut)), 587 | // mint_for_testing(invoice_repay_amount_y, ctx(scenario_mut)) 588 | // ); 589 | 590 | // destroy_request(request); 591 | // }; 592 | 593 | // test::end(scenario); 594 | // } 595 | 596 | // #[test] 597 | // #[expected_failure(abort_code = amm::interest_amm_errors::EWrongRepayAmount, location = amm::interest_amm)] 598 | // fun test_repay_wrong_repay_amount_y() { 599 | // let (mut scenario, alice, _) = start_test(); 600 | 601 | // let scenario_mut = &mut scenario; 602 | 603 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 604 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 605 | 606 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 607 | // next_tx(scenario_mut, alice); 608 | // { 609 | // let mut request = request(scenario_mut); 610 | 611 | // let (invoice, eth_coin, usdc_coin) = interest_amm::flash_loan( 612 | // &mut request.pool, 613 | // eth_amount, 614 | // usdc_amount, 615 | // ctx(scenario_mut) 616 | // ); 617 | 618 | // burn_for_testing(eth_coin); 619 | // burn_for_testing(usdc_coin); 620 | 621 | // let invoice_repay_amount_x = interest_amm::repay_amount_x(&invoice); 622 | // let invoice_repay_amount_y = interest_amm::repay_amount_y(&invoice); 623 | 624 | // interest_amm::repay_flash_loan( 625 | // &mut request.pool, 626 | // invoice, 627 | // mint_for_testing(invoice_repay_amount_x, ctx(scenario_mut)), 628 | // mint_for_testing(invoice_repay_amount_y - 1, ctx(scenario_mut)) 629 | // ); 630 | 631 | // destroy_request(request); 632 | // }; 633 | // test::end(scenario); 634 | // } 635 | 636 | // #[test] 637 | // #[expected_failure(abort_code = amm::interest_amm_errors::EWrongModuleName, location = amm::interest_amm_utils)] 638 | // fun test_new_pool_wrong_lp_coin_metadata() { 639 | // let (mut scenario, alice, _) = start_test(); 640 | 641 | // let scenario_mut = &mut scenario; 642 | 643 | // next_tx(scenario_mut, alice); 644 | // { 645 | // ipx_btce_eth::init_for_testing(ctx(scenario_mut)); 646 | // }; 647 | 648 | // next_tx(scenario_mut, alice); 649 | // { 650 | // let mut registry = test::take_shared(scenario_mut); 651 | // let lp_coin_cap = test::take_from_sender>(scenario_mut); 652 | // let btc_metadata = test::take_shared>(scenario_mut); 653 | // let eth_metadata = test::take_shared>(scenario_mut); 654 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 655 | 656 | // let lp_coin = interest_amm::new( 657 | // &mut registry, 658 | // mint_for_testing(100, ctx(scenario_mut)), 659 | // mint_for_testing(10, ctx(scenario_mut)), 660 | // lp_coin_cap, 661 | // &btc_metadata, 662 | // ð_metadata, 663 | // &mut lp_coin_metadata, 664 | // ctx(scenario_mut) 665 | // ); 666 | 667 | // burn_for_testing(lp_coin); 668 | 669 | // test::return_shared(eth_metadata); 670 | // test::return_shared(btc_metadata); 671 | // test::return_shared(lp_coin_metadata); 672 | // test::return_shared(registry); 673 | // }; 674 | // test::end(scenario); 675 | // } 676 | 677 | // #[test] 678 | // #[expected_failure(abort_code = amm::interest_amm_errors::ESupplyMustHaveZeroValue, location = amm::interest_amm)] 679 | // fun test_new_pool_wrong_lp_coin_supply() { 680 | // let (mut scenario, alice, _) = start_test(); 681 | 682 | // let scenario_mut = &mut scenario; 683 | 684 | // next_tx(scenario_mut, alice); 685 | // { 686 | // ipx_eth_usdc::init_for_testing(ctx(scenario_mut)); 687 | // }; 688 | 689 | // next_tx(scenario_mut, alice); 690 | // { 691 | // let mut registry = test::take_shared(scenario_mut); 692 | // let mut lp_coin_cap = test::take_from_sender>(scenario_mut); 693 | // let eth_metadata = test::take_shared>(scenario_mut); 694 | // let usdc_metadata = test::take_shared>(scenario_mut); 695 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 696 | 697 | // burn_for_testing(coin::mint(&mut lp_coin_cap, 100, ctx(scenario_mut))); 698 | 699 | // let lp_coin = interest_amm::new( 700 | // &mut registry, 701 | // mint_for_testing(100, ctx(scenario_mut)), 702 | // mint_for_testing(10, ctx(scenario_mut)), 703 | // lp_coin_cap, 704 | // ð_metadata, 705 | // &usdc_metadata, 706 | // &mut lp_coin_metadata, 707 | // ctx(scenario_mut) 708 | // ); 709 | 710 | // burn_for_testing(lp_coin); 711 | 712 | // test::return_shared(eth_metadata); 713 | // test::return_shared(usdc_metadata); 714 | // test::return_shared(lp_coin_metadata); 715 | // test::return_shared(registry); 716 | // }; 717 | // test::end(scenario); 718 | // } 719 | 720 | // #[test] 721 | // #[expected_failure(abort_code = amm::interest_amm_errors::EProvideBothCoins, location = amm::interest_amm)] 722 | // fun test_new_pool_zero_coin_x() { 723 | // let (mut scenario, alice, _) = start_test(); 724 | 725 | // let scenario_mut = &mut scenario; 726 | 727 | // next_tx(scenario_mut, alice); 728 | // { 729 | // ipx_eth_usdc::init_for_testing(ctx(scenario_mut)); 730 | // }; 731 | 732 | // next_tx(scenario_mut, alice); 733 | // { 734 | // let mut registry = test::take_shared(scenario_mut); 735 | // let lp_coin_cap = test::take_from_sender>(scenario_mut); 736 | // let eth_metadata = test::take_shared>(scenario_mut); 737 | // let usdc_metadata = test::take_shared>(scenario_mut); 738 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 739 | 740 | // let lp_coin =interest_amm::new( 741 | // &mut registry, 742 | // coin::zero(ctx(scenario_mut)), 743 | // mint_for_testing(10, ctx(scenario_mut)), 744 | // lp_coin_cap, 745 | // ð_metadata, 746 | // &usdc_metadata, 747 | // &mut lp_coin_metadata, 748 | // ctx(scenario_mut) 749 | // ); 750 | 751 | // burn_for_testing(lp_coin); 752 | 753 | // test::return_shared(eth_metadata); 754 | // test::return_shared(usdc_metadata); 755 | // test::return_shared(lp_coin_metadata); 756 | // test::return_shared(registry); 757 | // }; 758 | // test::end(scenario); 759 | // } 760 | 761 | // #[test] 762 | // #[expected_failure(abort_code = amm::interest_amm_errors::EProvideBothCoins, location = amm::interest_amm)] 763 | // fun test_new_pool_zero_coin_y() { 764 | // let (mut scenario, alice, _) = start_test(); 765 | 766 | // let scenario_mut = &mut scenario; 767 | 768 | // next_tx(scenario_mut, alice); 769 | // { 770 | // ipx_eth_usdc::init_for_testing(ctx(scenario_mut)); 771 | // }; 772 | 773 | // next_tx(scenario_mut, alice); 774 | // { 775 | // let mut registry = test::take_shared(scenario_mut); 776 | // let lp_coin_cap = test::take_from_sender>(scenario_mut); 777 | // let eth_metadata = test::take_shared>(scenario_mut); 778 | // let usdc_metadata = test::take_shared>(scenario_mut); 779 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 780 | 781 | // let lp_coin =interest_amm::new( 782 | // &mut registry, 783 | // mint_for_testing(10, ctx(scenario_mut)), 784 | // coin::zero(ctx(scenario_mut)), 785 | // lp_coin_cap, 786 | // ð_metadata, 787 | // &usdc_metadata, 788 | // &mut lp_coin_metadata, 789 | // ctx(scenario_mut) 790 | // ); 791 | 792 | // burn_for_testing(lp_coin); 793 | 794 | // test::return_shared(eth_metadata); 795 | // test::return_shared(usdc_metadata); 796 | // test::return_shared(lp_coin_metadata); 797 | // test::return_shared(registry); 798 | // }; 799 | // test::end(scenario); 800 | // } 801 | 802 | // #[test] 803 | // #[expected_failure(abort_code = amm::interest_amm_errors::EPoolAlreadyDeployed, location = amm::interest_amm)] 804 | // fun test_new_pool_deploy_same_pool() { 805 | // let (mut scenario, alice, _) = start_test(); 806 | 807 | // let scenario_mut = &mut scenario; 808 | 809 | // next_tx(scenario_mut, alice); 810 | // { 811 | // ipx_eth_usdc::init_for_testing(ctx(scenario_mut)); 812 | // }; 813 | 814 | // next_tx(scenario_mut, alice); 815 | // { 816 | // let mut registry = test::take_shared(scenario_mut); 817 | // let lp_coin_cap = test::take_from_sender>(scenario_mut); 818 | // let eth_metadata = test::take_shared>(scenario_mut); 819 | // let usdc_metadata = test::take_shared>(scenario_mut); 820 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 821 | 822 | // let lp_coin =interest_amm::new( 823 | // &mut registry, 824 | // mint_for_testing(10, ctx(scenario_mut)), 825 | // mint_for_testing(10, ctx(scenario_mut)), 826 | // lp_coin_cap, 827 | // ð_metadata, 828 | // &usdc_metadata, 829 | // &mut lp_coin_metadata, 830 | // ctx(scenario_mut) 831 | // ); 832 | 833 | // burn_for_testing(lp_coin); 834 | 835 | // test::return_shared(eth_metadata); 836 | // test::return_shared(usdc_metadata); 837 | // test::return_shared(lp_coin_metadata); 838 | // test::return_shared(registry); 839 | // }; 840 | 841 | // next_tx(scenario_mut, alice); 842 | // { 843 | // ipx_eth_usdc::init_for_testing(ctx(scenario_mut)); 844 | // }; 845 | 846 | // next_tx(scenario_mut, alice); 847 | // { 848 | // let mut registry = test::take_shared(scenario_mut); 849 | // let lp_coin_cap = test::take_from_sender>(scenario_mut); 850 | // let eth_metadata = test::take_shared>(scenario_mut); 851 | // let usdc_metadata = test::take_shared>(scenario_mut); 852 | // let mut lp_coin_metadata = test::take_shared>(scenario_mut); 853 | 854 | // let lp_coin =interest_amm::new( 855 | // &mut registry, 856 | // mint_for_testing(10, ctx(scenario_mut)), 857 | // mint_for_testing(10, ctx(scenario_mut)), 858 | // lp_coin_cap, 859 | // ð_metadata, 860 | // &usdc_metadata, 861 | // &mut lp_coin_metadata, 862 | // ctx(scenario_mut) 863 | // ); 864 | 865 | // burn_for_testing(lp_coin); 866 | 867 | // test::return_shared(eth_metadata); 868 | // test::return_shared(usdc_metadata); 869 | // test::return_shared(lp_coin_metadata); 870 | // test::return_shared(registry); 871 | // }; 872 | 873 | // test::end(scenario); 874 | // } 875 | 876 | // #[test] 877 | // #[expected_failure(abort_code = amm::interest_amm_errors::ENoZeroCoin, location = amm::interest_amm)] 878 | // fun test_swap_zero_coin() { 879 | // let (mut scenario, alice, _) = start_test(); 880 | 881 | // let scenario_mut = &mut scenario; 882 | 883 | // next_tx(scenario_mut, alice); 884 | // { 885 | // admin::init_for_testing(ctx(scenario_mut)); 886 | // interest_amm::init_for_testing(ctx(scenario_mut)); 887 | // }; 888 | 889 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 890 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 891 | 892 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 893 | 894 | // next_tx(scenario_mut, alice); 895 | // { 896 | // let mut request = request(scenario_mut); 897 | 898 | // let eth_coin = interest_amm::swap( 899 | // &mut request.pool, 900 | // coin::zero(ctx(scenario_mut)), 901 | // 0, 902 | // ctx(scenario_mut) 903 | // ); 904 | 905 | // burn_for_testing(eth_coin); 906 | 907 | // destroy_request(request); 908 | // }; 909 | // test::end(scenario); 910 | // } 911 | 912 | // #[test] 913 | // #[expected_failure(abort_code = amm::interest_amm_errors::EPoolIsLocked, location = amm::interest_amm)] 914 | // fun test_swap_x_locked_pool() { 915 | // let (mut scenario, alice, _) = start_test(); 916 | 917 | // let scenario_mut = &mut scenario; 918 | 919 | // next_tx(scenario_mut, alice); 920 | // { 921 | // admin::init_for_testing(ctx(scenario_mut)); 922 | // interest_amm::init_for_testing(ctx(scenario_mut)); 923 | // }; 924 | 925 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 926 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 927 | 928 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 929 | 930 | // next_tx(scenario_mut, alice); 931 | // { 932 | // let mut request = request(scenario_mut); 933 | 934 | // let (invoice, coin_x, coin_y) = interest_amm::flash_loan( 935 | // &mut request.pool, 936 | // 1, 937 | // 2, 938 | // ctx(scenario_mut) 939 | // ); 940 | 941 | // let eth_coin = interest_amm::swap( 942 | // &mut request.pool, 943 | // mint_for_testing(1, ctx(scenario_mut)), 944 | // 0, 945 | // ctx(scenario_mut) 946 | // ); 947 | 948 | // interest_amm::repay_flash_loan( 949 | // &mut request.pool, 950 | // invoice, 951 | // coin_x, 952 | // coin_y 953 | // ); 954 | 955 | // burn_for_testing(eth_coin); 956 | 957 | // destroy_request(request); 958 | // }; 959 | // test::end(scenario); 960 | // } 961 | 962 | // #[test] 963 | // #[expected_failure(abort_code = amm::interest_amm_errors::EPoolIsLocked, location = amm::interest_amm)] 964 | // fun test_swap_y_locked_pool() { 965 | // let (mut scenario, alice, _) = start_test(); 966 | 967 | // let scenario_mut = &mut scenario; 968 | 969 | // next_tx(scenario_mut, alice); 970 | // { 971 | // admin::init_for_testing(ctx(scenario_mut)); 972 | // interest_amm::init_for_testing(ctx(scenario_mut)); 973 | // }; 974 | 975 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 976 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 977 | 978 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 979 | 980 | // next_tx(scenario_mut, alice); 981 | // { 982 | // let mut request = request(scenario_mut); 983 | 984 | // let (invoice, coin_x, coin_y) = interest_amm::flash_loan( 985 | // &mut request.pool, 986 | // 1, 987 | // 2, 988 | // ctx(scenario_mut) 989 | // ); 990 | 991 | // let usdc_coin = interest_amm::swap( 992 | // &mut request.pool, 993 | // mint_for_testing(1, ctx(scenario_mut)), 994 | // 0, 995 | // ctx(scenario_mut) 996 | // ); 997 | 998 | // interest_amm::repay_flash_loan( 999 | // &mut request.pool, 1000 | // invoice, 1001 | // coin_x, 1002 | // coin_y 1003 | // ); 1004 | 1005 | // burn_for_testing(usdc_coin); 1006 | 1007 | // destroy_request(request); 1008 | // }; 1009 | // test::end(scenario); 1010 | // } 1011 | 1012 | // #[test] 1013 | // #[expected_failure(abort_code = amm::interest_amm_errors::ESlippage, location = amm::interest_amm)] 1014 | // fun test_swap_x_slippage() { 1015 | // let (mut scenario, alice, _) = start_test(); 1016 | 1017 | // let scenario_mut = &mut scenario; 1018 | 1019 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1020 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1021 | 1022 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1023 | 1024 | // next_tx(scenario_mut, alice); 1025 | // { 1026 | // let mut request = request(scenario_mut); 1027 | 1028 | // let amount_in = 3 * ETH_DECIMAL_SCALAR; 1029 | // let amount_in_fee = fees::get_fee_in_amount(&request.fees, amount_in); 1030 | // let expected_amount_out = interest_amm_invariant::get_amount_out(amount_in - amount_in_fee, eth_amount, usdc_amount); 1031 | // let amount_out_fee = fees::get_fee_out_amount(&request.fees, expected_amount_out); 1032 | // let expected_amount_out = expected_amount_out - amount_out_fee; 1033 | 1034 | // let usdc_coin = interest_amm::swap( 1035 | // &mut request.pool, 1036 | // mint_for_testing(amount_in, ctx(scenario_mut)), 1037 | // expected_amount_out + 1, 1038 | // ctx(scenario_mut) 1039 | // ); 1040 | 1041 | // burn_for_testing(usdc_coin); 1042 | 1043 | // destroy_request(request); 1044 | // }; 1045 | 1046 | // test::end(scenario); 1047 | // } 1048 | 1049 | // #[test] 1050 | // #[expected_failure(abort_code = amm::interest_amm_errors::ESlippage, location = amm::interest_amm)] 1051 | // fun test_swap_y_slippage() { 1052 | // let (mut scenario, alice, _) = start_test(); 1053 | 1054 | // let scenario_mut = &mut scenario; 1055 | 1056 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1057 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1058 | 1059 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1060 | 1061 | // next_tx(scenario_mut, alice); 1062 | // { 1063 | // let mut request = request(scenario_mut); 1064 | 1065 | // let eth_amount = interest_amm::balance_x(&request.pool); 1066 | // let usdc_amount = interest_amm::balance_y(&request.pool); 1067 | 1068 | // let amount_in = 7777 * USDC_DECIMAL_SCALAR; 1069 | // let amount_in_fee = fees::get_fee_in_amount(&request.fees, amount_in); 1070 | // let expected_amount_out = interest_amm_invariant::get_amount_out(amount_in - amount_in_fee, usdc_amount, eth_amount); 1071 | // let amount_out_fee = fees::get_fee_out_amount(&request.fees, expected_amount_out); 1072 | // let expected_amount_out = expected_amount_out - amount_out_fee; 1073 | 1074 | // let eth_coin = interest_amm::swap( 1075 | // &mut request.pool, 1076 | // mint_for_testing(amount_in, ctx(scenario_mut)), 1077 | // expected_amount_out + 1, 1078 | // ctx(scenario_mut) 1079 | // ); 1080 | 1081 | // burn_for_testing(eth_coin); 1082 | 1083 | // destroy_request(request); 1084 | // }; 1085 | // test::end(scenario); 1086 | // } 1087 | 1088 | // #[test] 1089 | // #[expected_failure(abort_code = amm::interest_amm_errors::ESlippage, location = amm::interest_amm)] 1090 | // fun test_add_liquidity_slippage() { 1091 | // let (mut scenario, alice, _) = start_test(); 1092 | 1093 | // let scenario_mut = &mut scenario; 1094 | 1095 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1096 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1097 | 1098 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1099 | 1100 | // next_tx(scenario_mut, alice); 1101 | // { 1102 | // let mut request = request(scenario_mut); 1103 | 1104 | // let amount_x = 20 * ETH_DECIMAL_SCALAR; 1105 | // let amount_y = 27000 * USDC_DECIMAL_SCALAR; 1106 | 1107 | // let (shares, _, _) = quote::add_liquidity(&request.pool, amount_x, amount_y); 1108 | 1109 | // let (lp_coin, eth_coin, usdc_coin) = interest_amm::add_liquidity( 1110 | // &mut request.pool, 1111 | // mint_for_testing(amount_x, ctx(scenario_mut)), 1112 | // mint_for_testing(amount_y, ctx(scenario_mut)), 1113 | // shares + 1, 1114 | // ctx(scenario_mut) 1115 | // ); 1116 | 1117 | // burn_for_testing(lp_coin); 1118 | // burn_for_testing(eth_coin); 1119 | // burn_for_testing(usdc_coin); 1120 | 1121 | // destroy_request(request); 1122 | // }; 1123 | // test::end(scenario); 1124 | // } 1125 | 1126 | // #[test] 1127 | // #[expected_failure(abort_code = amm::interest_amm_errors::EPoolIsLocked, location = amm::interest_amm)] 1128 | // fun test_add_liquidity_locked() { 1129 | // let (mut scenario, alice, _) = start_test(); 1130 | 1131 | // let scenario_mut = &mut scenario; 1132 | 1133 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1134 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1135 | 1136 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1137 | 1138 | // next_tx(scenario_mut, alice); 1139 | // { 1140 | // let mut request = request(scenario_mut); 1141 | 1142 | // let amount_x = 20 * ETH_DECIMAL_SCALAR; 1143 | // let amount_y = 27000 * USDC_DECIMAL_SCALAR; 1144 | 1145 | // let (invoice, coin_x, coin_y) = interest_amm::flash_loan( 1146 | // &mut request.pool, 1147 | // 1, 1148 | // 2, 1149 | // ctx(scenario_mut) 1150 | // ); 1151 | 1152 | // let (lp_coin, eth_coin, usdc_coin) = interest_amm::add_liquidity( 1153 | // &mut request.pool, 1154 | // mint_for_testing(amount_x, ctx(scenario_mut)), 1155 | // mint_for_testing(amount_y, ctx(scenario_mut)), 1156 | // 0, 1157 | // ctx(scenario_mut) 1158 | // ); 1159 | 1160 | // interest_amm::repay_flash_loan( 1161 | // &mut request.pool, 1162 | // invoice, 1163 | // coin_x, 1164 | // coin_y 1165 | // ); 1166 | 1167 | // burn_for_testing(lp_coin); 1168 | // burn_for_testing(eth_coin); 1169 | // burn_for_testing(usdc_coin); 1170 | 1171 | // destroy_request(request); 1172 | // }; 1173 | // test::end(scenario); 1174 | // } 1175 | 1176 | // #[test] 1177 | // #[expected_failure(abort_code = amm::interest_amm_errors::EProvideBothCoins, location = amm::interest_amm)] 1178 | // fun test_add_liquidity_zero_coin_x() { 1179 | // let (mut scenario, alice, _) = start_test(); 1180 | 1181 | // let scenario_mut = &mut scenario; 1182 | 1183 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1184 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1185 | 1186 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1187 | 1188 | // next_tx(scenario_mut, alice); 1189 | // { 1190 | // let mut request = request(scenario_mut); 1191 | 1192 | // let (lp_coin, eth_coin, usdc_coin) = interest_amm::add_liquidity( 1193 | // &mut request.pool, 1194 | // coin::zero(ctx(scenario_mut)), 1195 | // mint_for_testing(1, ctx(scenario_mut)), 1196 | // 0, 1197 | // ctx(scenario_mut) 1198 | // ); 1199 | 1200 | // burn_for_testing(lp_coin); 1201 | // burn_for_testing(eth_coin); 1202 | // burn_for_testing(usdc_coin); 1203 | 1204 | // destroy_request(request); 1205 | // }; 1206 | // test::end(scenario); 1207 | // } 1208 | 1209 | // #[test] 1210 | // #[expected_failure(abort_code = amm::interest_amm_errors::EProvideBothCoins, location = amm::interest_amm)] 1211 | // fun test_add_liquidity_zero_coin_y() { 1212 | // let (mut scenario, alice, _) = start_test(); 1213 | 1214 | // let scenario_mut = &mut scenario; 1215 | 1216 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1217 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1218 | 1219 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1220 | 1221 | // next_tx(scenario_mut, alice); 1222 | // { 1223 | // let mut request = request(scenario_mut); 1224 | 1225 | // let (lp_coin, eth_coin, usdc_coin) = interest_amm::add_liquidity( 1226 | // &mut request.pool, 1227 | // mint_for_testing(1, ctx(scenario_mut)), 1228 | // coin::zero(ctx(scenario_mut)), 1229 | // 0, 1230 | // ctx(scenario_mut) 1231 | // ); 1232 | 1233 | // burn_for_testing(lp_coin); 1234 | // burn_for_testing(eth_coin); 1235 | // burn_for_testing(usdc_coin); 1236 | 1237 | // destroy_request(request); 1238 | // }; 1239 | // test::end(scenario); 1240 | // } 1241 | 1242 | // #[test] 1243 | // #[expected_failure(abort_code = amm::interest_amm_errors::EProvideBothCoins, location = amm::interest_amm)] 1244 | // fun test_add_liquidity_both_zero_coins() { 1245 | // let (mut scenario, alice, _) = start_test(); 1246 | 1247 | // let scenario_mut = &mut scenario; 1248 | 1249 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1250 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1251 | 1252 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1253 | 1254 | // next_tx(scenario_mut, alice); 1255 | // { 1256 | // let mut request = request(scenario_mut); 1257 | 1258 | // let (lp_coin, eth_coin, usdc_coin) = interest_amm::add_liquidity( 1259 | // &mut request.pool, 1260 | // coin::zero(ctx(scenario_mut)), 1261 | // coin::zero(ctx(scenario_mut)), 1262 | // 0, 1263 | // ctx(scenario_mut) 1264 | // ); 1265 | 1266 | // burn_for_testing(lp_coin); 1267 | // burn_for_testing(eth_coin); 1268 | // burn_for_testing(usdc_coin); 1269 | 1270 | // destroy_request(request); 1271 | // }; 1272 | // test::end(scenario); 1273 | // } 1274 | 1275 | // #[test] 1276 | // #[expected_failure(abort_code = amm::interest_amm_errors::ENoZeroCoin, location = amm::interest_amm)] 1277 | // fun test_remove_liquidity_no_zero_coin() { 1278 | // let (mut scenario, alice, _) = start_test(); 1279 | 1280 | // let scenario_mut = &mut scenario; 1281 | 1282 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1283 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1284 | 1285 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1286 | 1287 | // next_tx(scenario_mut, alice); 1288 | // { 1289 | // let mut request = request(scenario_mut); 1290 | 1291 | // let (eth_coin, usdc_coin) = interest_amm::remove_liquidity( 1292 | // &mut request.pool, 1293 | // coin::zero(ctx(scenario_mut)), 1294 | // 0, 1295 | // 0, 1296 | // ctx(scenario_mut) 1297 | // ); 1298 | 1299 | // burn_for_testing(eth_coin); 1300 | // burn_for_testing(usdc_coin); 1301 | 1302 | // destroy_request(request); 1303 | // }; 1304 | // test::end(scenario); 1305 | // } 1306 | 1307 | // #[test] 1308 | // #[expected_failure(abort_code = amm::interest_amm_errors::EPoolIsLocked, location = amm::interest_amm)] 1309 | // fun test_remove_liquidity_locked() { 1310 | // let (mut scenario, alice, _) = start_test(); 1311 | 1312 | // let scenario_mut = &mut scenario; 1313 | 1314 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1315 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1316 | 1317 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1318 | 1319 | // next_tx(scenario_mut, alice); 1320 | // { 1321 | // let mut request = request(scenario_mut); 1322 | 1323 | // let (invoice, coin_x, coin_y) = interest_amm::flash_loan( 1324 | // &mut request.pool, 1325 | // 1, 1326 | // 2, 1327 | // ctx(scenario_mut) 1328 | // ); 1329 | 1330 | // let (eth_coin, usdc_coin) = interest_amm::remove_liquidity( 1331 | // &mut request.pool, 1332 | // mint_for_testing(1, ctx(scenario_mut)), 1333 | // 0, 1334 | // 0, 1335 | // ctx(scenario_mut) 1336 | // ); 1337 | 1338 | // burn_for_testing(eth_coin); 1339 | // burn_for_testing(usdc_coin); 1340 | 1341 | // interest_amm::repay_flash_loan( 1342 | // &mut request.pool, 1343 | // invoice, 1344 | // coin_x, 1345 | // coin_y 1346 | // ); 1347 | 1348 | // destroy_request(request); 1349 | // }; 1350 | // test::end(scenario); 1351 | // } 1352 | 1353 | // #[test] 1354 | // #[expected_failure(abort_code = amm::interest_amm_errors::ESlippage, location = amm::interest_amm)] 1355 | // fun test_remove_liquidity_slippage_coin_x() { 1356 | // let (mut scenario, alice, _) = start_test(); 1357 | 1358 | // let scenario_mut = &mut scenario; 1359 | 1360 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1361 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1362 | 1363 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1364 | 1365 | // next_tx(scenario_mut, alice); 1366 | // { 1367 | // let mut request = request(scenario_mut); 1368 | // let initial_lp_coin_supply = interest_amm::lp_coin_supply(&request.pool); 1369 | 1370 | // let (expected_x, expected_y) = quote::remove_liquidity(&request.pool, initial_lp_coin_supply / 3); 1371 | 1372 | // let (eth_coin, usdc_coin) = interest_amm::remove_liquidity( 1373 | // &mut request.pool, 1374 | // mint_for_testing(initial_lp_coin_supply / 3, ctx(scenario_mut)), 1375 | // expected_x + 1, 1376 | // expected_y, 1377 | // ctx(scenario_mut) 1378 | // ); 1379 | 1380 | // burn_for_testing(eth_coin); 1381 | // burn_for_testing(usdc_coin); 1382 | 1383 | // destroy_request(request); 1384 | // }; 1385 | // test::end(scenario); 1386 | // } 1387 | 1388 | // #[test] 1389 | // #[expected_failure(abort_code = amm::interest_amm_errors::ESlippage, location = amm::interest_amm)] 1390 | // fun test_remove_liquidity_slippage_coin_y() { 1391 | // let (mut scenario, alice, _) = start_test(); 1392 | 1393 | // let scenario_mut = &mut scenario; 1394 | 1395 | // let eth_amount = 15 * ETH_DECIMAL_SCALAR; 1396 | // let usdc_amount = 37500 * USDC_DECIMAL_SCALAR; 1397 | 1398 | // deploy_eth_usdc_pool(scenario_mut, eth_amount, usdc_amount); 1399 | 1400 | // next_tx(scenario_mut, alice); 1401 | // { 1402 | // let mut request = request(scenario_mut); 1403 | // let initial_lp_coin_supply = interest_amm::lp_coin_supply(&request.pool); 1404 | 1405 | // let (expected_x, expected_y) = quote::remove_liquidity(&request.pool, initial_lp_coin_supply / 3); 1406 | 1407 | // let (eth_coin, usdc_coin) = interest_amm::remove_liquidity( 1408 | // &mut request.pool, 1409 | // mint_for_testing(initial_lp_coin_supply / 3, ctx(scenario_mut)), 1410 | // expected_x, 1411 | // expected_y + 1, 1412 | // ctx(scenario_mut) 1413 | // ); 1414 | 1415 | // burn_for_testing(eth_coin); 1416 | // burn_for_testing(usdc_coin); 1417 | 1418 | // destroy_request(request); 1419 | // }; 1420 | // test::end(scenario); 1421 | // } 1422 | 1423 | // public struct Request { 1424 | // registry: Registry, 1425 | // pool: InterestPool, 1426 | // fees: Fees 1427 | // } 1428 | 1429 | // fun request(scenario_mut: &Scenario): Request { 1430 | // let registry = test::take_shared(scenario_mut); 1431 | // let pool_address = interest_amm::pool_address(®istry); 1432 | // let pool = test::take_shared_by_id(scenario_mut, object::id_from_address(option::destroy_some(pool_address))); 1433 | // let fees = interest_amm::fees(&pool); 1434 | 1435 | // Request { 1436 | // registry, 1437 | // pool, 1438 | // fees 1439 | // } 1440 | // } 1441 | 1442 | // fun destroy_request(request: Request) { 1443 | // let Request { registry, pool, fees: _ } = request; 1444 | 1445 | // test::return_shared(registry); 1446 | // test::return_shared(pool); 1447 | // } 1448 | 1449 | // fun start_test(): (Scenario, address, address) { 1450 | // let mut scenario = scenario(); 1451 | // let (alice, bob) = people(); 1452 | 1453 | // let scenario_mut = &mut scenario; 1454 | 1455 | // deploy_coins(scenario_mut); 1456 | 1457 | // next_tx(scenario_mut, alice); 1458 | // { 1459 | // admin::init_for_testing(ctx(scenario_mut)); 1460 | // interest_amm::init_for_testing(ctx(scenario_mut)); 1461 | // }; 1462 | 1463 | // (scenario, alice, bob) 1464 | // } 1465 | // } --------------------------------------------------------------------------------