Skip to content

Commit

Permalink
Merge pull request #11 from InjectiveLabs/tests/issue-9-round-properl…
Browse files Browse the repository at this point in the history
…y-min-tick-multiple-quote-denoms
  • Loading branch information
gorgos authored Aug 21, 2023
2 parents 22547d7 + 1c6368d commit f63f9ff
Show file tree
Hide file tree
Showing 4 changed files with 512 additions and 17 deletions.
31 changes: 26 additions & 5 deletions contracts/swap/src/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use injective_cosmwasm::{
create_spot_market_order_msg, get_default_subaccount_id_for_checked_address,
InjectiveMsgWrapper, InjectiveQuerier, InjectiveQueryWrapper, OrderType, SpotOrder,
};
use injective_math::FPDecimal;
use injective_math::{round_to_min_tick, FPDecimal};
use injective_protobuf::proto::tx;

use crate::error::ContractError;
Expand Down Expand Up @@ -221,17 +221,38 @@ pub fn handle_atomic_order_reply(
quantity * average_price - fee
};

let swap = SWAP_OPERATION_STATE.load(deps.storage)?;

let has_next_market = swap.swap_steps.len() > (current_step.step_idx + 1) as usize;

let new_rounded_quantity = if has_next_market {
let querier = InjectiveQuerier::new(&deps.querier);
let next_market_id = swap.swap_steps[(current_step.step_idx + 1) as usize].to_owned();
let next_market = querier
.query_spot_market(&next_market_id)?
.market
.expect("market should be available");

let is_next_swap_sell = next_market.base_denom == current_step.step_target_denom;

if is_next_swap_sell {
round_to_min_tick(new_quantity, next_market.min_quantity_tick_size)
} else {
new_quantity
}
} else {
new_quantity
};

let new_balance = FPCoin {
amount: new_quantity,
amount: new_rounded_quantity,
denom: current_step.step_target_denom,
};

let swap = SWAP_OPERATION_STATE.load(deps.storage)?;

swap_results.push(SwapResults {
market_id: swap.swap_steps[(current_step.step_idx) as usize].to_owned(),
price: average_price,
quantity: new_quantity,
quantity: new_rounded_quantity,
fee,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ use injective_math::FPDecimal;

use crate::msg::{ExecuteMsg, QueryMsg};
use crate::testing::test_utils::{
are_fpdecimals_approximately_equal, create_realistic_atom_usdt_sell_orders_from_spreadsheet,
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,
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,
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, USDT,
INJ, INJ_2, USDC, USDT,
};
use crate::types::SwapEstimationResult;
use crate::types::{FPCoin, SwapEstimationResult};

/*
This test suite focuses on using using realistic values both for spot markets and for orders and
Expand Down Expand Up @@ -681,6 +683,217 @@ fn it_swaps_inj_to_get_very_high_exact_amount_of_eth() {
);
}

#[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();
Expand Down
Loading

0 comments on commit f63f9ff

Please sign in to comment.