From 2dc7b89798188bb3b71defdef8fd78638a004d52 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Wed, 3 Apr 2024 13:12:18 +0100 Subject: [PATCH] added authz test for setting the route --- Cargo.toml | 23 +- contracts/swap/src/testing/authz_tests.rs | 116 + .../integration_logic_tests.rs | 2233 ----------------- ...egration_realistic_tests_exact_quantity.rs | 1685 ------------- ...ntegration_realistic_tests_min_quantity.rs | 1416 ----------- .../swap/src/testing/integration_tests/mod.rs | 3 - contracts/swap/src/testing/mod.rs | 1 + contracts/swap/src/testing/test_utils.rs | 77 +- 8 files changed, 184 insertions(+), 5370 deletions(-) create mode 100644 contracts/swap/src/testing/authz_tests.rs delete mode 100644 contracts/swap/src/testing/integration_tests/integration_logic_tests.rs delete mode 100644 contracts/swap/src/testing/integration_tests/integration_realistic_tests_exact_quantity.rs delete mode 100644 contracts/swap/src/testing/integration_tests/integration_realistic_tests_min_quantity.rs delete mode 100644 contracts/swap/src/testing/integration_tests/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 38ef009..d5e73df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,22 @@ [workspace] -members = ["contracts/*"] +members = [ "contracts/*" ] +resolver = "2" [profile.release.package.injective-cosmwasm] codegen-units = 1 -incremental = false +incremental = false [profile.release] -opt-level = 3 -debug = false -rpath = false -lto = true +codegen-units = 1 +debug = false debug-assertions = false -codegen-units = 1 -panic = 'abort' -incremental = false -overflow-checks = true +incremental = false +lto = true +opt-level = 3 +overflow-checks = true +panic = 'abort' +rpath = false [patch.crates-io] #cw-multi-test = { path = "../cw-multi-test" } -#cw-multi-test = { git = "https://github.com/InjectiveLabs/cw-multi-test.git", branch ="feature/custom_address_generator" } \ No newline at end of file +#cw-multi-test = { git = "https://github.com/InjectiveLabs/cw-multi-test.git", branch ="feature/custom_address_generator" } diff --git a/contracts/swap/src/testing/authz_tests.rs b/contracts/swap/src/testing/authz_tests.rs new file mode 100644 index 0000000..8fa5cba --- /dev/null +++ b/contracts/swap/src/testing/authz_tests.rs @@ -0,0 +1,116 @@ +use crate::{ + msg::{ExecuteMsg, QueryMsg}, + testing::test_utils::{ + create_generic_authorization, create_realistic_atom_usdt_sell_orders_from_spreadsheet, + create_realistic_eth_usdt_buy_orders_from_spreadsheet, human_to_dec, init_rich_account, + init_self_relaying_contract_and_get_address, launch_realistic_atom_usdt_spot_market, + launch_realistic_weth_usdt_spot_market, must_init_account_with_funds, str_coin, Decimals, + ATOM, ETH, INJ, USDT, + }, + types::SwapEstimationResult, +}; + +use cosmos_sdk_proto::{cosmwasm::wasm::v1::MsgExecuteContract, traits::MessageExt}; +use injective_std::{ + shim::Any, + types::cosmos::authz::v1beta1::{MsgExec, MsgExecResponse}, +}; +use injective_test_tube::{ + Account, Exchange, ExecuteResponse, InjectiveTestApp, Module, Runner, Wasm, +}; + +#[test] +pub fn set_route_for_third_party_test() { + let app = InjectiveTestApp::new(); + let wasm = Wasm::new(&app); + let exchange = Exchange::new(&app); + + let owner = must_init_account_with_funds( + &app, + &[ + str_coin("1", ETH, Decimals::Eighteen), + str_coin("1", ATOM, Decimals::Six), + str_coin("1_000", USDT, Decimals::Six), + str_coin("10_000", INJ, Decimals::Eighteen), + ], + ); + + let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); + let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); + + let contr_addr = init_self_relaying_contract_and_get_address( + &wasm, + &owner, + &[str_coin("1_000", USDT, Decimals::Six)], + ); + + let trader1 = init_rich_account(&app); + let trader2 = init_rich_account(&app); + let trader3 = init_rich_account(&app); + + create_generic_authorization( + &app, + &owner, + trader1.address().to_string(), + "/cosmwasm.wasm.v1.MsgExecuteContract".to_string(), + None, + ); + + create_realistic_eth_usdt_buy_orders_from_spreadsheet( + &app, + &spot_market_1_id, + &trader1, + &trader2, + ); + create_realistic_atom_usdt_sell_orders_from_spreadsheet( + &app, + &spot_market_2_id, + &trader1, + &trader2, + &trader3, + ); + + app.increase_time(1); + + let eth_to_swap = "4.08"; + + let set_route_msg = ExecuteMsg::SetRoute { + source_denom: ETH.to_string(), + target_denom: ATOM.to_string(), + route: vec![ + spot_market_1_id.as_str().into(), + spot_market_2_id.as_str().into(), + ], + }; + + let execute_msg = MsgExecuteContract { + contract: contr_addr.clone(), + sender: owner.address().to_string(), + msg: serde_json_wasm::to_vec(&set_route_msg).unwrap(), + funds: vec![], + }; + + // execute on more time to excercise account sequence + let msg = MsgExec { + grantee: trader1.address().to_string(), + msgs: vec![Any { + type_url: "/cosmwasm.wasm.v1.MsgExecuteContract".to_string(), + value: execute_msg.to_bytes().unwrap(), + }], + }; + + let _res: ExecuteResponse = app + .execute(msg, "/cosmos.authz.v1beta1.MsgExec", &trader1) + .unwrap(); + + let _query_result: SwapEstimationResult = wasm + .query( + &contr_addr, + &QueryMsg::GetOutputQuantity { + source_denom: ETH.to_string(), + target_denom: ATOM.to_string(), + from_quantity: human_to_dec(eth_to_swap, Decimals::Eighteen), + }, + ) + .unwrap(); +} diff --git a/contracts/swap/src/testing/integration_tests/integration_logic_tests.rs b/contracts/swap/src/testing/integration_tests/integration_logic_tests.rs deleted file mode 100644 index d009000..0000000 --- a/contracts/swap/src/testing/integration_tests/integration_logic_tests.rs +++ /dev/null @@ -1,2233 +0,0 @@ -use cosmwasm_std::{coin, Addr}; - -use injective_test_tube::RunnerError::{ExecuteError, QueryError}; -use injective_test_tube::{ - Account, Bank, Exchange, InjectiveTestApp, Module, RunnerError, RunnerResult, SigningAccount, - Wasm, -}; - -use injective_math::{round_to_min_tick, FPDecimal}; - -use crate::msg::{ExecuteMsg, QueryMsg}; -use crate::testing::test_utils::{ - are_fpdecimals_approximately_equal, assert_fee_is_as_expected, create_limit_order, - fund_account_with_some_inj, human_to_dec, init_contract_with_fee_recipient_and_get_address, - init_default_signer_account, init_default_validator_account, init_rich_account, - init_self_relaying_contract_and_get_address, launch_spot_market, must_init_account_with_funds, - pause_spot_market, query_all_bank_balances, query_bank_balance, set_route_and_assert_success, - str_coin, Decimals, OrderSide, ATOM, DEFAULT_ATOMIC_MULTIPLIER, DEFAULT_RELAYER_SHARE, - DEFAULT_SELF_RELAYING_FEE_PART, DEFAULT_TAKER_FEE, ETH, INJ, USDC, USDT, -}; -use crate::types::{FPCoin, SwapEstimationResult}; - -/* - This suite of tests focuses on calculation logic itself and doesn't attempt to use neither - realistic market configuration nor order prices, so that we don't have to deal with scaling issues. - - Hardcoded values used in these tests come from the first tab of this spreadsheet: - https://docs.google.com/spreadsheets/d/1-0epjX580nDO_P2mm1tSjhvjJVppsvrO1BC4_wsBeyA/edit?usp=sharing -*/ - -#[test] -fn it_executes_a_swap_between_two_base_assets_with_multiple_price_levels() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ) - .unwrap(); - - assert_eq!( - query_result.result_quantity, - FPDecimal::must_from_str("2893.886"), //slightly rounded down - "incorrect swap result estimate returned by query" - ); - - assert_eq!( - query_result.expected_fees.len(), - 2, - "Wrong number of fee denoms received" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: FPDecimal::must_from_str("3541.5"), - denom: "usdt".to_string(), - }, - FPCoin { - amount: FPDecimal::must_from_str("3530.891412"), - denom: "usdt".to_string(), - }, - ]; - - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - FPDecimal::must_from_str("0.000001"), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, - FPDecimal::must_from_str("2893"), - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_balance_usdt_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - let contract_balance_usdt_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - - assert!( - contract_balance_usdt_after >= contract_balance_usdt_before, - "Contract lost some money after swap. Balance before: {contract_balance_usdt_before}, after: {contract_balance_usdt_after}", - ); - - let max_diff = human_to_dec("0.00001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_balance_usdt_after, - contract_balance_usdt_before, - max_diff, - ), - "Contract balance changed too much. Before: {}, After: {}", - contract_balances_before[0].amount, - contract_balances_after[0].amount - ); -} - -#[test] -fn it_executes_a_swap_between_two_base_assets_with_single_price_level() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(3, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let expected_atom_estimate_quantity = FPDecimal::must_from_str("751.492"); - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(3u128), - }, - ) - .unwrap(); - - assert_eq!( - query_result.result_quantity, expected_atom_estimate_quantity, - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: FPDecimal::must_from_str("904.5"), - denom: "usdt".to_string(), - }, - FPCoin { - amount: FPDecimal::must_from_str("901.790564"), - denom: "usdt".to_string(), - }, - ]; - - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.00001", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(750u128), - }, - &[coin(3, ETH)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, - expected_atom_estimate_quantity.int(), - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_executes_swap_between_markets_using_different_quote_assets() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDC); - let spot_market_3_id = launch_spot_market(&exchange, &owner, USDC, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[ - str_coin("100_000", USDC, Decimals::Six), - str_coin("100_000", USDT, Decimals::Six), - ], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_3_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - //USDT-USDC - create_limit_order( - &app, - &trader3, - &spot_market_3_id, - OrderSide::Sell, - 1, - 100_000_000, - ); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ) - .unwrap(); - - // expected amount is a bit lower, even though 1 USDT = 1 USDC, because of the fees - assert_eq!( - query_result.result_quantity, - FPDecimal::must_from_str("2889.64"), - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: FPDecimal::must_from_str("3541.5"), - denom: "usdt".to_string(), - }, - FPCoin { - amount: FPDecimal::must_from_str("3530.891412"), - denom: "usdt".to_string(), - }, - FPCoin { - amount: FPDecimal::must_from_str("3525.603007"), - denom: "usdc".to_string(), - }, - ]; - - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.000001", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, - FPDecimal::must_from_str("2889"), - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 2, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_reverts_swap_between_markets_using_different_quote_asset_if_one_quote_buffer_is_insufficient() -{ - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDC); - let spot_market_3_id = launch_spot_market(&exchange, &owner, USDC, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[ - str_coin("0.0001", USDC, Decimals::Six), - str_coin("100_000", USDT, Decimals::Six), - ], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_3_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - //USDT-USDC - create_limit_order( - &app, - &trader3, - &spot_market_3_id, - OrderSide::Sell, - 1, - 100_000_000, - ); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let query_result: RunnerResult = wasm.query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ); - - assert!(query_result.is_err(), "swap should have failed"); - assert!( - query_result - .unwrap_err() - .to_string() - .contains("Swap amount too high"), - "incorrect query result error message" - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert!(execute_result.is_err(), "swap should have failed"); - assert!( - execute_result - .unwrap_err() - .to_string() - .contains("Swap amount too high"), - "incorrect query result error message" - ); - - let source_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let target_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - source_balance, - FPDecimal::must_from_str("12"), - "source balance should not have changed after failed swap" - ); - assert_eq!( - target_balance, - FPDecimal::ZERO, - "target balance should not have changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 2, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_executes_a_sell_of_base_asset_to_receive_min_output_quantity() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - USDT, - vec![spot_market_1_id.as_str().into()], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: USDT.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ) - .unwrap(); - - // calculate how much can be USDT can be bought for 12 ETH without fees - let orders_nominal_total_value = FPDecimal::from(201_000u128) * FPDecimal::from(5u128) - + FPDecimal::from(195_000u128) * FPDecimal::from(4u128) - + FPDecimal::from(192_000u128) * FPDecimal::from(3u128); - let expected_target_quantity = orders_nominal_total_value - * (FPDecimal::ONE - - FPDecimal::must_from_str(&format!( - "{}", - DEFAULT_TAKER_FEE * DEFAULT_ATOMIC_MULTIPLIER * DEFAULT_SELF_RELAYING_FEE_PART - ))); - - assert_eq!( - query_result.result_quantity, expected_target_quantity, - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![FPCoin { - amount: FPDecimal::must_from_str("3541.5"), - denom: "usdt".to_string(), - }]; - - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - FPDecimal::must_from_str("0.000001"), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: USDT.to_string(), - min_output_quantity: FPDecimal::from(2357458u128), - }, - &[coin(12, ETH)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, USDT, swapper.address().as_str()); - let expected_execute_result = expected_target_quantity.int(); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, expected_execute_result, - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_executes_a_buy_of_base_asset_to_receive_min_output_quantity() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - USDT, - vec![spot_market_1_id.as_str().into()], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - - create_limit_order( - &app, - &trader1, - &spot_market_1_id, - OrderSide::Sell, - 201_000, - 5, - ); - create_limit_order( - &app, - &trader2, - &spot_market_1_id, - OrderSide::Sell, - 195_000, - 4, - ); - create_limit_order( - &app, - &trader2, - &spot_market_1_id, - OrderSide::Sell, - 192_000, - 3, - ); - - app.increase_time(1); - - let swapper_usdt = 2_360_995; - let swapper = must_init_account_with_funds( - &app, - &[ - coin(swapper_usdt, USDT), - str_coin("500_000", INJ, Decimals::Eighteen), - ], - ); - - // calculate how much ETH we can buy with USDT we have - let available_usdt_after_fee = FPDecimal::from(swapper_usdt) - / (FPDecimal::ONE - + FPDecimal::must_from_str(&format!( - "{}", - DEFAULT_TAKER_FEE * DEFAULT_ATOMIC_MULTIPLIER * DEFAULT_SELF_RELAYING_FEE_PART - ))); - let usdt_left_for_most_expensive_order = available_usdt_after_fee - - (FPDecimal::from(195_000u128) * FPDecimal::from(4u128) - + FPDecimal::from(192_000u128) * FPDecimal::from(3u128)); - let most_expensive_order_quantity = - usdt_left_for_most_expensive_order / FPDecimal::from(201_000u128); - let expected_quantity = - most_expensive_order_quantity + (FPDecimal::from(4u128) + FPDecimal::from(3u128)); - - // round to min tick - let expected_quantity_rounded = - round_to_min_tick(expected_quantity, FPDecimal::must_from_str("0.001")); - - // calculate dust notional value as this will be the portion of user's funds that will stay in the contract - let dust = expected_quantity - expected_quantity_rounded; - // we need to use worst priced order - let dust_value = dust * FPDecimal::from(201_000u128); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: USDT.to_string(), - target_denom: ETH.to_string(), - from_quantity: FPDecimal::from(swapper_usdt), - }, - ) - .unwrap(); - - assert_eq!( - query_result.result_quantity, expected_quantity_rounded, - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![FPCoin { - amount: FPDecimal::must_from_str("3536.188217"), - denom: "usdt".to_string(), - }]; - - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - FPDecimal::must_from_str("0.000001"), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ETH.to_string(), - min_output_quantity: FPDecimal::from(11u128), - }, - &[coin(swapper_usdt, USDT)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, USDT, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let expected_execute_result = expected_quantity.int(); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, expected_execute_result, - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - let mut expected_contract_balances_after = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()) + dust_value; - expected_contract_balances_after = expected_contract_balances_after.int(); - - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()), - expected_contract_balances_after, - "contract balance changed unexpectedly after swap" - ); -} - -#[test] -fn it_executes_a_swap_between_base_assets_with_external_fee_recipient() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let fee_recipient = must_init_account_with_funds(&app, &[]); - let contr_addr = init_contract_with_fee_recipient_and_get_address( - &wasm, - &owner, - &[str_coin("10_000", USDT, Decimals::Six)], - &fee_recipient, - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - // calculate relayer's share of the fee based on assumptions that all orders are matched - let buy_orders_nominal_total_value = FPDecimal::from(201_000u128) * FPDecimal::from(5u128) - + FPDecimal::from(195_000u128) * FPDecimal::from(4u128) - + FPDecimal::from(192_000u128) * FPDecimal::from(3u128); - let relayer_sell_fee = buy_orders_nominal_total_value - * FPDecimal::must_from_str(&format!( - "{}", - DEFAULT_TAKER_FEE * DEFAULT_ATOMIC_MULTIPLIER * DEFAULT_RELAYER_SHARE - )); - - // calculate relayer's share of the fee based on assumptions that some of orders are matched - let expected_nominal_buy_most_expensive_match_quantity = - FPDecimal::must_from_str("488.2222155454736648"); - let sell_orders_nominal_total_value = FPDecimal::from(800u128) * FPDecimal::from(800u128) - + FPDecimal::from(810u128) * FPDecimal::from(800u128) - + FPDecimal::from(820u128) * FPDecimal::from(800u128) - + FPDecimal::from(830u128) * expected_nominal_buy_most_expensive_match_quantity; - let relayer_buy_fee = sell_orders_nominal_total_value - * FPDecimal::must_from_str(&format!( - "{}", - DEFAULT_TAKER_FEE * DEFAULT_ATOMIC_MULTIPLIER * DEFAULT_RELAYER_SHARE - )); - let expected_fee_for_fee_recipient = relayer_buy_fee + relayer_sell_fee; - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ) - .unwrap(); - - assert_eq!( - query_result.result_quantity, - FPDecimal::must_from_str("2888.221"), //slightly rounded down vs spreadsheet - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: FPDecimal::must_from_str("5902.5"), - denom: "usdt".to_string(), - }, - FPCoin { - amount: FPDecimal::must_from_str("5873.061097"), - denom: "usdt".to_string(), - }, - ]; - - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - FPDecimal::must_from_str("0.000001"), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2888u128), - }, - &[coin(12, ETH)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, - FPDecimal::must_from_str("2888"), - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_balance_usdt_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - let contract_balance_usdt_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - - assert!( - contract_balance_usdt_after >= contract_balance_usdt_before, - "Contract lost some money after swap. Balance before: {contract_balance_usdt_before}, after: {contract_balance_usdt_after}", - ); - - let max_diff = human_to_dec("0.00001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_balance_usdt_after, - contract_balance_usdt_before, - max_diff, - ), - "Contract balance changed too much. Before: {}, After: {}", - contract_balances_before[0].amount, - contract_balances_after[0].amount - ); - - let fee_recipient_balance = query_all_bank_balances(&bank, &fee_recipient.address()); - - assert_eq!( - fee_recipient_balance.len(), - 1, - "wrong number of denoms in fee recipient's balances" - ); - assert_eq!( - fee_recipient_balance[0].denom, USDT, - "fee recipient did not receive fee in expected denom" - ); - assert_eq!( - FPDecimal::must_from_str(fee_recipient_balance[0].amount.as_str()), - expected_fee_for_fee_recipient.int(), - "fee recipient did not receive expected fee" - ); -} - -#[test] -fn it_reverts_the_swap_if_there_isnt_enough_buffer_for_buying_target_asset() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("0.001", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let query_result: RunnerResult = wasm.query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ); - - assert!(query_result.is_err(), "query should fail"); - assert!( - query_result - .unwrap_err() - .to_string() - .contains("Swap amount too high"), - "wrong query error message" - ); - - let contract_balances_before = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert!(execute_result.is_err(), "execute should fail"); - assert!( - execute_result - .unwrap_err() - .to_string() - .contains("Swap amount too high"), - "wrong execute error message" - ); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changes after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changes after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_reverts_swap_if_no_funds_were_passed() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let contract_balances_before = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[], - &swapper, - ); - let expected_error = RunnerError::ExecuteError { msg: "failed to execute message; message index: 0: Custom Error: \"Only one denom can be passed in funds\": execute wasm contract failed".to_string() }; - assert_eq!( - execute_result.unwrap_err(), - expected_error, - "wrong error message" - ); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changes after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changes after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_reverts_swap_if_multiple_funds_were_passed() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let eth_balance = 12u128; - let atom_balance = 10u128; - - let swapper = must_init_account_with_funds( - &app, - &[ - coin(eth_balance, ETH), - coin(atom_balance, ATOM), - str_coin("500_000", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(10u128), - }, - &[coin(10, ATOM), coin(12, ETH)], - &swapper, - ); - assert!( - execute_result - .unwrap_err() - .to_string() - .contains("Only one denom can be passed in funds"), - "wrong error message" - ); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(eth_balance), - "wrong ETH balance after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::from(atom_balance), - "wrong ATOM balance after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_reverts_if_user_passes_quantities_equal_to_zero() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let query_result: RunnerResult = wasm.query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(0u128), - }, - ); - assert!( - query_result - .unwrap_err() - .to_string() - .contains("source_quantity must be positive"), - "incorrect error returned by query" - ); - - let contract_balances_before = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let err = wasm - .execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::ZERO, - }, - &[coin(12, ETH)], - &swapper, - ) - .unwrap_err(); - assert!( - err.to_string() - .contains("Output quantity must be positive!"), - "incorrect error returned by execute" - ); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::must_from_str("12"), - "swap should not have occurred" - ); - assert_eq!( - to_balance, - FPDecimal::must_from_str("0"), - "swapper should not have received any target tokens" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_reverts_if_user_passes_negative_quantities() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - app.increase_time(1); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::must_from_str("-1"), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert!( - execute_result.is_err(), - "swap with negative minimum amount to receive did not fail" - ); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after failed swap" - ); -} - -#[test] -fn it_reverts_if_there_arent_enough_orders_to_satisfy_min_quantity() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - - create_limit_order(&app, &trader1, &spot_market_2_id, OrderSide::Sell, 800, 800); - create_limit_order(&app, &trader2, &spot_market_2_id, OrderSide::Sell, 810, 800); - create_limit_order(&app, &trader3, &spot_market_2_id, OrderSide::Sell, 820, 800); - create_limit_order(&app, &trader1, &spot_market_2_id, OrderSide::Sell, 830, 450); //not enough for minimum requested - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let query_result: RunnerResult = wasm.query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ); - assert_eq!( - query_result.unwrap_err(), - QueryError { - msg: "Generic error: Not enough liquidity to fulfill order: query wasm contract failed" - .to_string() - }, - "wrong error message" - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert_eq!(execute_result.unwrap_err(), RunnerError::ExecuteError { msg: "failed to execute message; message index: 0: dispatch: submessages: reply: Generic error: Not enough liquidity to fulfill order: execute wasm contract failed".to_string() }, "wrong error message"); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after swap" - ); -} - -#[test] -fn it_reverts_if_min_quantity_cannot_be_reached() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - // set the market - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let min_quantity = 3500u128; - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(min_quantity), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert_eq!(execute_result.unwrap_err(), RunnerError::ExecuteError { msg: format!("failed to execute message; message index: 0: dispatch: submessages: reply: dispatch: submessages: reply: Min expected swap amount ({min_quantity}) not reached: execute wasm contract failed") }, "wrong error message"); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after failed swap" - ); -} - -#[test] -fn it_reverts_if_market_is_paused() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let signer = init_default_signer_account(&app); - let validator = init_default_validator_account(&app); - fund_account_with_some_inj(&bank, &signer, &validator); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - pause_spot_market(&app, spot_market_1_id.as_str(), &signer, &validator); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let query_error: RunnerError = wasm - .query::( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ) - .unwrap_err(); - - assert!( - query_error.to_string().contains("Querier contract error"), - "wrong error returned by query" - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert!( - execute_result - .unwrap_err() - .to_string() - .contains("Querier contract error"), - "wrong error returned by execute" - ); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after failed swap" - ); -} - -#[test] -fn it_reverts_if_user_doesnt_have_enough_inj_to_pay_for_gas() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let swapper = must_init_account_with_funds(&app, &[coin(12, ETH), coin(10, INJ)]); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - app.increase_time(1); - - let query_result: RunnerResult = wasm.query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: FPDecimal::from(12u128), - }, - ); - - let target_quantity = query_result.unwrap().result_quantity; - - assert_eq!( - target_quantity, - FPDecimal::must_from_str("2893.886"), //slightly underestimated vs spreadsheet - "incorrect swap result estimate returned by query" - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(2800u128), - }, - &[coin(12, ETH)], - &swapper, - ); - - assert_eq!(execute_result.unwrap_err(), ExecuteError { msg: "spendable balance 10inj is smaller than 2500inj: insufficient funds: insufficient funds".to_string() }, "wrong error returned by execute"); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after failed swap" - ); -} - -#[test] -fn it_reverts_if_target_quantity_is_not_multiple_of_min_quantity_tick_size() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = init_default_signer_account(&app); - let _validator = init_default_validator_account(&app); - let owner = init_rich_account(&app); - - // set the market - let spot_market_1_id = launch_spot_market(&exchange, &owner, ETH, USDT); - let spot_market_2_id = launch_spot_market(&exchange, &owner, ATOM, USDT); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("100_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_eth_buy_orders(&app, &spot_market_1_id, &trader1, &trader2); - create_atom_sell_orders(&app, &spot_market_2_id, &trader1, &trader2, &trader3); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[coin(12, ETH), str_coin("500_000", INJ, Decimals::Eighteen)], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let min_quantity = FPDecimal::must_from_str("3500.0001"); - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: min_quantity, - }, - &[coin(12, ETH)], - &swapper, - ); - - assert_eq!(execute_result.unwrap_err(), RunnerError::ExecuteError { msg: "failed to execute message; message index: 0: Generic error: Target quantity must be a multiple of min_quantity_tick_size: execute wasm contract failed".to_string() }, "wrong error message"); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::from(12u128), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balance has changed after failed swap" - ); -} - -#[test] -fn it_allows_admin_to_withdraw_all_funds_from_contract_to_his_address() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let bank = Bank::new(&app); - - let usdt_to_withdraw = str_coin("10_000", USDT, Decimals::Six); - let eth_to_withdraw = str_coin("0.00062", ETH, Decimals::Eighteen); - - let owner = must_init_account_with_funds( - &app, - &[ - eth_to_withdraw.clone(), - str_coin("1", INJ, Decimals::Eighteen), - usdt_to_withdraw.clone(), - ], - ); - - let initial_contract_balance = &[eth_to_withdraw, usdt_to_withdraw]; - let contr_addr = - init_self_relaying_contract_and_get_address(&wasm, &owner, initial_contract_balance); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::WithdrawSupportFunds { - coins: initial_contract_balance.to_vec(), - target_address: Addr::unchecked(owner.address()), - }, - &[], - &owner, - ); - - assert!(execute_result.is_ok(), "failed to withdraw support funds"); - let contract_balances_after = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_after.len(), - 0, - "contract had some balances after withdraw" - ); - - let owner_eth_balance = query_bank_balance(&bank, ETH, owner.address().as_str()); - assert_eq!( - owner_eth_balance, - FPDecimal::from(initial_contract_balance[0].amount), - "wrong owner eth balance after withdraw" - ); - - let owner_usdt_balance = query_bank_balance(&bank, USDT, owner.address().as_str()); - assert_eq!( - owner_usdt_balance, - FPDecimal::from(initial_contract_balance[1].amount), - "wrong owner usdt balance after withdraw" - ); -} - -#[test] -fn it_allows_admin_to_withdraw_all_funds_from_contract_to_other_address() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let bank = Bank::new(&app); - - let usdt_to_withdraw = str_coin("10_000", USDT, Decimals::Six); - let eth_to_withdraw = str_coin("0.00062", ETH, Decimals::Eighteen); - - let owner = must_init_account_with_funds( - &app, - &[ - eth_to_withdraw.clone(), - str_coin("1", INJ, Decimals::Eighteen), - usdt_to_withdraw.clone(), - ], - ); - - let initial_contract_balance = &[eth_to_withdraw, usdt_to_withdraw]; - let contr_addr = - init_self_relaying_contract_and_get_address(&wasm, &owner, initial_contract_balance); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - let random_dude = must_init_account_with_funds(&app, &[]); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::WithdrawSupportFunds { - coins: initial_contract_balance.to_vec(), - target_address: Addr::unchecked(random_dude.address()), - }, - &[], - &owner, - ); - - assert!(execute_result.is_ok(), "failed to withdraw support funds"); - let contract_balances_after = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_after.len(), - 0, - "contract had some balances after withdraw" - ); - - let random_dude_eth_balance = query_bank_balance(&bank, ETH, random_dude.address().as_str()); - assert_eq!( - random_dude_eth_balance, - FPDecimal::from(initial_contract_balance[0].amount), - "wrong owner eth balance after withdraw" - ); - - let random_dude_usdt_balance = query_bank_balance(&bank, USDT, random_dude.address().as_str()); - assert_eq!( - random_dude_usdt_balance, - FPDecimal::from(initial_contract_balance[1].amount), - "wrong owner usdt balance after withdraw" - ); -} - -#[test] -fn it_doesnt_allow_non_admin_to_withdraw_anything_from_contract() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let bank = Bank::new(&app); - - let usdt_to_withdraw = str_coin("10_000", USDT, Decimals::Six); - let eth_to_withdraw = str_coin("0.00062", ETH, Decimals::Eighteen); - - let owner = must_init_account_with_funds( - &app, - &[ - eth_to_withdraw.clone(), - str_coin("1", INJ, Decimals::Eighteen), - usdt_to_withdraw.clone(), - ], - ); - - let initial_contract_balance = &[eth_to_withdraw, usdt_to_withdraw]; - let contr_addr = - init_self_relaying_contract_and_get_address(&wasm, &owner, initial_contract_balance); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - let random_dude = must_init_account_with_funds(&app, &[coin(1_000_000_000_000, INJ)]); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::WithdrawSupportFunds { - coins: initial_contract_balance.to_vec(), - target_address: Addr::unchecked(owner.address()), - }, - &[], - &random_dude, - ); - - assert!( - execute_result.is_err(), - "succeeded to withdraw support funds" - ); - let contract_balances_after = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_after, contract_balances_before, - "contract balances changed after failed withdraw" - ); - - let random_dude_eth_balance = query_bank_balance(&bank, ETH, random_dude.address().as_str()); - assert_eq!( - random_dude_eth_balance, - FPDecimal::ZERO, - "random dude has some eth balance after failed withdraw" - ); - - let random_dude_usdt_balance = query_bank_balance(&bank, USDT, random_dude.address().as_str()); - assert_eq!( - random_dude_usdt_balance, - FPDecimal::ZERO, - "random dude has some usdt balance after failed withdraw" - ); -} - -fn create_eth_buy_orders( - app: &InjectiveTestApp, - market_id: &str, - trader1: &SigningAccount, - trader2: &SigningAccount, -) { - create_limit_order(app, trader1, market_id, OrderSide::Buy, 201_000, 5); - create_limit_order(app, trader2, market_id, OrderSide::Buy, 195_000, 4); - create_limit_order(app, trader2, market_id, OrderSide::Buy, 192_000, 3); -} - -fn create_atom_sell_orders( - app: &InjectiveTestApp, - market_id: &str, - trader1: &SigningAccount, - trader2: &SigningAccount, - trader3: &SigningAccount, -) { - create_limit_order(app, trader1, market_id, OrderSide::Sell, 800, 800); - create_limit_order(app, trader2, market_id, OrderSide::Sell, 810, 800); - create_limit_order(app, trader3, market_id, OrderSide::Sell, 820, 800); - create_limit_order(app, trader1, market_id, OrderSide::Sell, 830, 800); -} diff --git a/contracts/swap/src/testing/integration_tests/integration_realistic_tests_exact_quantity.rs b/contracts/swap/src/testing/integration_tests/integration_realistic_tests_exact_quantity.rs deleted file mode 100644 index 53e129f..0000000 --- a/contracts/swap/src/testing/integration_tests/integration_realistic_tests_exact_quantity.rs +++ /dev/null @@ -1,1685 +0,0 @@ -use injective_test_tube::{Account, Bank, Exchange, InjectiveTestApp, Module, Wasm}; -use std::ops::Neg; - -use crate::helpers::Scaled; -use injective_math::FPDecimal; - -use crate::msg::{ExecuteMsg, QueryMsg}; -use crate::testing::test_utils::{ - are_fpdecimals_approximately_equal, assert_fee_is_as_expected, - create_realistic_atom_usdt_sell_orders_from_spreadsheet, - create_realistic_eth_usdt_buy_orders_from_spreadsheet, - create_realistic_eth_usdt_sell_orders_from_spreadsheet, - create_realistic_inj_usdt_buy_orders_from_spreadsheet, create_realistic_limit_order, - create_realistic_usdt_usdc_both_side_orders, human_to_dec, init_rich_account, - init_self_relaying_contract_and_get_address, launch_realistic_atom_usdt_spot_market, - launch_realistic_inj_usdt_spot_market, launch_realistic_usdt_usdc_spot_market, - launch_realistic_weth_usdt_spot_market, must_init_account_with_funds, query_all_bank_balances, - query_bank_balance, set_route_and_assert_success, str_coin, Decimals, OrderSide, ATOM, ETH, - INJ, INJ_2, USDC, USDT, -}; -use crate::types::{FPCoin, SwapEstimationResult}; - -/* - This test suite focuses on using using realistic values both for spot markets and for orders and - focuses on swaps requesting exact amount. This works as expected apart, when we are converting very - low quantities from a source asset that is orders of magnitude more expensive than the target - asset (as we round up to min quantity tick size). - - ATOM/USDT market parameters was taken from mainnet. ETH/USDT market parameters mirror WETH/USDT - spot market on mainnet. INJ_2/USDT mirrors mainnet's INJ/USDT market (we used a different denom - to avoid mixing balance changes related to gas payments). - - All values used in these tests come from the 2nd, 3rd and 4th tab of this spreadsheet: - https://docs.google.com/spreadsheets/d/1-0epjX580nDO_P2mm1tSjhvjJVppsvrO1BC4_wsBeyA/edit?usp=sharing - - In all tests contract is configured to self-relay trades and thus receive a 60% fee discount. -*/ - -struct Percent<'a>(&'a str); - -#[test] -fn it_swaps_eth_to_get_minimum_exact_amount_of_atom_by_mildly_rounding_up() { - exact_two_hop_eth_atom_swap_test_template(human_to_dec("0.01", Decimals::Six), Percent("2200")) -} - -#[test] -fn it_swaps_eth_to_get_very_low_exact_amount_of_atom_by_heavily_rounding_up() { - exact_two_hop_eth_atom_swap_test_template(human_to_dec("0.11", Decimals::Six), Percent("110")) -} - -#[test] -fn it_swaps_eth_to_get_low_exact_amount_of_atom_by_rounding_up() { - exact_two_hop_eth_atom_swap_test_template(human_to_dec("4.12", Decimals::Six), Percent("10")) -} - -#[test] -fn it_correctly_swaps_eth_to_get_normal_exact_amount_of_atom() { - exact_two_hop_eth_atom_swap_test_template(human_to_dec("12.05", Decimals::Six), Percent("1")) -} - -#[test] -fn it_correctly_swaps_eth_to_get_high_exact_amount_of_atom() { - exact_two_hop_eth_atom_swap_test_template(human_to_dec("612", Decimals::Six), Percent("1")) -} - -#[test] -fn it_correctly_swaps_eth_to_get_very_high_exact_amount_of_atom() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_limit_order( - &app, - &trader1, - &spot_market_1_id, - OrderSide::Buy, - "2137.2", - "2.78", - Decimals::Eighteen, - Decimals::Six, - ); //order not present in the spreadsheet - - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - create_realistic_limit_order( - &app, - &trader1, - &spot_market_2_id, - OrderSide::Sell, - "9.11", - "321.11", - Decimals::Six, - Decimals::Six, - ); //order not present in the spreadsheet - - app.increase_time(1); - - let eth_to_swap = "4.4"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(eth_to_swap, ETH, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let exact_quantity_to_receive = human_to_dec("1014.19", Decimals::Six); - - let query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let expected_difference = - human_to_dec(eth_to_swap, Decimals::Eighteen) - query_result.result_quantity; - let swapper_eth_balance_after = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - swapper_eth_balance_after, expected_difference, - "wrong amount of ETH was exchanged" - ); - - assert!( - swapper_atom_balance_after >= exact_quantity_to_receive, - "swapper got less than exact amount required -> expected: {} ATOM, actual: {} ATOM", - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()) - ); - - let one_percent_diff = exact_quantity_to_receive * FPDecimal::must_from_str("0.01"); - - assert!( - are_fpdecimals_approximately_equal( - swapper_atom_balance_after, - exact_quantity_to_receive, - one_percent_diff, - ), - "swapper did not receive expected exact amount +/- 1% -> expected: {} ATOM, actual: {} ATOM, max diff: {} ATOM", - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()), - one_percent_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {contract_usdt_balance_after}, previous balance: {contract_usdt_balance_before}", - ); - - // contract is allowed to earn extra 0.73 USDT from the swap of ~$8450 worth of ETH - let max_diff = human_to_dec("0.8", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {}, previous balance: {}. Max diff: {}", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_swaps_inj_to_get_minimum_exact_amount_of_atom_by_mildly_rounding_up() { - exact_two_hop_inj_atom_swap_test_template(human_to_dec("0.01", Decimals::Six), Percent("0")) -} - -#[test] -fn it_swaps_inj_to_get_very_low_exact_amount_of_atom() { - exact_two_hop_inj_atom_swap_test_template(human_to_dec("0.11", Decimals::Six), Percent("0")) -} - -#[test] -fn it_swaps_inj_to_get_low_exact_amount_of_atom() { - exact_two_hop_inj_atom_swap_test_template(human_to_dec("4.12", Decimals::Six), Percent("0")) -} - -#[test] -fn it_correctly_swaps_inj_to_get_normal_exact_amount_of_atom() { - exact_two_hop_inj_atom_swap_test_template(human_to_dec("12.05", Decimals::Six), Percent("0")) -} - -#[test] -fn it_correctly_swaps_inj_to_get_high_exact_amount_of_atom() { - exact_two_hop_inj_atom_swap_test_template(human_to_dec("612", Decimals::Six), Percent("0.01")) -} - -#[test] -fn it_correctly_swaps_inj_to_get_very_high_exact_amount_of_atom() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("10_000", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_limit_order( - &app, - &trader1, - &spot_market_1_id, - OrderSide::Buy, - "8.99", - "280.2", - Decimals::Eighteen, - Decimals::Six, - ); //order not present in the spreadsheet - - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - create_realistic_limit_order( - &app, - &trader1, - &spot_market_2_id, - OrderSide::Sell, - "9.11", - "321.11", - Decimals::Six, - Decimals::Six, - ); //order not present in the spreadsheet - - app.increase_time(1); - - let inj_to_swap = "1100.1"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let exact_quantity_to_receive = human_to_dec("1010.12", Decimals::Six); - let max_diff_percentage = Percent("0.01"); - - let query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ATOM.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let expected_difference = - human_to_dec(inj_to_swap, Decimals::Eighteen) - query_result.result_quantity; - let swapper_inj_balance_after = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - swapper_inj_balance_after, expected_difference, - "wrong amount of INJ was exchanged" - ); - - assert!( - swapper_atom_balance_after >= exact_quantity_to_receive, - "swapper got less than exact amount required -> expected: {} ATOM, actual: {} ATOM", - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()) - ); - - let one_percent_diff = exact_quantity_to_receive - * (FPDecimal::must_from_str(max_diff_percentage.0) / FPDecimal::from(100u128)); - - assert!( - are_fpdecimals_approximately_equal( - swapper_atom_balance_after, - exact_quantity_to_receive, - one_percent_diff, - ), - "swapper did not receive expected exact ATOM amount +/- {}% -> expected: {} ATOM, actual: {} ATOM, max diff: {} ATOM", - max_diff_percentage.0, - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()), - one_percent_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {contract_usdt_balance_after}, previous balance: {contract_usdt_balance_before}", - ); - - // contract is allowed to earn extra 0.7 USDT from the swap of ~$8150 worth of INJ - let max_diff = human_to_dec("0.7", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_swaps_inj_to_get_minimum_exact_amount_of_eth() { - exact_two_hop_inj_eth_swap_test_template( - human_to_dec("0.001", Decimals::Eighteen), - Percent("0"), - ) -} - -#[test] -fn it_swaps_inj_to_get_low_exact_amount_of_eth() { - exact_two_hop_inj_eth_swap_test_template( - human_to_dec("0.012", Decimals::Eighteen), - Percent("0"), - ) -} - -#[test] -fn it_swaps_inj_to_get_normal_exact_amount_of_eth() { - exact_two_hop_inj_eth_swap_test_template(human_to_dec("0.1", Decimals::Eighteen), Percent("0")) -} - -#[test] -fn it_swaps_inj_to_get_high_exact_amount_of_eth() { - exact_two_hop_inj_eth_swap_test_template(human_to_dec("3.1", Decimals::Eighteen), Percent("0")) -} - -#[test] -fn it_swaps_inj_to_get_very_high_exact_amount_of_eth() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("10_000", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ETH, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_limit_order( - &app, - &trader1, - &spot_market_1_id, - OrderSide::Buy, - "8.99", - "1882.001", - Decimals::Eighteen, - Decimals::Six, - ); //order not present in the spreadsheet - create_realistic_eth_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - create_realistic_limit_order( - &app, - &trader3, - &spot_market_2_id, - OrderSide::Sell, - "2123.1", - "18.11", - Decimals::Eighteen, - Decimals::Six, - ); //order not present in the spreadsheet - - app.increase_time(1); - - let inj_to_swap = "2855.259"; - let exact_quantity_to_receive = human_to_dec("11.2", Decimals::Eighteen); - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ETH.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ETH.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let expected_difference = - human_to_dec(inj_to_swap, Decimals::Eighteen) - query_result.result_quantity; - let swapper_inj_balance_after = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ETH, swapper.address().as_str()); - - assert_eq!( - swapper_inj_balance_after, expected_difference, - "wrong amount of INJ was exchanged" - ); - - assert!( - swapper_atom_balance_after >= exact_quantity_to_receive, - "swapper got less than exact amount required -> expected: {} ETH, actual: {} ETH", - exact_quantity_to_receive.scaled(Decimals::Eighteen.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let max_diff_percent = Percent("0"); - let one_percent_diff = exact_quantity_to_receive - * (FPDecimal::must_from_str(max_diff_percent.0) / FPDecimal::from(100u128)); - - assert!( - are_fpdecimals_approximately_equal( - swapper_atom_balance_after, - exact_quantity_to_receive, - one_percent_diff, - ), - "swapper did not receive expected exact ETH amount +/- {}% -> expected: {} ETH, actual: {} ETH, max diff: {} ETH", - max_diff_percent.0, - exact_quantity_to_receive.scaled(Decimals::Eighteen.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Eighteen.get_decimals().neg()), - one_percent_diff.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {contract_usdt_balance_after}, previous balance: {contract_usdt_balance_before}", - ); - - // contract is allowed to earn extra 1.6 USDT from the swap of ~$23500 worth of INJ - let max_diff = human_to_dec("1.6", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_correctly_swaps_between_markets_using_different_quote_assets_self_relaying() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1_000", USDT, Decimals::Six), - str_coin("1_000", USDC, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("1", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_usdt_usdc_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[ - str_coin("10", USDC, Decimals::Six), - str_coin("500", USDT, Decimals::Six), - ], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - USDC, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_usdt_usdc_both_side_orders(&app, &spot_market_2_id, &trader1); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin("1", INJ, Decimals::Eighteen), - str_coin("1", INJ_2, Decimals::Eighteen), - ], - ); - - let inj_to_swap = "1"; - let to_output_quantity = human_to_dec("8", Decimals::Six); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - to_quantity: to_output_quantity, - source_denom: INJ_2.to_string(), - target_denom: USDC.to_string(), - }, - ) - .unwrap(); - - let expected_input_quantity = human_to_dec("0.903", Decimals::Eighteen); - let max_diff = human_to_dec("0.001", Decimals::Eighteen); - - assert!( - are_fpdecimals_approximately_equal(expected_input_quantity, query_result.result_quantity, max_diff), - "incorrect swap result estimate returned by query. Expected: {} INJ, actual: {} INJ, max diff: {} INJ", - expected_input_quantity.scaled(Decimals::Eighteen.get_decimals().neg()), - query_result.result_quantity.scaled(Decimals::Eighteen.get_decimals().neg()), - max_diff.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let mut expected_fees = vec![ - FPCoin { - amount: human_to_dec("0.013365", Decimals::Six), - denom: USDT.to_string(), - }, - FPCoin { - amount: human_to_dec("0.01332", Decimals::Six), - denom: USDC.to_string(), - }, - ]; - - // we don't care too much about decimal fraction of the fee - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.1", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: USDC.to_string(), - target_output_quantity: to_output_quantity, - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, USDC, swapper.address().as_str()); - - let expected_inj_leftover = - human_to_dec(inj_to_swap, Decimals::Eighteen) - expected_input_quantity; - assert_eq!( - from_balance, expected_inj_leftover, - "incorrect original amount was left after swap" - ); - - let expected_amount = human_to_dec("8.00711", Decimals::Six); - - assert_eq!( - to_balance, - expected_amount, - "Swapper received less than expected minimum amount. Expected: {} USDC, actual: {} USDC", - expected_amount.scaled(Decimals::Six.get_decimals().neg()), - to_balance.scaled(Decimals::Six.get_decimals().neg()), - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 2, - "wrong number of denoms in contract balances" - ); - - // let's check contract's USDT balance - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {} USDT, previous balance: {} USDT", - contract_usdt_balance_after, - contract_usdt_balance_before - ); - - // contract is allowed to earn extra 0.001 USDT from the swap of ~$8 worth of INJ - let max_diff = human_to_dec("0.001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - // let's check contract's USDC balance - let contract_usdc_balance_before = - FPDecimal::must_from_str(contract_balances_before[1].amount.as_str()); - let contract_usdc_balance_after = - FPDecimal::must_from_str(contract_balances_after[1].amount.as_str()); - - assert!( - contract_usdc_balance_after >= contract_usdc_balance_before, - "Contract lost some money after swap. Actual balance: {} USDC, previous balance: {} USDC", - contract_usdc_balance_after, - contract_usdc_balance_before - ); - - // contract is allowed to earn extra 0.001 USDC from the swap of ~$8 worth of INJ - let max_diff = human_to_dec("0.001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdc_balance_after, - contract_usdc_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDC, previous balance: {} USDC. Max diff: {} USDC", - contract_usdc_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdc_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_doesnt_lose_buffer_if_exact_swap_of_eth_to_atom_is_executed_multiple_times() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - let eth_to_swap = "4.08"; - let iterations = 100i128; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin( - (FPDecimal::must_from_str(eth_to_swap) * FPDecimal::from(iterations)) - .to_string() - .as_str(), - ETH, - Decimals::Eighteen, - ), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let mut counter = 0; - - while counter < iterations { - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: human_to_dec("906", Decimals::Six), - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - counter += 1 - } - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_balance_usdt_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - let contract_balance_usdt_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - - assert!( - contract_balance_usdt_after >= contract_balance_usdt_before, - "Contract lost some money after swap. Starting balance: {contract_balance_usdt_after}, Current balance: {contract_balance_usdt_before}", - ); - - // single swap with the same values results in < 0.7 USDT earning, so we expected that 100 same swaps - // won't change balance by more than 0.7 * 100 = 70 USDT - let max_diff = human_to_dec("0.7", Decimals::Six) * FPDecimal::from(iterations); - - assert!(are_fpdecimals_approximately_equal( - contract_balance_usdt_after, - contract_balance_usdt_before, - max_diff, - ), "Contract balance changed too much. Starting balance: {}, Current balance: {}. Max diff: {}", - contract_balance_usdt_before.scaled(Decimals::Six.get_decimals().neg()), - contract_balance_usdt_after.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_reverts_when_funds_provided_are_below_required_to_get_exact_amount() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("10_000", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let inj_to_swap = "608"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let exact_quantity_to_receive = human_to_dec("600", Decimals::Six); - let swapper_inj_balance_before = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - - let _: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ATOM.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - let execute_result = wasm - .execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap_err(); - - assert!(execute_result.to_string().contains("Provided amount of 608000000000000000000 is below required amount of 609714000000000000000"), "wrong error message"); - - let swapper_inj_balance_after = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - swapper_inj_balance_before, swapper_inj_balance_after, - "some amount of INJ was exchanged" - ); - - assert_eq!( - FPDecimal::ZERO, - swapper_atom_balance_after, - "swapper received some ATOM" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert_eq!( - contract_usdt_balance_after, contract_usdt_balance_before, - "Contract's balance changed after failed swap", - ); -} - -// TEST TEMPLATES - -// source much more expensive than target -fn exact_two_hop_eth_atom_swap_test_template( - exact_quantity_to_receive: FPDecimal, - max_diff_percentage: Percent, -) { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let eth_to_swap = "4.08"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(eth_to_swap, ETH, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let expected_difference = - human_to_dec(eth_to_swap, Decimals::Eighteen) - query_result.result_quantity; - let swapper_eth_balance_after = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - swapper_eth_balance_after, expected_difference, - "wrong amount of ETH was exchanged" - ); - - let one_percent_diff = exact_quantity_to_receive - * (FPDecimal::must_from_str(max_diff_percentage.0) / FPDecimal::from(100u128)); - - assert!( - swapper_atom_balance_after >= exact_quantity_to_receive, - "swapper got less than exact amount required -> expected: {} ATOM, actual: {} ATOM", - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()) - ); - - assert!( - are_fpdecimals_approximately_equal( - swapper_atom_balance_after, - exact_quantity_to_receive, - one_percent_diff, - ), - "swapper did not receive expected exact amount +/- {}% -> expected: {} ATOM, actual: {} ATOM, max diff: {} ATOM", - max_diff_percentage.0, - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()), - one_percent_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {contract_usdt_balance_after}, previous balance: {contract_usdt_balance_before}", - ); - - // contract is allowed to earn extra 0.7 USDT from the swap of ~$8150 worth of ETH - let max_diff = human_to_dec("0.7", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -// source more or less similarly priced as target -fn exact_two_hop_inj_atom_swap_test_template( - exact_quantity_to_receive: FPDecimal, - max_diff_percentage: Percent, -) { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("10_000", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let inj_to_swap = "973.258"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ATOM.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ATOM.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let expected_difference = - human_to_dec(inj_to_swap, Decimals::Eighteen) - query_result.result_quantity; - let swapper_inj_balance_after = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - swapper_inj_balance_after, expected_difference, - "wrong amount of INJ was exchanged" - ); - - assert!( - swapper_atom_balance_after >= exact_quantity_to_receive, - "swapper got less than exact amount required -> expected: {} ATOM, actual: {} ATOM", - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()) - ); - - let one_percent_diff = exact_quantity_to_receive - * (FPDecimal::must_from_str(max_diff_percentage.0) / FPDecimal::from(100u128)); - - assert!( - are_fpdecimals_approximately_equal( - swapper_atom_balance_after, - exact_quantity_to_receive, - one_percent_diff, - ), - "swapper did not receive expected exact ATOM amount +/- {}% -> expected: {} ATOM, actual: {} ATOM, max diff: {} ATOM", - max_diff_percentage.0, - exact_quantity_to_receive.scaled(Decimals::Six.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Six.get_decimals().neg()), - one_percent_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {contract_usdt_balance_after}, previous balance: {contract_usdt_balance_before}", - ); - - // contract is allowed to earn extra 0.7 USDT from the swap of ~$8150 worth of INJ - let max_diff = human_to_dec("0.7", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -// source much cheaper than target -fn exact_two_hop_inj_eth_swap_test_template( - exact_quantity_to_receive: FPDecimal, - max_diff_percentage: Percent, -) { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("10_000", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ETH, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_eth_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let inj_to_swap = "973.258"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetInputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ETH.to_string(), - to_quantity: exact_quantity_to_receive, - }, - ) - .unwrap(); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapExactOutput { - target_denom: ETH.to_string(), - target_output_quantity: exact_quantity_to_receive, - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let expected_difference = - human_to_dec(inj_to_swap, Decimals::Eighteen) - query_result.result_quantity; - let swapper_inj_balance_after = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let swapper_atom_balance_after = query_bank_balance(&bank, ETH, swapper.address().as_str()); - - assert_eq!( - swapper_inj_balance_after, expected_difference, - "wrong amount of INJ was exchanged" - ); - - assert!( - swapper_atom_balance_after >= exact_quantity_to_receive, - "swapper got less than exact amount required -> expected: {} ETH, actual: {} ETH", - exact_quantity_to_receive.scaled(Decimals::Eighteen.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let one_percent_diff = exact_quantity_to_receive - * (FPDecimal::must_from_str(max_diff_percentage.0) / FPDecimal::from(100u128)); - - assert!( - are_fpdecimals_approximately_equal( - swapper_atom_balance_after, - exact_quantity_to_receive, - one_percent_diff, - ), - "swapper did not receive expected exact ETH amount +/- {}% -> expected: {} ETH, actual: {} ETH, max diff: {} ETH", - max_diff_percentage.0, - exact_quantity_to_receive.scaled(Decimals::Eighteen.get_decimals().neg()), - swapper_atom_balance_after.scaled(Decimals::Eighteen.get_decimals().neg()), - one_percent_diff.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {contract_usdt_balance_after}, previous balance: {contract_usdt_balance_before}", - ); - - // contract is allowed to earn extra 0.7 USDT from the swap of ~$8500 worth of INJ - let max_diff = human_to_dec("0.82", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} diff --git a/contracts/swap/src/testing/integration_tests/integration_realistic_tests_min_quantity.rs b/contracts/swap/src/testing/integration_tests/integration_realistic_tests_min_quantity.rs deleted file mode 100644 index ed4d64a..0000000 --- a/contracts/swap/src/testing/integration_tests/integration_realistic_tests_min_quantity.rs +++ /dev/null @@ -1,1416 +0,0 @@ -use injective_test_tube::{Account, Bank, Exchange, InjectiveTestApp, Module, RunnerResult, Wasm}; -use std::ops::Neg; - -use crate::helpers::Scaled; -use injective_math::FPDecimal; - -use crate::msg::{ExecuteMsg, QueryMsg}; -use crate::testing::test_utils::{ - are_fpdecimals_approximately_equal, assert_fee_is_as_expected, - create_realistic_atom_usdt_sell_orders_from_spreadsheet, - create_realistic_eth_usdt_buy_orders_from_spreadsheet, - create_realistic_eth_usdt_sell_orders_from_spreadsheet, - create_realistic_inj_usdt_buy_orders_from_spreadsheet, - create_realistic_usdt_usdc_both_side_orders, human_to_dec, init_rich_account, - init_self_relaying_contract_and_get_address, launch_realistic_atom_usdt_spot_market, - launch_realistic_inj_usdt_spot_market, launch_realistic_usdt_usdc_spot_market, - launch_realistic_weth_usdt_spot_market, must_init_account_with_funds, query_all_bank_balances, - query_bank_balance, set_route_and_assert_success, str_coin, Decimals, ATOM, - DEFAULT_ATOMIC_MULTIPLIER, DEFAULT_SELF_RELAYING_FEE_PART, DEFAULT_TAKER_FEE, ETH, INJ, INJ_2, - USDC, USDT, -}; -use crate::types::{FPCoin, SwapEstimationResult}; - -/* - This test suite focuses on using using realistic values both for spot markets and for orders and - focuses on swaps requesting minimum amount. - - ATOM/USDT market parameters were taken from mainnet. ETH/USDT market parameters mirror WETH/USDT - spot market on mainnet. INJ_2/USDT mirrors mainnet's INJ/USDT market (we used a different denom - to avoid mixing balance changes related to swap with ones related to gas payments). - - Hardcoded values used in these tests come from the second tab of this spreadsheet: - https://docs.google.com/spreadsheets/d/1-0epjX580nDO_P2mm1tSjhvjJVppsvrO1BC4_wsBeyA/edit?usp=sharing - - In all tests contract is configured to self-relay trades and thus receive a 60% fee discount. -*/ - -#[test] -fn happy_path_two_hops_swap_eth_atom_realistic_values_self_relaying() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let eth_to_swap = "4.08"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(eth_to_swap, ETH, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: human_to_dec(eth_to_swap, Decimals::Eighteen), - }, - ) - .unwrap(); - - // it's expected that it is slightly less than what's in the spreadsheet - let expected_amount = human_to_dec("906.17", Decimals::Six); - - assert_eq!( - query_result.result_quantity, expected_amount, - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: human_to_dec("12.221313", Decimals::Six), - denom: "usdt".to_string(), - }, - FPCoin { - amount: human_to_dec("12.184704", Decimals::Six), - denom: "usdt".to_string(), - }, - ]; - - // we don't care too much about decimal fraction of the fee - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.1", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(906u128), - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - - assert!( - to_balance >= expected_amount, - "Swapper received less than expected minimum amount. Expected: {} ATOM, actual: {} ATOM", - expected_amount.scaled(Decimals::Six.get_decimals().neg()), - to_balance.scaled(Decimals::Six.get_decimals().neg()), - ); - - let max_diff = human_to_dec("0.1", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - expected_amount, - to_balance, - max_diff, - ), - "Swapper did not receive expected amount. Expected: {} ATOM, actual: {} ATOM, max diff: {} ATOM", - expected_amount.scaled(Decimals::Six.get_decimals().neg()), - to_balance.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {} USDT, previous balance: {} USDT", - contract_usdt_balance_after, - contract_usdt_balance_before - ); - - // contract is allowed to earn extra 0.7 USDT from the swap of ~$8150 worth of ETH - let max_diff = human_to_dec("0.7", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn happy_path_two_hops_swap_inj_eth_realistic_values_self_relaying() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("1", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ETH, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_eth_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let inj_to_swap = "973.258"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ETH.to_string(), - from_quantity: human_to_dec(inj_to_swap, Decimals::Eighteen), - }, - ) - .unwrap(); - - // it's expected that it is slightly less than what's in the spreadsheet - let expected_amount = human_to_dec("3.994", Decimals::Eighteen); - - assert_eq!( - query_result.result_quantity, expected_amount, - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: human_to_dec("12.73828775", Decimals::Six), - denom: "usdt".to_string(), - }, - FPCoin { - amount: human_to_dec("12.70013012", Decimals::Six), - denom: "usdt".to_string(), - }, - ]; - - // we don't care too much about decimal fraction of the fee - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.1", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ETH.to_string(), - min_output_quantity: FPDecimal::from(906u128), - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - - assert!( - to_balance >= expected_amount, - "Swapper received less than expected minimum amount. Expected: {} ETH, actual: {} ETH", - expected_amount.scaled(Decimals::Eighteen.get_decimals().neg()), - to_balance.scaled(Decimals::Eighteen.get_decimals().neg()), - ); - - let max_diff = human_to_dec("0.1", Decimals::Eighteen); - - assert!( - are_fpdecimals_approximately_equal( - expected_amount, - to_balance, - max_diff, - ), - "Swapper did not receive expected amount. Expected: {} ETH, actual: {} ETH, max diff: {} ETH", - expected_amount.scaled(Decimals::Eighteen.get_decimals().neg()), - to_balance.scaled(Decimals::Eighteen.get_decimals().neg()), - max_diff.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {} USDT, previous balance: {} USDT", - contract_usdt_balance_after, - contract_usdt_balance_before - ); - - // contract is allowed to earn extra 0.7 USDT from the swap of ~$8150 worth of ETH - let max_diff = human_to_dec("0.7", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn happy_path_two_hops_swap_inj_atom_realistic_values_self_relaying() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("1", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let inj_to_swap = "973.258"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(inj_to_swap, INJ_2, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: INJ_2.to_string(), - target_denom: ATOM.to_string(), - from_quantity: human_to_dec(inj_to_swap, Decimals::Eighteen), - }, - ) - .unwrap(); - - // it's expected that it is slightly less than what's in the spreadsheet - let expected_amount = human_to_dec("944.26", Decimals::Six); - - assert_eq!( - query_result.result_quantity, expected_amount, - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: human_to_dec("12.73828775", Decimals::Six), - denom: "usdt".to_string(), - }, - FPCoin { - amount: human_to_dec("12.70013012", Decimals::Six), - denom: "usdt".to_string(), - }, - ]; - - // we don't care too much about decimal fraction of the fee - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.1", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(944u128), - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - - assert!( - to_balance >= expected_amount, - "Swapper received less than expected minimum amount. Expected: {} ATOM, actual: {} ATOM", - expected_amount.scaled(Decimals::Six.get_decimals().neg()), - to_balance.scaled(Decimals::Six.get_decimals().neg()), - ); - - let max_diff = human_to_dec("0.1", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - expected_amount, - to_balance, - max_diff, - ), - "Swapper did not receive expected amount. Expected: {} ATOM, actual: {} ATOM, max diff: {} ATOM", - expected_amount.scaled(Decimals::Six.get_decimals().neg()), - to_balance.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {} USDT, previous balance: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()) - ); - - // contract is allowed to earn extra 0.82 USDT from the swap of ~$8500 worth of INJ - let max_diff = human_to_dec("0.82", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {}, previous balance: {}. Max diff: {}", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_executes_swap_between_markets_using_different_quote_assets_self_relaying() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1_000", USDT, Decimals::Six), - str_coin("1_000", USDC, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - str_coin("1", INJ_2, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_usdt_usdc_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[ - str_coin("10", USDC, Decimals::Six), - str_coin("500", USDT, Decimals::Six), - ], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - INJ_2, - USDC, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - - create_realistic_inj_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_usdt_usdc_both_side_orders(&app, &spot_market_2_id, &trader1); - - app.increase_time(1); - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin("1", INJ, Decimals::Eighteen), - str_coin("1", INJ_2, Decimals::Eighteen), - ], - ); - - let inj_to_swap = "1"; - - let mut query_result: SwapEstimationResult = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: INJ_2.to_string(), - target_denom: USDC.to_string(), - from_quantity: human_to_dec(inj_to_swap, Decimals::Eighteen), - }, - ) - .unwrap(); - - let expected_amount = human_to_dec("8.867", Decimals::Six); - let max_diff = human_to_dec("0.001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal(expected_amount, query_result.result_quantity, max_diff), - "incorrect swap result estimate returned by query" - ); - - let mut expected_fees = vec![ - FPCoin { - amount: human_to_dec("0.013365", Decimals::Six), - denom: USDT.to_string(), - }, - FPCoin { - amount: human_to_dec("0.01332", Decimals::Six), - denom: USDC.to_string(), - }, - ]; - - // we don't care too much about decimal fraction of the fee - assert_fee_is_as_expected( - &mut query_result.expected_fees, - &mut expected_fees, - human_to_dec("0.1", Decimals::Six), - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 2, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: USDC.to_string(), - min_output_quantity: FPDecimal::from(8u128), - }, - &[str_coin(inj_to_swap, INJ_2, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, INJ_2, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, USDC, swapper.address().as_str()); - - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - - assert!( - to_balance >= expected_amount, - "Swapper received less than expected minimum amount. Expected: {} USDC, actual: {} USDC", - expected_amount.scaled(Decimals::Eighteen.get_decimals().neg()), - to_balance.scaled(Decimals::Eighteen.get_decimals().neg()), - ); - - let max_diff = human_to_dec("0.1", Decimals::Eighteen); - - assert!( - are_fpdecimals_approximately_equal( - expected_amount, - to_balance, - max_diff, - ), - "Swapper did not receive expected amount. Expected: {} USDC, actual: {} USDC, max diff: {} USDC", - expected_amount.scaled(Decimals::Eighteen.get_decimals().neg()), - to_balance.scaled(Decimals::Eighteen.get_decimals().neg()), - max_diff.scaled(Decimals::Eighteen.get_decimals().neg()) - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 2, - "wrong number of denoms in contract balances" - ); - - // let's check contract's USDT balance - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {} USDT, previous balance: {} USDT", - contract_usdt_balance_after, - contract_usdt_balance_before - ); - - // contract is allowed to earn extra 0.001 USDT from the swap of ~$8 worth of INJ - let max_diff = human_to_dec("0.001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDT, previous balance: {} USDT. Max diff: {} USDT", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); - - // let's check contract's USDC balance - let contract_usdc_balance_before = - FPDecimal::must_from_str(contract_balances_before[1].amount.as_str()); - let contract_usdc_balance_after = - FPDecimal::must_from_str(contract_balances_after[1].amount.as_str()); - - assert!( - contract_usdc_balance_after >= contract_usdc_balance_before, - "Contract lost some money after swap. Actual balance: {} USDC, previous balance: {} USDC", - contract_usdc_balance_after, - contract_usdc_balance_before - ); - - // contract is allowed to earn extra 0.001 USDC from the swap of ~$8 worth of INJ - let max_diff = human_to_dec("0.001", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdc_balance_after, - contract_usdc_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {} USDC, previous balance: {} USDC. Max diff: {} USDC", - contract_usdc_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdc_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_doesnt_lose_buffer_if_executed_multiple_times() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); - - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - let eth_to_swap = "4.08"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin( - (FPDecimal::must_from_str(eth_to_swap) * FPDecimal::from(100u128)) - .to_string() - .as_str(), - ETH, - Decimals::Eighteen, - ), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let mut counter = 0; - let iterations = 100; - - while counter < iterations { - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(906u128), - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - counter += 1 - } - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_balance_usdt_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - let contract_balance_usdt_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - - assert!( - contract_balance_usdt_after >= contract_balance_usdt_before, - "Contract lost some money after swap. Starting balance: {}, Current balance: {}", - contract_balance_usdt_after, - contract_balance_usdt_before - ); - - // single swap with the same values results in < 0.7 USDT earning, so we expected that 100 same swaps - // won't change balance by more than 0.7 * 100 = 70 USDT - let max_diff = human_to_dec("0.7", Decimals::Six) * FPDecimal::from(iterations as u128); - - assert!(are_fpdecimals_approximately_equal( - contract_balance_usdt_after, - contract_balance_usdt_before, - max_diff, - ), "Contract balance changed too much. Starting balance: {}, Current balance: {}. Max diff: {}", - contract_balance_usdt_before.scaled(Decimals::Six.get_decimals().neg()), - contract_balance_usdt_after.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -/* - This test shows that query overestimates the amount of USDT needed to execute the swap. It seems - that in reality we get a better price when selling ETH than the one returned by query and can - execute the swap with less USDT. - - It's easiest to check by commenting out the query_result assert and running the test. It will - pass and amounts will perfectly match our assertions. -*/ -#[ignore] -#[test] -fn it_correctly_calculates_required_funds_when_querying_buy_with_minimum_buffer_and_realistic_values( -) { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("51", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let eth_to_swap = "4.08"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(eth_to_swap, ETH, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let query_result: FPDecimal = wasm - .query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: human_to_dec(eth_to_swap, Decimals::Eighteen), - }, - ) - .unwrap(); - - assert_eq!( - query_result, - human_to_dec("906.195", Decimals::Six), - "incorrect swap result estimate returned by query" - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(906u128), - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, - human_to_dec("906.195", Decimals::Six), - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let atom_amount_below_min_tick_size = FPDecimal::must_from_str("0.0005463"); - let mut dust_value = atom_amount_below_min_tick_size * human_to_dec("8.89", Decimals::Six); - - let fee_refund = dust_value - * FPDecimal::must_from_str(&format!( - "{}", - DEFAULT_TAKER_FEE * DEFAULT_ATOMIC_MULTIPLIER * DEFAULT_SELF_RELAYING_FEE_PART - )); - - dust_value += fee_refund; - - let expected_contract_usdt_balance = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()) + dust_value; - let actual_contract_balance = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - let contract_balance_diff = expected_contract_usdt_balance - actual_contract_balance; - - // here the actual difference is 0.000067 USDT, which we attribute differences between decimal precision of Rust/Go and Google Sheets - assert!( - human_to_dec("0.0001", Decimals::Six) - contract_balance_diff > FPDecimal::ZERO, - "contract balance has changed too much after swap" - ); -} - -/* - This test shows that in some edge cases we calculate required funds differently than the chain does. - When estimating balance hold for atomic market order chain doesn't take into account whether sender is - also fee recipient, while we do. This leads to a situation where we estimate required funds to be - lower than what's expected by the chain, which makes the swap fail. - - In this test we skip query estimation and go straight to executing swap. -*/ -#[ignore] -#[test] -fn it_correctly_calculates_required_funds_when_executing_buy_with_minimum_buffer_and_realistic_values( -) { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - // in reality we need to add at least 49 USDT to the buffer, even if according to contract's calculations 42 USDT would be enough to execute the swap - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("42", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let eth_to_swap = "4.08"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(eth_to_swap, ETH, Decimals::Eighteen), - str_coin("0.01", INJ, Decimals::Eighteen), - ], - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(906u128), - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ) - .unwrap(); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - assert_eq!( - from_balance, - FPDecimal::ZERO, - "some of the original amount wasn't swapped" - ); - assert_eq!( - to_balance, - human_to_dec("906.195", Decimals::Six), - "swapper did not receive expected amount" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let contract_usdt_balance_before = - FPDecimal::must_from_str(contract_balances_before[0].amount.as_str()); - let contract_usdt_balance_after = - FPDecimal::must_from_str(contract_balances_after[0].amount.as_str()); - - assert!( - contract_usdt_balance_after >= contract_usdt_balance_before, - "Contract lost some money after swap. Actual balance: {}, previous balance: {}", - contract_usdt_balance_after, - contract_usdt_balance_before - ); - - // contract can earn max of 0.7 USDT, when exchanging ETH worth ~$8150 - let max_diff = human_to_dec("0.7", Decimals::Six); - - assert!( - are_fpdecimals_approximately_equal( - contract_usdt_balance_after, - contract_usdt_balance_before, - max_diff, - ), - "Contract balance changed too much. Actual balance: {}, previous balance: {}. Max diff: {}", - contract_usdt_balance_after.scaled(Decimals::Six.get_decimals().neg()), - contract_usdt_balance_before.scaled(Decimals::Six.get_decimals().neg()), - max_diff.scaled(Decimals::Six.get_decimals().neg()) - ); -} - -#[test] -fn it_returns_all_funds_if_there_is_not_enough_buffer_realistic_values() { - let app = InjectiveTestApp::new(); - let wasm = Wasm::new(&app); - let exchange = Exchange::new(&app); - let bank = Bank::new(&app); - - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); - let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - - // 41 USDT is just below the amount required to buy required ATOM amount - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("41", USDT, Decimals::Six)], - ); - set_route_and_assert_success( - &wasm, - &owner, - &contr_addr, - ETH, - ATOM, - vec![ - spot_market_1_id.as_str().into(), - spot_market_2_id.as_str().into(), - ], - ); - - let trader1 = init_rich_account(&app); - let trader2 = init_rich_account(&app); - let trader3 = init_rich_account(&app); - - create_realistic_eth_usdt_buy_orders_from_spreadsheet( - &app, - &spot_market_1_id, - &trader1, - &trader2, - ); - create_realistic_atom_usdt_sell_orders_from_spreadsheet( - &app, - &spot_market_2_id, - &trader1, - &trader2, - &trader3, - ); - - app.increase_time(1); - - let eth_to_swap = "4.08"; - - let swapper = must_init_account_with_funds( - &app, - &[ - str_coin(eth_to_swap, ETH, Decimals::Eighteen), - str_coin("1", INJ, Decimals::Eighteen), - ], - ); - - let query_result: RunnerResult = wasm.query( - &contr_addr, - &QueryMsg::GetOutputQuantity { - source_denom: ETH.to_string(), - target_denom: ATOM.to_string(), - from_quantity: human_to_dec(eth_to_swap, Decimals::Eighteen), - }, - ); - - assert!(query_result.is_err(), "query should fail"); - - assert!( - query_result - .unwrap_err() - .to_string() - .contains("Swap amount too high"), - "incorrect error message in query result" - ); - - let contract_balances_before = query_all_bank_balances(&bank, &contr_addr); - assert_eq!( - contract_balances_before.len(), - 1, - "wrong number of denoms in contract balances" - ); - - let execute_result = wasm.execute( - &contr_addr, - &ExecuteMsg::SwapMinOutput { - target_denom: ATOM.to_string(), - min_output_quantity: FPDecimal::from(906u128), - }, - &[str_coin(eth_to_swap, ETH, Decimals::Eighteen)], - &swapper, - ); - - assert!(execute_result.is_err(), "execute should fail"); - - let from_balance = query_bank_balance(&bank, ETH, swapper.address().as_str()); - let to_balance = query_bank_balance(&bank, ATOM, swapper.address().as_str()); - - assert_eq!( - from_balance, - human_to_dec(eth_to_swap, Decimals::Eighteen), - "source balance changed after failed swap" - ); - assert_eq!( - to_balance, - FPDecimal::ZERO, - "target balance changed after failed swap" - ); - - let contract_balances_after = query_all_bank_balances(&bank, contr_addr.as_str()); - assert_eq!( - contract_balances_after.len(), - 1, - "wrong number of denoms in contract balances" - ); - assert_eq!( - contract_balances_before[0].amount, contract_balances_after[0].amount, - "contract balance has changed after failed swap" - ); -} diff --git a/contracts/swap/src/testing/integration_tests/mod.rs b/contracts/swap/src/testing/integration_tests/mod.rs deleted file mode 100644 index 516ec20..0000000 --- a/contracts/swap/src/testing/integration_tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod integration_logic_tests; -mod integration_realistic_tests_exact_quantity; -mod integration_realistic_tests_min_quantity; diff --git a/contracts/swap/src/testing/mod.rs b/contracts/swap/src/testing/mod.rs index ed9f565..bfb3e85 100644 --- a/contracts/swap/src/testing/mod.rs +++ b/contracts/swap/src/testing/mod.rs @@ -1,3 +1,4 @@ +mod authz_tests; mod config_tests; mod integration_logic_tests; mod integration_realistic_tests_exact_quantity; diff --git a/contracts/swap/src/testing/test_utils.rs b/contracts/swap/src/testing/test_utils.rs index ccee9a3..a83e63c 100644 --- a/contracts/swap/src/testing/test_utils.rs +++ b/contracts/swap/src/testing/test_utils.rs @@ -1,28 +1,11 @@ -use std::collections::HashMap; -use std::str::FromStr; +use crate::helpers::Scaled; -use cosmwasm_std::testing::{MockApi, MockStorage}; use cosmwasm_std::{ - coin, to_json_binary, Addr, Coin, ContractResult, OwnedDeps, QuerierResult, SystemError, + coin, + testing::{MockApi, MockStorage}, + to_json_binary, Addr, Coin, ContractResult, OwnedDeps, QuerierResult, SystemError, SystemResult, }; -use injective_std::shim::Any; -use injective_std::types::cosmos::bank::v1beta1::{ - MsgSend, QueryAllBalancesRequest, QueryBalanceRequest, -}; -use injective_std::types::cosmos::base::v1beta1::Coin as TubeCoin; -use injective_std::types::cosmos::gov::v1::MsgVote; -use injective_std::types::cosmos::gov::v1beta1::MsgSubmitProposal; -use injective_std::types::injective::exchange; -use injective_std::types::injective::exchange::v1beta1::{ - MsgCreateSpotLimitOrder, MsgInstantSpotMarketLaunch, OrderInfo, OrderType, - QuerySpotMarketsRequest, SpotMarketParamUpdateProposal, SpotOrder, -}; -use injective_test_tube::{ - Account, Bank, Exchange, Gov, InjectiveTestApp, Module, SigningAccount, Wasm, -}; - -use crate::helpers::Scaled; use injective_cosmwasm::{ create_orderbook_response_handler, create_spot_multi_market_handler, get_default_subaccount_id_for_checked_address, inj_mock_deps, test_market_ids, @@ -31,7 +14,27 @@ use injective_cosmwasm::{ TEST_MARKET_ID_2, }; use injective_math::FPDecimal; +use injective_std::{ + shim::{Any, Timestamp}, + types::{ + cosmos::{ + authz::v1beta1::{GenericAuthorization, Grant, MsgGrant}, + bank::v1beta1::{MsgSend, QueryAllBalancesRequest, QueryBalanceRequest}, + base::v1beta1::Coin as TubeCoin, + gov::v1::MsgVote, + gov::v1beta1::MsgSubmitProposal, + }, + injective::exchange::v1beta1::{ + MsgCreateSpotLimitOrder, MsgInstantSpotMarketLaunch, OrderInfo, OrderType, + QuerySpotMarketsRequest, SpotMarketParamUpdateProposal, SpotOrder, + }, + }, +}; +use injective_test_tube::{ + Account, Authz, Bank, Exchange, Gov, InjectiveTestApp, Module, SigningAccount, Wasm, +}; use prost::Message; +use std::{collections::HashMap, str::FromStr}; use crate::msg::{ExecuteMsg, FeeRecipient, InstantiateMsg}; use crate::types::FPCoin; @@ -939,6 +942,36 @@ pub fn pause_spot_market( app.increase_time(10u64) } +pub fn create_generic_authorization( + app: &InjectiveTestApp, + granter: &SigningAccount, + grantee: String, + msg: String, + expiration: Option, +) { + let authz = Authz::new(app); + + let mut buf = vec![]; + GenericAuthorization::encode(&GenericAuthorization { msg }, &mut buf).unwrap(); + + authz + .grant( + MsgGrant { + granter: granter.address(), + grantee, + grant: Some(Grant { + authorization: Some(Any { + type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), + value: buf.clone(), + }), + expiration, + }), + }, + granter, + ) + .unwrap(); +} + pub fn pass_spot_market_params_update_proposal( gov: &Gov, proposal: &SpotMarketParamUpdateProposal, @@ -946,7 +979,7 @@ pub fn pass_spot_market_params_update_proposal( validator: &SigningAccount, ) { let mut buf = vec![]; - exchange::v1beta1::SpotMarketParamUpdateProposal::encode(proposal, &mut buf).unwrap(); + SpotMarketParamUpdateProposal::encode(proposal, &mut buf).unwrap(); println!("submitting proposal: {proposal:?}"); let submit_response = gov.submit_proposal_v1beta1(