From fd55e7fce43d54108bc15829678ad03ed961b14f Mon Sep 17 00:00:00 2001 From: maxrobot Date: Tue, 19 Mar 2024 16:52:54 +0000 Subject: [PATCH 1/9] bump --- Cargo.lock | 15 ++++++++++++--- Changelog.md | 16 +++++++--------- contracts/swap/Cargo.toml | 4 ++-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ccb2d1..af5fa65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,7 +404,7 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.1", "sha2 0.10.6", "static_assertions 1.1.0", "thiserror", @@ -2166,6 +2166,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.9" @@ -2366,7 +2375,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "swap-contract" -version = "1.0.1" +version = "1.0.2" dependencies = [ "cosmos-sdk-proto", "cosmwasm-schema", @@ -2386,7 +2395,7 @@ dependencies = [ "protobuf", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 1.0.1", "thiserror", ] diff --git a/Changelog.md b/Changelog.md index 431d467..b2fc3c4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,7 +16,13 @@ All notable changes to this project will be documented in this file. - -## [Version v1.1.0] - 2024-02-24 +## [1.0.2] - 2024-03-19 + +### Fixed + +- Bump version of `serde-json-wasm` + +## [1.0.1] - 2024-02-24 ### Added @@ -24,10 +30,6 @@ All notable changes to this project will be documented in this file. - `rust-toolchain` file - Contract migration templates and migrate function -### Changed - -- - ### Fixed - Correctly round to `min_quantity_tick_size` in intermediate steps for multi-hop buys @@ -41,7 +43,3 @@ All notable changes to this project will be documented in this file. ### Changed - Updated to latest CosmWasm version - -### Fixed - -- diff --git a/contracts/swap/Cargo.toml b/contracts/swap/Cargo.toml index aa57bc1..f9f0608 100644 --- a/contracts/swap/Cargo.toml +++ b/contracts/swap/Cargo.toml @@ -2,7 +2,7 @@ authors = [ "Markus Waas " ] edition = "2021" name = "swap-contract" -version = "1.0.1" +version = "1.0.2" exclude = [ # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. @@ -41,7 +41,7 @@ num-traits = "0.2.15" protobuf = { version = "2", features = [ "with-bytes" ] } schemars = "0.8.8" serde = { version = "1.0.137", default-features = false, features = [ "derive" ] } -serde-json-wasm = "0.5.1" +serde-json-wasm = "1.0.1" thiserror = { version = "1.0.31" } [dev-dependencies] From a088c23b22236b0ee8bb4f238539f77cc384eb90 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Tue, 2 Apr 2024 18:38:23 +0100 Subject: [PATCH 2/9] fix --- contracts/swap/msg.rs | 70 + .../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 + 5 files changed, 5407 insertions(+) create mode 100644 contracts/swap/msg.rs create mode 100644 contracts/swap/src/testing/integration_tests/integration_logic_tests.rs create mode 100644 contracts/swap/src/testing/integration_tests/integration_realistic_tests_exact_quantity.rs create mode 100644 contracts/swap/src/testing/integration_tests/integration_realistic_tests_min_quantity.rs create mode 100644 contracts/swap/src/testing/integration_tests/mod.rs diff --git a/contracts/swap/msg.rs b/contracts/swap/msg.rs new file mode 100644 index 0000000..f0c841e --- /dev/null +++ b/contracts/swap/msg.rs @@ -0,0 +1,70 @@ +use cosmwasm_std::{Addr, Coin}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use injective_cosmwasm::MarketId; +use injective_math::FPDecimal; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum FeeRecipient { + Address(Addr), + SwapContract, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct InstantiateMsg { + pub fee_recipient: FeeRecipient, + pub admin: Addr, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + SwapMinOutput { + target_denom: String, + min_output_quantity: FPDecimal, + }, + SwapExactOutput { + target_denom: String, + target_output_quantity: FPDecimal, + }, + SetRoute { + source_denom: String, + target_denom: String, + route: Vec, + }, + DeleteRoute { + source_denom: String, + target_denom: String, + }, + UpdateConfig { + admin: Option, + fee_recipient: Option, + }, + WithdrawSupportFunds { + coins: Vec, + target_address: Addr, + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + GetRoute { + source_denom: String, + target_denom: String, + }, + GetOutputQuantity { + from_quantity: FPDecimal, + source_denom: String, + target_denom: String, + }, + GetInputQuantity { + to_quantity: FPDecimal, + source_denom: String, + target_denom: String, + }, + GetAllRoutes {}, + GetConfig {}, +} diff --git a/contracts/swap/src/testing/integration_tests/integration_logic_tests.rs b/contracts/swap/src/testing/integration_tests/integration_logic_tests.rs new file mode 100644 index 0000000..d009000 --- /dev/null +++ b/contracts/swap/src/testing/integration_tests/integration_logic_tests.rs @@ -0,0 +1,2233 @@ +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 new file mode 100644 index 0000000..53e129f --- /dev/null +++ b/contracts/swap/src/testing/integration_tests/integration_realistic_tests_exact_quantity.rs @@ -0,0 +1,1685 @@ +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 new file mode 100644 index 0000000..ed4d64a --- /dev/null +++ b/contracts/swap/src/testing/integration_tests/integration_realistic_tests_min_quantity.rs @@ -0,0 +1,1416 @@ +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 new file mode 100644 index 0000000..516ec20 --- /dev/null +++ b/contracts/swap/src/testing/integration_tests/mod.rs @@ -0,0 +1,3 @@ +mod integration_logic_tests; +mod integration_realistic_tests_exact_quantity; +mod integration_realistic_tests_min_quantity; From 2dc7b89798188bb3b71defdef8fd78638a004d52 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Wed, 3 Apr 2024 13:12:18 +0100 Subject: [PATCH 3/9] 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( From 89babb746f43025edc946d6ac4588217b55b207f Mon Sep 17 00:00:00 2001 From: maxrobot Date: Wed, 3 Apr 2024 14:33:12 +0100 Subject: [PATCH 4/9] added compilation --- .github/workflows/rust.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index e9a8a28..8d93b9b 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -50,13 +50,13 @@ jobs: key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: ${{ runner.os }}-cargo- - - name: Check if cw-optimize is installed - run: | - cargo cw-optimizoor --version || cargo install cw-optimizoor - - - name: Compile contracts - run: | - cargo cw-optimizoor + - name: Compile WASM contracts + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target wasm32-unknown-unknown --locked --package swap-contract + env: + RUSTFLAGS: "-C link-arg=-s" - name: Run tests uses: actions-rs/cargo@v1 From d8b0707d59f7e94da5ab766deba02e75f762ff93 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Wed, 3 Apr 2024 14:59:48 +0100 Subject: [PATCH 5/9] made the file search more generic --- contracts/swap/src/testing/test_utils.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/contracts/swap/src/testing/test_utils.rs b/contracts/swap/src/testing/test_utils.rs index a83e63c..d3aece1 100644 --- a/contracts/swap/src/testing/test_utils.rs +++ b/contracts/swap/src/testing/test_utils.rs @@ -310,11 +310,22 @@ fn create_mock_spot_market( } pub fn wasm_file(contract_name: String) -> String { + let snaked_name = contract_name.replace('-', "_"); let arch = std::env::consts::ARCH; + + let target = format!("../../target/wasm32-unknown-unknown/release/{snaked_name}.wasm"); + let artifacts_dir = std::env::var("ARTIFACTS_DIR_PATH").unwrap_or_else(|_| "artifacts".to_string()); - let snaked_name = contract_name.replace('-', "_"); - format!("../../{artifacts_dir}/{snaked_name}-{arch}.wasm") + let arch_target = format!("../../{artifacts_dir}/{snaked_name}-{arch}.wasm"); + + if std::path::Path::new(&target).exists() { + target + } else if std::path::Path::new(&arch_target).exists() { + arch_target + } else { + format!("../../{artifacts_dir}/{snaked_name}.wasm") + } } pub fn store_code( From c69be10199fcd74565d1cfb8a8658b27d389fd82 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Wed, 1 May 2024 18:37:50 +0100 Subject: [PATCH 6/9] did the impossible --- Cargo.lock | 1648 +++++++++++++-------- contracts/swap/Cargo.toml | 23 +- contracts/swap/src/testing/authz_tests.rs | 53 +- contracts/swap/src/testing/test_utils.rs | 49 +- 4 files changed, 1126 insertions(+), 647 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af5fa65..da10351 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -15,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -39,19 +54,41 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -67,9 +104,69 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "base16ct" @@ -83,12 +180,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -113,7 +204,7 @@ version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "clap", @@ -132,18 +223,16 @@ dependencies = [ [[package]] name = "bip32" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e40748d60a3296653e45e87e64c6989aebfad607bccce59cc4156c5d81b2f70" +checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ "bs58", "hmac", "k256 0.13.1", - "once_cell", - "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.6", + "sha2 0.10.8", "subtle", "zeroize", ] @@ -154,6 +243,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "block-buffer" version = "0.9.0" @@ -174,45 +269,45 @@ dependencies = [ [[package]] name = "bnum" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" +checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] name = "bs58" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.9.9", + "sha2 0.10.8", ] [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" [[package]] name = "cexpr" @@ -231,23 +326,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.0", + "windows-targets 0.52.5", ] [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -261,9 +356,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_lex", - "indexmap", + "indexmap 1.9.3", "strsim", "termcolor", "textwrap", @@ -284,20 +379,20 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -305,36 +400,48 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cosmos-sdk-proto" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" +checksum = "32560304ab4c365791fd307282f76637213d8083c1a98490c35159cd67852237" dependencies = [ - "prost 0.11.9", - "prost-types", - "tendermint-proto", + "prost 0.12.4", + "prost-types 0.12.4", + "tendermint-proto 0.34.1", +] + +[[package]] +name = "cosmos-sdk-proto" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e23f6ab56d5f031cde05b8b82a5fefd3a1a223595c79e32317a97189e612bc" +dependencies = [ + "prost 0.12.4", + "prost-types 0.12.4", + "tendermint-proto 0.35.0", + "tonic", ] [[package]] name = "cosmrs" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" +checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea" dependencies = [ "bip32", - "cosmos-sdk-proto", - "ecdsa 0.16.7", + "cosmos-sdk-proto 0.20.0", + "ecdsa 0.16.9", "eyre", - "getrandom", "k256 0.13.1", "rand_core 0.6.4", "serde", "serde_json", + "signature 2.2.0", "subtle-encoding", "tendermint", "tendermint-rpc", @@ -343,12 +450,12 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad24bc9dae9aac5dc124b4560e3f7678729d701f1bf3cb11140703d91f247d31" +checksum = "e6b4c3f9c4616d6413d4b5fc4c270a4cc32a374b9be08671e80e1a019f805d8f" dependencies = [ "digest 0.10.7", - "ecdsa 0.16.7", + "ecdsa 0.16.9", "ed25519-zebra", "k256 0.13.1", "rand_core 0.6.4", @@ -357,18 +464,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca65635b768406eabdd28ba015cc3f2f863ca5a2677a7dc4c237b8ee1298efb3" +checksum = "c586ced10c3b00e809ee664a895025a024f60d65d34fe4c09daed4a4db68a3f3" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e2a6ce6dbcad572495fd9d9c1072793fe682aebfcc09752c3b0de3fa1814d7" +checksum = "8467874827d384c131955ff6f4d47d02e72a956a08eb3c0ff24f8c903a5517b4" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -379,9 +486,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904408dc6d73fd1d535c764a55370803cccf6b9be5af7423c4db8967058673f0" +checksum = "f6db85d98ac80922aef465e564d5b21fa9cfac5058cb62df7f116c3682337393" dependencies = [ "proc-macro2", "quote", @@ -390,11 +497,11 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.5.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed4564772e5d779235f2d7353e3d66e37793065c3a5155a2978256bf4c5b7d5" +checksum = "712fe58f39d55c812f7b2c84e097cdede3a39d520f89b6dc3153837e31741927" dependencies = [ - "base64 0.21.7", + "base64", "bech32", "bnum", "cosmwasm-crypto", @@ -404,17 +511,17 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm 0.5.1", - "sha2 0.10.6", + "serde-json-wasm 0.5.2", + "sha2 0.10.8", "static_assertions 1.1.0", "thiserror", ] [[package]] name = "cosmwasm-storage" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2b4ae72a03e8f56c85df59d172d51d2d7dc9cec6e2bc811e3fb60c588032a4" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" dependencies = [ "cosmwasm-std", "serde", @@ -422,9 +529,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -449,9 +556,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -469,15 +576,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct", -] - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -506,16 +604,16 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.16.4" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a18afd2e201221c6d72a57f0886ef2a22151bbc9e6db7af276fde8a91081042" +checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" dependencies = [ "anyhow", "cosmwasm-std", "cw-storage-plus 1.2.0", - "cw-utils 1.0.1", + "cw-utils 1.0.3", "derivative", - "itertools", + "itertools 0.10.5", "k256 0.11.6", "prost 0.9.0", "schemars", @@ -561,13 +659,13 @@ dependencies = [ [[package]] name = "cw-utils" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" +checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw2 1.0.1", + "cw2 1.1.2", "schemars", "semver", "serde", @@ -588,15 +686,17 @@ dependencies = [ [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus 1.2.0", "schemars", + "semver", "serde", + "thiserror", ] [[package]] @@ -611,14 +711,23 @@ dependencies = [ [[package]] name = "der" -version = "0.7.6" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -653,9 +762,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" @@ -671,26 +780,26 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.6", + "der 0.7.9", "digest 0.10.7", - "elliptic-curve 0.13.5", + "elliptic-curve 0.13.8", "rfc6979 0.4.0", - "signature 2.1.0", - "spki 0.7.2", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] name = "ed25519" -version = "2.2.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8 0.10.2", - "signature 2.1.0", + "signature 2.2.0", ] [[package]] @@ -713,7 +822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "serde", @@ -723,9 +832,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elliptic-curve" @@ -749,23 +858,32 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct 0.2.0", - "crypto-bigint 0.5.2", + "crypto-bigint 0.5.5", "digest 0.10.7", "ff 0.13.0", "generic-array", "group 0.13.0", "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1 0.7.2", + "sec1 0.7.3", "subtle", "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -779,6 +897,22 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "ethbloom" version = "0.6.4" @@ -817,9 +951,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -853,7 +987,7 @@ checksum = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" dependencies = [ "byteorder", "heapsize", - "rand", + "rand 0.5.6", "rustc-hex", "static_assertions 0.2.5", ] @@ -885,9 +1019,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -906,13 +1040,12 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", - "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -921,9 +1054,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -931,66 +1064,39 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ - "futures-channel", "futures-core", - "futures-io", - "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", - "slab", ] [[package]] @@ -1006,9 +1112,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -1017,6 +1123,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "glob" version = "0.3.1" @@ -1047,9 +1159,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1057,7 +1169,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1074,29 +1186,10 @@ dependencies = [ ] [[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heapsize" @@ -1118,12 +1211,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1143,11 +1233,20 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1156,9 +1255,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1173,9 +1272,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -1185,9 +1284,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1208,54 +1307,43 @@ dependencies = [ ] [[package]] -name = "hyper-proxy" -version = "0.9.1" +name = "hyper-rustls" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ - "bytes", - "futures", - "headers", + "futures-util", "http", "hyper", - "hyper-rustls", - "rustls-native-certs", + "rustls", "tokio", "tokio-rustls", - "tower-service", - "webpki", ] [[package]] -name = "hyper-rustls" -version = "0.22.1" +name = "hyper-timeout" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "ct-logs", - "futures-util", "hyper", - "log", - "rustls", - "rustls-native-certs", + "pin-project-lite", "tokio", - "tokio-rustls", - "webpki", - "webpki-roots", + "tokio-io-timeout", ] [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1269,9 +1357,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1308,14 +1396,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", ] [[package]] name = "injective-cosmwasm" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b6f08b14a23696948d51ba6a050382cbc6e23522efec3ca607a5b1a317de0c" +checksum = "f85e8f06d04d0e20d59d673ae242c5cffb6a13f3b5189fd2e95ea153f3066f52" dependencies = [ "cosmwasm-std", "cw-storage-plus 1.2.0", @@ -1361,15 +1459,14 @@ dependencies = [ [[package]] name = "injective-std" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7a5b52d19dca05823c7e4b481d41b49c04a0e56f66a5c92396a6fdd3314710" +version = "0.1.7" +source = "git+https://github.com/InjectiveLabs/cw-injective.git?rev=8957ee2#8957ee25cd885d3246c48a487da757548bc60f1c" dependencies = [ "chrono", "cosmwasm-std", "osmosis-std-derive", - "prost 0.11.9", - "prost-types", + "prost 0.12.4", + "prost-types 0.12.4", "schemars", "serde", "serde-cw-value", @@ -1377,24 +1474,29 @@ dependencies = [ [[package]] name = "injective-test-tube" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cb772fd4c8c1da872b742633e73928cbf1cf0505a09ec8ad41934a4d7f10b4" +version = "1.2.0" +source = "git+https://github.com/InjectiveLabs/test-tube.git?rev=402efd7#402efd7a868ff44af08bd08abdd39d439c36a494" dependencies = [ - "base64 0.13.1", + "base64", "bindgen", "cosmrs", "cosmwasm-std", "hex", "injective-cosmwasm", "injective-std", - "prost 0.11.9", + "prost 0.12.4", "serde", "serde_json", "test-tube-inj", "thiserror", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.10.5" @@ -1404,17 +1506,26 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1428,7 +1539,7 @@ dependencies = [ "cfg-if", "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -1438,11 +1549,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.7", - "elliptic-curve 0.13.5", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", - "sha2 0.10.6", - "signature 2.1.0", + "sha2 0.10.8", + "signature 2.2.0", ] [[package]] @@ -1459,34 +1570,43 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "winapi", + "windows-targets 0.52.5", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" @@ -1500,16 +1620,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1524,9 +1652,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint", "num-complex", @@ -1538,9 +1666,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -1549,13 +1677,19 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" @@ -1567,21 +1701,31 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -1602,34 +1746,43 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.9", "libc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" @@ -1639,37 +1792,28 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "osmosis-std-derive" -version = "0.15.3" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4d482a16be198ee04e0f94e10dd9b8d02332dcf33bc5ea4b255e7e25eedc5df" +checksum = "c5ebdfd1bc8ed04db596e110c6baa9b174b04f6ed1ec22c666ddc5cb3fa91bd7" dependencies = [ - "itertools", + "itertools 0.10.5", "proc-macro2", + "prost-types 0.11.9", "quote", "syn 1.0.109", ] [[package]] name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "pbkdf2" -version = "0.12.1" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" -dependencies = [ - "digest 0.10.7", - "hmac", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "peeking_take_while" @@ -1679,9 +1823,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "peg" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" +checksum = "8a625d12ad770914cbf7eff6f9314c3ef803bfe364a1b20bc36ddf56673e71e5" dependencies = [ "peg-macros", "peg-runtime", @@ -1689,9 +1833,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" +checksum = "f241d42067ed3ab6a4fece1db720838e1418f36d868585a27931f95d6bc03582" dependencies = [ "peg-runtime", "proc-macro2", @@ -1700,41 +1844,41 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1758,10 +1902,22 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.6", - "spki 0.7.2", + "der 0.7.9", + "spki 0.7.3", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "primitive-types" version = "0.12.2" @@ -1774,9 +1930,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -1801,6 +1957,16 @@ dependencies = [ "prost-derive 0.11.9", ] +[[package]] +name = "prost" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +dependencies = [ + "bytes", + "prost-derive 0.12.4", +] + [[package]] name = "prost-derive" version = "0.9.0" @@ -1808,7 +1974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", @@ -1821,12 +1987,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "prost-types" version = "0.11.9" @@ -1836,6 +2015,15 @@ dependencies = [ "prost 0.11.9", ] +[[package]] +name = "prost-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +dependencies = [ + "prost 0.12.4", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -1866,9 +2054,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1886,6 +2074,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -1918,9 +2127,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1929,9 +2150,50 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] [[package]] name = "rfc6979" @@ -1956,17 +2218,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1987,6 +2249,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1999,36 +2267,73 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" -version = "0.19.1" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ - "base64 0.13.1", "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] name = "rustls-native-certs" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls", + "rustls-pemfile", "schannel", "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -2041,18 +2346,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.52.0", ] [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" dependencies = [ "dyn-clone", "schemars_derive", @@ -2062,21 +2367,21 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] name = "sct" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -2098,12 +2403,12 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.6", + "der 0.7.9", "generic-array", "pkcs8 0.10.2", "subtle", @@ -2112,11 +2417,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -2125,9 +2430,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -2135,15 +2440,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] @@ -2159,9 +2464,9 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" dependencies = [ "serde", ] @@ -2177,40 +2482,40 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -2219,24 +2524,25 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] -name = "sha1" -version = "0.10.5" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] @@ -2254,9 +2560,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -2265,9 +2571,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" @@ -2281,9 +2587,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -2291,28 +2597,28 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" @@ -2326,12 +2632,12 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.6", + "der 0.7.9", ] [[package]] @@ -2377,7 +2683,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" name = "swap-contract" version = "1.0.2" dependencies = [ - "cosmos-sdk-proto", + "cosmos-sdk-proto 0.21.1", "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", @@ -2412,20 +2718,47 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tendermint" -version = "0.32.0" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a46ec6b25b028097ab682ffae11d09d64fe1e2535833b902f26a278a0f88a705" +checksum = "15ab8f0a25d0d2ad49ac615da054d6a76aa6603ff95f7d18bafdd34450a1a04b" dependencies = [ "bytes", "digest 0.10.7", @@ -2436,27 +2769,27 @@ dependencies = [ "k256 0.13.1", "num-traits", "once_cell", - "prost 0.11.9", - "prost-types", + "prost 0.12.4", + "prost-types 0.12.4", "ripemd", "serde", "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.6", - "signature 2.1.0", + "sha2 0.10.8", + "signature 2.2.0", "subtle", "subtle-encoding", - "tendermint-proto", + "tendermint-proto 0.34.1", "time", "zeroize", ] [[package]] name = "tendermint-config" -version = "0.32.0" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dbb7610ef8422d5886116868e48ab6a9ea72859b3bf9021c6d87318e5600225" +checksum = "e1a02da769166e2052cd537b1a97c78017632c2d9e19266367b27e73910434fc" dependencies = [ "flex-error", "serde", @@ -2468,16 +2801,52 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.32.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23c8ff0e6634eb4c3c4aeed45076dc97dac91aac5501a905a67fa222e165b" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" dependencies = [ "bytes", "flex-error", - "num-derive", + "num-derive 0.3.3", "num-traits", "prost 0.11.9", - "prost-types", + "prost-types 0.11.9", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-proto" +version = "0.34.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b797dd3d2beaaee91d2f065e7bdf239dc8d80bba4a183a288bc1279dd5a69a1e" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.3.3", + "num-traits", + "prost 0.12.4", + "prost-types 0.12.4", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "tendermint-proto" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff525d5540a9fc535c38dc0d92a98da3ee36fcdfbda99cecb9f3cce5cd4d41d7" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.4.2", + "num-traits", + "prost 0.12.4", + "prost-types 0.12.4", "serde", "serde_bytes", "subtle-encoding", @@ -2486,21 +2855,19 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.32.0" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2cc789170db5a35d4e0bb2490035c03ef96df08f119bee25fd8dab5a09aa25" +checksum = "71afae8bb5f6b14ed48d4e1316a643b6c2c3cbad114f510be77b4ed20b7b3e42" dependencies = [ "async-trait", "bytes", "flex-error", "futures", "getrandom", - "http", - "hyper", - "hyper-proxy", - "hyper-rustls", "peg", "pin-project", + "rand 0.8.5", + "reqwest", "semver", "serde", "serde_bytes", @@ -2509,7 +2876,7 @@ dependencies = [ "subtle-encoding", "tendermint", "tendermint-config", - "tendermint-proto", + "tendermint-proto 0.34.1", "thiserror", "time", "tokio", @@ -2521,61 +2888,64 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "test-tube-inj" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae2aa4f0386a041eea657c0d0b4438f1d757a231d0736c1b3413b9c84cfa36db" +checksum = "751b40929081e03c825df424a5a81645f2cea444654d96f0aad962f7be4e541e" dependencies = [ - "base64 0.13.1", + "base64", "cosmrs", "cosmwasm-std", - "prost 0.11.9", + "prost 0.12.4", "serde", "serde_json", - "tendermint-proto", + "tendermint-proto 0.32.2", "thiserror", ] [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "time" -version = "0.3.21" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -2583,16 +2953,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -2622,11 +2993,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -2637,33 +3008,53 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "tokio-rustls" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", - "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -2682,6 +3073,59 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.12.4", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -2690,35 +3134,46 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "uint" @@ -2746,36 +3201,36 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2784,9 +3239,9 @@ dependencies = [ [[package]] name = "uuid" -version = "0.8.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "version_check" @@ -2796,9 +3251,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -2806,11 +3261,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2822,9 +3276,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2832,24 +3286,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2857,61 +3323,43 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] - [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -2932,11 +3380,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2946,166 +3394,168 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.5", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.52.5", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -3118,5 +3568,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] diff --git a/contracts/swap/Cargo.toml b/contracts/swap/Cargo.toml index f9f0608..0587650 100644 --- a/contracts/swap/Cargo.toml +++ b/contracts/swap/Cargo.toml @@ -21,15 +21,8 @@ backtraces = [ "cosmwasm-std/backtraces" ] # use library feature to disable all instantiate/execute/query exports library = [ ] -[package.metadata.scripts] -optimize = """docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ - --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer-arm64:0.12.11 -""" - [dependencies] -cosmwasm-std = { version = "1.5.0", features = [ "abort", "cosmwasm_1_2", "cosmwasm_1_3", "cosmwasm_1_4", "iterator", "stargate" ] } +cosmwasm-std = { version = "1.5.4", features = [ "abort", "cosmwasm_1_2", "cosmwasm_1_3", "cosmwasm_1_4", "iterator", "stargate" ] } cosmwasm-storage = "1.5.0" cw-storage-plus = "0.14.0" cw-utils = "0.14.0" @@ -45,9 +38,11 @@ serde-json-wasm = "1.0.1" thiserror = { version = "1.0.31" } [dev-dependencies] -cosmos-sdk-proto = { version = "0.19.0", default-features = false } -cosmwasm-schema = "1.5.0" -cw-multi-test = "0.16.2" -injective-std = { version = "0.1.5" } -injective-test-tube = "1.1.7" -prost = "0.11.9" +cosmos-sdk-proto = { version = "0.21.1", features = [ "cosmwasm" ] } +cosmwasm-schema = "1.5.0" +cw-multi-test = "0.16.2" +injective-std = { git = "https://github.com/InjectiveLabs/cw-injective.git", rev = "8957ee2" } +# injective-std = { version = "0.1.5" } +injective-test-tube = { git = "https://github.com/InjectiveLabs/test-tube.git", rev = "402efd7" } +# injective-test-tube = "1.1.7" +prost = "0.11.9" diff --git a/contracts/swap/src/testing/authz_tests.rs b/contracts/swap/src/testing/authz_tests.rs index 8fa5cba..6c7b8c4 100644 --- a/contracts/swap/src/testing/authz_tests.rs +++ b/contracts/swap/src/testing/authz_tests.rs @@ -1,28 +1,23 @@ use crate::{ - msg::{ExecuteMsg, QueryMsg}, + msg::ExecuteMsg, 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, + create_contract_authorization, create_realistic_atom_usdt_sell_orders_from_spreadsheet, + create_realistic_eth_usdt_buy_orders_from_spreadsheet, 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, -}; +use injective_std::{shim::Any, types::cosmos::authz::v1beta1::MsgExec}; +use injective_test_tube::{Account, Authz, Exchange, InjectiveTestApp, Module, Wasm}; #[test] pub fn set_route_for_third_party_test() { let app = InjectiveTestApp::new(); let wasm = Wasm::new(&app); + let authz = Authz::new(&app); let exchange = Exchange::new(&app); let owner = must_init_account_with_funds( @@ -48,11 +43,13 @@ pub fn set_route_for_third_party_test() { let trader2 = init_rich_account(&app); let trader3 = init_rich_account(&app); - create_generic_authorization( + create_contract_authorization( &app, + contr_addr.clone(), &owner, trader1.address().to_string(), - "/cosmwasm.wasm.v1.MsgExecuteContract".to_string(), + "set_route".to_string(), + 1, None, ); @@ -72,8 +69,6 @@ pub fn set_route_for_third_party_test() { 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(), @@ -99,18 +94,20 @@ pub fn set_route_for_third_party_test() { }], }; - let _res: ExecuteResponse = app - .execute(msg, "/cosmos.authz.v1beta1.MsgExec", &trader1) - .unwrap(); + let _res = authz.exec(msg, &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(); + // 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 err = authz.exec(msg, &trader1).unwrap_err(); + assert!( + err.to_string().contains("failed to update grant with key"), + "incorrect error returned by execute" + ); } diff --git a/contracts/swap/src/testing/test_utils.rs b/contracts/swap/src/testing/test_utils.rs index d3aece1..1646c80 100644 --- a/contracts/swap/src/testing/test_utils.rs +++ b/contracts/swap/src/testing/test_utils.rs @@ -1,5 +1,6 @@ use crate::helpers::Scaled; +use cosmos_sdk_proto::prost::Message; use cosmwasm_std::{ coin, testing::{MockApi, MockStorage}, @@ -18,12 +19,15 @@ use injective_std::{ shim::{Any, Timestamp}, types::{ cosmos::{ - authz::v1beta1::{GenericAuthorization, Grant, MsgGrant}, + authz::v1beta1::{Grant, MsgGrant}, bank::v1beta1::{MsgSend, QueryAllBalancesRequest, QueryBalanceRequest}, base::v1beta1::Coin as TubeCoin, gov::v1::MsgVote, gov::v1beta1::MsgSubmitProposal, }, + cosmwasm::wasm::v1::{ + AcceptedMessageKeysFilter, ContractExecutionAuthorization, ContractGrant, MaxCallsLimit, + }, injective::exchange::v1beta1::{ MsgCreateSpotLimitOrder, MsgInstantSpotMarketLaunch, OrderInfo, OrderType, QuerySpotMarketsRequest, SpotMarketParamUpdateProposal, SpotOrder, @@ -33,7 +37,6 @@ use injective_std::{ 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}; @@ -750,6 +753,7 @@ pub fn create_limit_order( fee_recipient: trader.address(), price: format!("{price}000000000000000000"), quantity: format!("{quantity}000000000000000000"), + cid: "".to_string(), }), order_type: if order_side == OrderSide::Buy { OrderType::BuyAtomic.into() @@ -812,6 +816,7 @@ pub fn create_realistic_limit_order( fee_recipient: trader.address(), price: price_to_send, quantity: quantity_to_send, + cid: "".to_string(), }), order_type: if order_side == OrderSide::Buy { OrderType::BuyAtomic.into() @@ -953,17 +958,49 @@ pub fn pause_spot_market( app.increase_time(10u64) } -pub fn create_generic_authorization( +pub fn create_contract_authorization( app: &InjectiveTestApp, + contract: String, granter: &SigningAccount, grantee: String, - msg: String, + message: String, + limit: u64, expiration: Option, ) { let authz = Authz::new(app); + let mut filter_buf = vec![]; + AcceptedMessageKeysFilter::encode( + &AcceptedMessageKeysFilter { + keys: vec![message], + }, + &mut filter_buf, + ) + .unwrap(); + + let mut limit_buf = vec![]; + MaxCallsLimit::encode(&MaxCallsLimit { remaining: limit }, &mut limit_buf).unwrap(); + + let contract_grant = ContractGrant { + contract, + limit: Some(Any { + type_url: MaxCallsLimit::TYPE_URL.to_string(), + value: limit_buf, + }), + filter: Some(Any { + type_url: AcceptedMessageKeysFilter::TYPE_URL.to_string(), + value: filter_buf, + }), + }; + let mut buf = vec![]; - GenericAuthorization::encode(&GenericAuthorization { msg }, &mut buf).unwrap(); + ContractExecutionAuthorization::encode( + &ContractExecutionAuthorization { + grants: vec![contract_grant], + }, + &mut buf, + ) + .unwrap(); authz .grant( @@ -972,7 +1009,7 @@ pub fn create_generic_authorization( grantee, grant: Some(Grant { authorization: Some(Any { - type_url: "/cosmos.authz.v1beta1.GenericAuthorization".to_string(), + type_url: ContractExecutionAuthorization::TYPE_URL.to_string(), value: buf.clone(), }), expiration, From 22e027f88f11415347e84b5638f1885ef2b33d94 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Wed, 1 May 2024 18:39:49 +0100 Subject: [PATCH 7/9] migration --- contracts/swap/src/contract.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/swap/src/contract.rs b/contracts/swap/src/contract.rs index 3702ecb..e4c184f 100644 --- a/contracts/swap/src/contract.rs +++ b/contracts/swap/src/contract.rs @@ -129,10 +129,12 @@ pub fn migrate( }, "crates.io:swap-contract" => match contract_version.version.as_ref() { "1.0.1" => { - unimplemented!( - "Migration from version {} is no yet supported", - contract_version.version - ); + // No further updates needed + set_contract_version( + deps.storage, + format!("crates.io:{CONTRACT_NAME}"), + CONTRACT_VERSION, + )?; } _ => return Err(ContractError::MigrationError {}), }, From 637af0e34bce0d98b4260c86651a20d862a6a4d6 Mon Sep 17 00:00:00 2001 From: maxrobot Date: Fri, 3 May 2024 10:12:27 +0100 Subject: [PATCH 8/9] bumped injective test tube --- Cargo.lock | 12 +++++++----- README.md | 10 +++++++++- contracts/swap/Cargo.toml | 14 ++++++-------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da10351..8af067b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,7 +1460,8 @@ dependencies = [ [[package]] name = "injective-std" version = "0.1.7" -source = "git+https://github.com/InjectiveLabs/cw-injective.git?rev=8957ee2#8957ee25cd885d3246c48a487da757548bc60f1c" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d7b7114796e44b034355983cb1ee2a1c9d1843df17c9552126e4404d463c06" dependencies = [ "chrono", "cosmwasm-std", @@ -1475,7 +1476,8 @@ dependencies = [ [[package]] name = "injective-test-tube" version = "1.2.0" -source = "git+https://github.com/InjectiveLabs/test-tube.git?rev=402efd7#402efd7a868ff44af08bd08abdd39d439c36a494" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7b985231e977f4202a8ef7112d34558473a1bdfb4bff8dcb8ab091909728f9" dependencies = [ "base64", "bindgen", @@ -2697,7 +2699,7 @@ dependencies = [ "injective-std", "injective-test-tube", "num-traits", - "prost 0.11.9", + "prost 0.12.4", "protobuf", "schemars", "serde", @@ -2897,9 +2899,9 @@ dependencies = [ [[package]] name = "test-tube-inj" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751b40929081e03c825df424a5a81645f2cea444654d96f0aad962f7be4e541e" +checksum = "cd5deed958ebd3e60b9d55bb7773442993deac73e15f0354eb5bdab05df14fcb" dependencies = [ "base64", "cosmrs", diff --git a/README.md b/README.md index 3df72a9..60dccc6 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,17 @@ Handles various queries to the contract: pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult ``` +## Authz Permission + +Example how to allow the contract admin to authorize other addresses to make contract calls on their behalf. + +```bash + injectived tx grant contract execution --allow-raw-msgs set_routes --max-calls 5 --max-funds 100000uwasm --expiration 1667979596 --from=sgt-account --gas=auto --gas-prices 500000000inj --gas-adjustment 1.3 --output=json --node=https://testnet.sentry.tm.injective.network:443 --chain-id='injective-888' +``` + ## Disclaimer -This contract is designed for educational purposes only. Your use of this contract constitutes your agreement to the terms of the License below. In addition, your use of this contract constitutes your agreement to defend, indemnify, and hold harmless the contributors to this codebase from all claims of any kind related to your use of this contract. +This contract is designed for educational purposes only. Your use of this contract constitutes your agreement to the terms of the License below. In addition, your use of this contract constitutes your agreement to defend, indemnify, and hold harmless the contributors to this codebase from all claims of any kind related to your use of this contract. ## License diff --git a/contracts/swap/Cargo.toml b/contracts/swap/Cargo.toml index 0587650..348ccd7 100644 --- a/contracts/swap/Cargo.toml +++ b/contracts/swap/Cargo.toml @@ -38,11 +38,9 @@ serde-json-wasm = "1.0.1" thiserror = { version = "1.0.31" } [dev-dependencies] -cosmos-sdk-proto = { version = "0.21.1", features = [ "cosmwasm" ] } -cosmwasm-schema = "1.5.0" -cw-multi-test = "0.16.2" -injective-std = { git = "https://github.com/InjectiveLabs/cw-injective.git", rev = "8957ee2" } -# injective-std = { version = "0.1.5" } -injective-test-tube = { git = "https://github.com/InjectiveLabs/test-tube.git", rev = "402efd7" } -# injective-test-tube = "1.1.7" -prost = "0.11.9" +cosmos-sdk-proto = { version = "0.21.1", features = [ "cosmwasm" ] } +cosmwasm-schema = "1.5.0" +cw-multi-test = "0.16.2" +injective-std = { version = "0.1.7" } +injective-test-tube = "1.2.0" +prost = "0.12.4" From 9860c8e3feb86e5e95b210465ff043d166b523f6 Mon Sep 17 00:00:00 2001 From: Markus Waas Date: Mon, 13 May 2024 16:36:48 +0100 Subject: [PATCH 9/9] chore: remove unused file --- contracts/swap/msg.rs | 70 ------------------------------------------- 1 file changed, 70 deletions(-) delete mode 100644 contracts/swap/msg.rs diff --git a/contracts/swap/msg.rs b/contracts/swap/msg.rs deleted file mode 100644 index f0c841e..0000000 --- a/contracts/swap/msg.rs +++ /dev/null @@ -1,70 +0,0 @@ -use cosmwasm_std::{Addr, Coin}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use injective_cosmwasm::MarketId; -use injective_math::FPDecimal; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum FeeRecipient { - Address(Addr), - SwapContract, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct InstantiateMsg { - pub fee_recipient: FeeRecipient, - pub admin: Addr, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ExecuteMsg { - SwapMinOutput { - target_denom: String, - min_output_quantity: FPDecimal, - }, - SwapExactOutput { - target_denom: String, - target_output_quantity: FPDecimal, - }, - SetRoute { - source_denom: String, - target_denom: String, - route: Vec, - }, - DeleteRoute { - source_denom: String, - target_denom: String, - }, - UpdateConfig { - admin: Option, - fee_recipient: Option, - }, - WithdrawSupportFunds { - coins: Vec, - target_address: Addr, - }, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum QueryMsg { - GetRoute { - source_denom: String, - target_denom: String, - }, - GetOutputQuantity { - from_quantity: FPDecimal, - source_denom: String, - target_denom: String, - }, - GetInputQuantity { - to_quantity: FPDecimal, - source_denom: String, - target_denom: String, - }, - GetAllRoutes {}, - GetConfig {}, -}