diff --git a/contracts/injective-cosmwasm-mock/src/contract.rs b/contracts/injective-cosmwasm-mock/src/contract.rs index bb2c6964..470b98b1 100644 --- a/contracts/injective-cosmwasm-mock/src/contract.rs +++ b/contracts/injective-cosmwasm-mock/src/contract.rs @@ -41,5 +41,75 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Std QueryMsg::TestSubAccountDepositQuery { subaccount_id, denom } => to_json_binary(&querier.query_subaccount_deposit(&subaccount_id, &denom)?), QueryMsg::TestSpotMarketQuery { market_id } => to_json_binary(&querier.query_spot_market(&market_id)?), QueryMsg::TestDerivativeMarketQuery { market_id } => to_json_binary(&querier.query_derivative_market(&market_id)?), + QueryMsg::TestEffectiveSubaccountPosition { market_id, subaccount_id } => { + to_json_binary(&querier.query_effective_subaccount_position(&market_id, &subaccount_id)?) + } + QueryMsg::TestVanillaSubaccountPosition { market_id, subaccount_id } => { + to_json_binary(&querier.query_vanilla_subaccount_position(&market_id, &subaccount_id)?) + } + QueryMsg::TestTraderDerivativeOrders { market_id, subaccount_id } => { + to_json_binary(&querier.query_trader_derivative_orders(&market_id, &subaccount_id)?) + } + QueryMsg::TestTraderTransientSpotOrders { market_id, subaccount_id } => { + to_json_binary(&querier.query_trader_transient_spot_orders(&market_id, &subaccount_id)?) + } + QueryMsg::TestTraderTransientDerivativeOrders { market_id, subaccount_id } => { + to_json_binary(&querier.query_trader_transient_derivative_orders(&market_id, &subaccount_id)?) + } + QueryMsg::TestTraderSpotOrders { market_id, subaccount_id } => to_json_binary(&querier.query_trader_spot_orders(&market_id, &subaccount_id)?), + QueryMsg::TestSpotOrdersToCancelUpToAmount { + market_id, + subaccount_id, + base_amount, + quote_amount, + strategy, + reference_price, + } => to_json_binary(&querier.query_spot_orders_to_cancel_up_to_amount( + &market_id, + &subaccount_id, + base_amount, + quote_amount, + strategy, + reference_price, + )?), + QueryMsg::TestDerivativeOrdersToCancelUpToAmount { + market_id, + subaccount_id, + quote_amount, + strategy, + reference_price, + } => to_json_binary(&querier.query_derivative_orders_to_cancel_up_to_amount( + &market_id, + &subaccount_id, + quote_amount, + strategy, + reference_price, + )?), + QueryMsg::TestPerpetualMarketInfo { market_id } => to_json_binary(&querier.query_perpetual_market_info(&market_id)?), + QueryMsg::TestPerpetualMarketFunding { market_id } => to_json_binary(&querier.query_perpetual_market_funding(&market_id)?), + QueryMsg::TestMarketVolatility { + market_id, + trade_grouping_sec, + max_age, + include_raw_history, + include_metadata, + } => to_json_binary(&querier.query_market_volatility(&market_id, trade_grouping_sec, max_age, include_raw_history, include_metadata)?), + QueryMsg::TestDerivativeMarketMidPriceAndTob { market_id } => to_json_binary(&querier.query_derivative_market_mid_price_and_tob(&market_id)?), + QueryMsg::TestAggregateMarketVolume { market_id } => to_json_binary(&querier.query_aggregate_market_volume(&market_id)?), + QueryMsg::TestAggregateAccountVolume { account_id } => to_json_binary(&querier.query_aggregate_account_volume(&account_id)?), + QueryMsg::TestSpotMarketMidPriceAndTob { market_id } => to_json_binary(&querier.query_spot_market_mid_price_and_tob(&market_id)?), + QueryMsg::TestSpotMarketOrderbook { + market_id, + side, + limit_cumulative_quantity, + limit_cumulative_notional, + } => to_json_binary(&querier.query_spot_market_orderbook(&market_id, side, limit_cumulative_quantity, limit_cumulative_notional)?), + QueryMsg::TestDerivativeMarketOrderbook { + market_id, + limit_cumulative_notional, + } => to_json_binary(&querier.query_derivative_market_orderbook(&market_id, limit_cumulative_notional)?), + QueryMsg::TestMarketAtomicExecutionFeeMultiplier { market_id } => { + to_json_binary(&querier.query_market_atomic_execution_fee_multiplier(&market_id)?) + } } } diff --git a/contracts/injective-cosmwasm-mock/src/msg.rs b/contracts/injective-cosmwasm-mock/src/msg.rs index 95a690dc..a7378d3e 100644 --- a/contracts/injective-cosmwasm-mock/src/msg.rs +++ b/contracts/injective-cosmwasm-mock/src/msg.rs @@ -16,7 +16,91 @@ pub enum ExecuteMsg { #[serde(rename_all = "snake_case")] pub enum QueryMsg { TestExchangeParamsQuery {}, - TestSpotMarketQuery { market_id: MarketId }, - TestDerivativeMarketQuery { market_id: MarketId }, - TestSubAccountDepositQuery { subaccount_id: SubaccountId, denom: String }, + TestSpotMarketQuery { + market_id: MarketId, + }, + TestDerivativeMarketQuery { + market_id: MarketId, + }, + TestSubAccountDepositQuery { + subaccount_id: SubaccountId, + denom: String, + }, + TestEffectiveSubaccountPosition { + market_id: MarketId, + subaccount_id: SubaccountId, + }, + TestVanillaSubaccountPosition { + market_id: MarketId, + subaccount_id: SubaccountId, + }, + TestTraderDerivativeOrders { + market_id: MarketId, + subaccount_id: SubaccountId, + }, + TestTraderTransientSpotOrders { + market_id: MarketId, + subaccount_id: SubaccountId, + }, + TestTraderTransientDerivativeOrders { + market_id: MarketId, + subaccount_id: SubaccountId, + }, + TestTraderSpotOrders { + market_id: MarketId, + subaccount_id: SubaccountId, + }, + TestSpotOrdersToCancelUpToAmount { + market_id: MarketId, + subaccount_id: SubaccountId, + base_amount: FPDecimal, + quote_amount: FPDecimal, + strategy: CancellationStrategy, + reference_price: Option, + }, + TestDerivativeOrdersToCancelUpToAmount { + market_id: MarketId, + subaccount_id: SubaccountId, + quote_amount: FPDecimal, + strategy: CancellationStrategy, + reference_price: Option, + }, + TestPerpetualMarketInfo { + market_id: MarketId, + }, + TestPerpetualMarketFunding { + market_id: MarketId, + }, + TestMarketVolatility { + market_id: MarketId, + trade_grouping_sec: u64, + max_age: u64, + include_raw_history: bool, + include_metadata: bool, + }, + TestDerivativeMarketMidPriceAndTob { + market_id: MarketId, + }, + TestAggregateMarketVolume { + market_id: MarketId, + }, + TestAggregateAccountVolume { + account_id: String, + }, + TestSpotMarketMidPriceAndTob { + market_id: MarketId, + }, + TestSpotMarketOrderbook { + market_id: MarketId, + side: OrderSide, + limit_cumulative_quantity: Option, + limit_cumulative_notional: Option, + }, + TestDerivativeMarketOrderbook { + market_id: MarketId, + limit_cumulative_notional: FPDecimal, + }, + TestMarketAtomicExecutionFeeMultiplier { + market_id: MarketId, + }, } diff --git a/contracts/injective-cosmwasm-mock/src/testing/query_exchange_test.rs b/contracts/injective-cosmwasm-mock/src/testing/query_exchange_test.rs index 5801edbd..3ca76e85 100644 --- a/contracts/injective-cosmwasm-mock/src/testing/query_exchange_test.rs +++ b/contracts/injective-cosmwasm-mock/src/testing/query_exchange_test.rs @@ -1,25 +1,23 @@ use crate::{ msg::{ExecuteMsg, QueryMsg}, - utils::{human_to_dec, human_to_proto, str_coin, Setup, BASE_DECIMALS, BASE_DENOM, QUOTE_DECIMALS, QUOTE_DENOM}, + utils::{ + dec_to_proto, get_spot_market_id, human_to_dec, human_to_proto, str_coin, Setup, BASE_DECIMALS, BASE_DENOM, QUOTE_DECIMALS, QUOTE_DENOM, + }, }; -use cosmwasm_std::Coin; -use injective_cosmwasm::{DerivativeMarketResponse, ExchangeParamsResponse, MarketId, SubaccountDepositResponse}; -use injective_math::{scale::Scaled, FPDecimal}; -use injective_std::types::injective::exchange::v1beta1::{ - Deposit, MsgDeposit, MsgInstantPerpetualMarketLaunch, MsgInstantSpotMarketLaunch, QueryDerivativeMarketsRequest, QuerySpotMarketsRequest, - QuerySubaccountDepositsRequest, -}; -use injective_test_tube::{injective_cosmwasm::SpotMarketResponse, Account, Exchange, Module, RunnerResult, Wasm}; - -pub fn dec_to_proto(val: FPDecimal) -> String { - val.scaled(18).to_string() -} +use crate::utils::{add_spot_orders, get_perpetual_market_id, scale_price_quantity_for_spot_market, ExchangeType, HumanOrder, scale_price_quantity_perp_market, add_derivative_orders}; +use cosmwasm_std::{Addr, Coin}; +use injective_cosmwasm::exchange::response::{QueryAggregateVolumeResponse, QueryOrderbookResponse}; +use injective_cosmwasm::{DerivativeMarketResponse, ExchangeParamsResponse, MarketId, MarketMidPriceAndTOBResponse, MarketVolatilityResponse, OrderSide, PerpetualMarketFundingResponse, PerpetualMarketInfoResponse, PriceLevel, QueryMarketAtomicExecutionFeeMultiplierResponse, SpotMarketResponse, SubaccountDepositResponse, SubaccountEffectivePositionInMarketResponse, SubaccountId, SubaccountPositionInMarketResponse, TraderDerivativeOrdersResponse, TraderSpotOrdersResponse, TrimmedDerivativeLimitOrder, TrimmedSpotLimitOrder}; +use injective_math::FPDecimal; +use injective_std::types::injective::exchange::v1beta1::{Deposit, DerivativeOrder, MsgCreateDerivativeLimitOrder, MsgCreateSpotLimitOrder, MsgDeposit, MsgInstantPerpetualMarketLaunch, MsgInstantSpotMarketLaunch, OrderInfo, OrderType, QueryAggregateMarketVolumeResponse, QuerySubaccountDepositsRequest, SpotOrder}; +use injective_test_tube::injective_cosmwasm::get_default_subaccount_id_for_checked_address; +use injective_test_tube::{Account, Exchange, Module, RunnerResult, Wasm}; #[test] #[cfg_attr(not(feature = "integration"), ignore)] fn test_msg_deposit() { - let env = Setup::new(); + let env = Setup::new(ExchangeType::None); let wasm = Wasm::new(&env.app); let user = &env.users[0]; @@ -40,7 +38,7 @@ fn test_msg_deposit() { #[test] #[cfg_attr(not(feature = "integration"), ignore)] fn test_exchange_params() { - let env = Setup::new(); + let env = Setup::new(ExchangeType::None); let wasm = Wasm::new(&env.app); let res: ExchangeParamsResponse = wasm.query(&env.contract_address, &QueryMsg::TestExchangeParamsQuery {}).unwrap(); @@ -57,7 +55,7 @@ fn test_exchange_params() { #[test] #[cfg_attr(not(feature = "integration"), ignore)] fn test_query_subaccount_deposit() { - let env = Setup::new(); + let env = Setup::new(ExchangeType::None); let exchange = Exchange::new(&env.app); let wasm = Wasm::new(&env.app); @@ -133,7 +131,7 @@ fn test_query_subaccount_deposit() { #[test] #[cfg_attr(not(feature = "integration"), ignore)] fn test_query_spot_market_no_market_on_exchange() { - let env = Setup::new(); + let env = Setup::new(ExchangeType::None); let wasm = Wasm::new(&env.app); // Query @@ -146,7 +144,7 @@ fn test_query_spot_market_no_market_on_exchange() { #[test] #[cfg_attr(not(feature = "integration"), ignore)] fn test_query_spot_market() { - let env = Setup::new(); + let env = Setup::new(ExchangeType::None); let wasm = Wasm::new(&env.app); let exchange = Exchange::new(&env.app); @@ -169,16 +167,7 @@ fn test_query_spot_market() { ) .unwrap(); - let spot_markets = exchange - .query_spot_markets(&QuerySpotMarketsRequest { - status: "Active".to_string(), - market_ids: vec![], - }) - .unwrap() - .markets; - - let market = spot_markets.iter().find(|m| m.ticker == ticker).unwrap(); - let spot_market_id = market.market_id.to_string(); + let spot_market_id = get_spot_market_id(&exchange, ticker.to_owned()); // Query let market_id = MarketId::new(spot_market_id.clone()).unwrap(); @@ -197,20 +186,19 @@ fn test_query_spot_market() { #[test] #[cfg_attr(not(feature = "integration"), ignore)] fn test_query_derivative_market() { - let env = Setup::new(); + let env = Setup::new(ExchangeType::None); let wasm = Wasm::new(&env.app); let exchange = Exchange::new(&env.app); let ticker = "INJ/USDT".to_string(); let initial_margin_ratio = FPDecimal::must_from_str("0.195"); let maintenance_margin_ratio = FPDecimal::must_from_str("0.05"); - let min_price_tick_size = FPDecimal::must_from_str("1000000000000000000000"); + let min_price_tick_size = FPDecimal::must_from_str("1000.0"); let min_quantity_tick_size = FPDecimal::must_from_str("1000000000000000"); let quote_denom = QUOTE_DENOM.to_string(); let maker_fee_rate = FPDecimal::ZERO; let taker_fee_rate = FPDecimal::ZERO; - println!("{} {}", initial_margin_ratio, dec_to_proto(initial_margin_ratio)); exchange .instant_perpetual_market_launch( MsgInstantPerpetualMarketLaunch { @@ -232,32 +220,13 @@ fn test_query_derivative_market() { ) .unwrap(); - let derivative_markets = exchange - .query_derivative_markets(&QueryDerivativeMarketsRequest { - status: "Active".to_string(), - market_ids: vec![], - with_mid_price_and_tob: false, - }) - .unwrap() - .markets; - - let market = derivative_markets - .iter() - .filter(|m| m.market.is_some()) - .find(|m| m.market.as_ref().unwrap().ticker == ticker) - .unwrap() - .market - .as_ref() - .unwrap(); - - let derivative_market_id = market.market_id.to_string(); + let derivative_market_id = get_perpetual_market_id(&exchange, ticker.to_owned()); let market_id = MarketId::new(derivative_market_id.clone()).unwrap(); let query_msg = QueryMsg::TestDerivativeMarketQuery { market_id }; let res: DerivativeMarketResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); let response_market = res.market.unwrap().market.unwrap(); - println!("{:?}", response_market); assert_eq!(response_market.market_id.as_str(), derivative_market_id); assert_eq!(response_market.ticker, ticker); assert_eq!(response_market.quote_denom, quote_denom); @@ -267,3 +236,796 @@ fn test_query_derivative_market() { assert_eq!(response_market.taker_fee_rate, taker_fee_rate); assert_eq!(response_market.initial_margin_ratio, initial_margin_ratio); } + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_effective_subaccount_position() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Buy, + }, + ]; + add_derivative_orders(&env.app, market_id.clone(), liquidity_orders.to_owned(), None); + + let (price, quantity, margin) = + scale_price_quantity_perp_market("9.7", "1", "2", "E_DECIMALS); + + let trader = &env.users[1]; + let subaccount_id = get_default_subaccount_id_for_checked_address(&Addr::unchecked(trader.account.address())) + .as_str() + .to_string(); + + exchange + .create_derivative_limit_order( + MsgCreateDerivativeLimitOrder { + sender: trader.account.address(), + order: Some(DerivativeOrder { + market_id: market_id.to_owned(), + order_info: Some(OrderInfo { + subaccount_id: subaccount_id.to_owned(), + fee_recipient: trader.account.address(), + price, + quantity, + }), + margin, + order_type: OrderType::Sell.into(), + trigger_price: "".to_string(), + }), + }, + &trader.account, + ) + .unwrap(); + + let query_msg = QueryMsg::TestEffectiveSubaccountPosition { + market_id: MarketId::new(market_id.clone()).unwrap(), + subaccount_id: SubaccountId::new(subaccount_id.to_owned()).unwrap(), + }; + let res: SubaccountEffectivePositionInMarketResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert!(res.state.is_some()); + +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_vanilla_subaccount_position() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Buy, + }, + ]; + add_derivative_orders(&env.app, market_id.clone(), liquidity_orders.to_owned(), None); + + let (price, quantity, margin) = + scale_price_quantity_perp_market("9.7", "1", "2", "E_DECIMALS); + + let trader = &env.users[1]; + let subaccount_id = get_default_subaccount_id_for_checked_address(&Addr::unchecked(trader.account.address())) + .as_str() + .to_string(); + + exchange + .create_derivative_limit_order( + MsgCreateDerivativeLimitOrder { + sender: trader.account.address(), + order: Some(DerivativeOrder { + market_id: market_id.to_owned(), + order_info: Some(OrderInfo { + subaccount_id: subaccount_id.to_owned(), + fee_recipient: trader.account.address(), + price, + quantity, + }), + margin, + order_type: OrderType::Sell.into(), + trigger_price: "".to_string(), + }), + }, + &trader.account, + ) + .unwrap(); + + let query_msg = QueryMsg::TestVanillaSubaccountPosition { + market_id: MarketId::new(market_id.clone()).unwrap(), + subaccount_id: SubaccountId::new(subaccount_id.to_owned()).unwrap(), + }; + let res: SubaccountPositionInMarketResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert!(res.state.is_some()); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "9.7".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + ]; + add_derivative_orders(&env.app, market_id.clone(), liquidity_orders.to_owned(), None); + + let res: SubaccountPositionInMarketResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + + +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_trader_spot_orders() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + let market_id = env.market_id.unwrap(); + + { + let (price, quantity) = scale_price_quantity_for_spot_market("10.01", "5.1", &BASE_DECIMALS, "E_DECIMALS); + let trader = &env.users[0]; + + let subaccount_id = get_default_subaccount_id_for_checked_address(&Addr::unchecked(trader.account.address())) + .as_str() + .to_string(); + + exchange + .create_spot_limit_order( + MsgCreateSpotLimitOrder { + sender: trader.account.address(), + order: Some(SpotOrder { + market_id: market_id.clone(), + order_info: Some(OrderInfo { + subaccount_id: subaccount_id.to_owned(), + fee_recipient: trader.account.address(), + price, + quantity, + }), + order_type: OrderType::Sell.into(), + trigger_price: "".to_string(), + }), + }, + &trader.account, + ) + .unwrap(); + + let query_msg = QueryMsg::TestTraderSpotOrders { + market_id: MarketId::new(market_id.clone()).unwrap(), + subaccount_id: SubaccountId::new(subaccount_id).unwrap(), + }; + let res: TraderSpotOrdersResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + let orders = res.orders.clone().unwrap(); + + assert_eq!(orders.len(), 1); + let expected_orders = TrimmedSpotLimitOrder { + price: human_to_dec("10.01", QUOTE_DECIMALS - BASE_DECIMALS), + quantity: human_to_dec("5.1", BASE_DECIMALS), + fillable: human_to_dec("5.1", BASE_DECIMALS), + isBuy: false, + order_hash: "".to_string(), + }; + assert_eq!(orders[0].price, expected_orders.price); + assert_eq!(orders[0].quantity, expected_orders.quantity); + assert_eq!(orders[0].fillable, expected_orders.fillable); + assert_eq!(orders[0].isBuy, expected_orders.isBuy); + } + + { + let (price, quantity) = scale_price_quantity_for_spot_market("9.90", "0.5", &BASE_DECIMALS, "E_DECIMALS); + let trader = &env.users[0]; + + let subaccount_id = get_default_subaccount_id_for_checked_address(&Addr::unchecked(trader.account.address())) + .as_str() + .to_string(); + + exchange + .create_spot_limit_order( + MsgCreateSpotLimitOrder { + sender: trader.account.address(), + order: Some(SpotOrder { + market_id: market_id.clone(), + order_info: Some(OrderInfo { + subaccount_id: subaccount_id.to_owned(), + fee_recipient: trader.account.address(), + price, + quantity, + }), + order_type: OrderType::Buy.into(), + trigger_price: "".to_string(), + }), + }, + &trader.account, + ) + .unwrap(); + + let query_msg = QueryMsg::TestTraderSpotOrders { + market_id: MarketId::new(market_id.clone()).unwrap(), + subaccount_id: SubaccountId::new(subaccount_id).unwrap(), + }; + let res: TraderSpotOrdersResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + let orders = res.orders.clone().unwrap(); + + assert_eq!(orders.len(), 2); + let expected_order = TrimmedSpotLimitOrder { + price: human_to_dec("9.90", QUOTE_DECIMALS - BASE_DECIMALS), + quantity: human_to_dec("0.5", BASE_DECIMALS), + fillable: human_to_dec("0.5", BASE_DECIMALS), + isBuy: true, + order_hash: "".to_string(), + }; + assert_eq!(orders[0].price, expected_order.price); + assert_eq!(orders[0].quantity, expected_order.quantity); + assert_eq!(orders[0].fillable, expected_order.fillable); + assert_eq!(orders[0].isBuy, expected_order.isBuy); + } +} +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_trader_derivative_orders() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + let market_id = env.market_id.unwrap(); + + let (price, quantity, margin) = + scale_price_quantity_perp_market("10.1", "1", "2", "E_DECIMALS); + + let trader = &env.users[0]; + let subaccount_id = get_default_subaccount_id_for_checked_address(&Addr::unchecked(trader.account.address())) + .as_str() + .to_string(); + + exchange + .create_derivative_limit_order( + MsgCreateDerivativeLimitOrder { + sender: trader.account.address(), + order: Some(DerivativeOrder { + market_id: market_id.to_owned(), + order_info: Some(OrderInfo { + subaccount_id: subaccount_id.to_owned(), + fee_recipient: trader.account.address(), + price, + quantity, + }), + margin, + order_type: OrderType::Sell.into(), + trigger_price: "".to_string(), + }), + }, + &trader.account, + ) + .unwrap(); + + let query_msg = QueryMsg::TestTraderDerivativeOrders { + market_id: MarketId::new(market_id.clone()).unwrap(), + subaccount_id: SubaccountId::new(subaccount_id).unwrap(), + }; + let res: TraderDerivativeOrdersResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert!(res.orders.is_some()); + + let orders = res.orders.clone().unwrap(); + assert_eq!(orders.len(), 1); + let expected_order = TrimmedDerivativeLimitOrder { + price: human_to_dec("10.1", QUOTE_DECIMALS), + quantity: FPDecimal::must_from_str("1"), + margin: human_to_dec("20.2", QUOTE_DECIMALS), + fillable: FPDecimal::must_from_str("1"), + isBuy: false, + order_hash: "".to_string(), + }; + assert_eq!(orders[0].price, expected_order.price); + assert_eq!(orders[0].quantity, expected_order.quantity); + assert_eq!(orders[0].fillable, expected_order.fillable); + assert_eq!(orders[0].isBuy, expected_order.isBuy); + assert_eq!(orders[0].margin, expected_order.margin); + +} + + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_spot_market_mid_price_and_tob() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Buy, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), liquidity_orders); + + let query_msg = QueryMsg::TestSpotMarketMidPriceAndTob { + market_id: MarketId::new(market_id.clone()).unwrap(), + }; + + let res: MarketMidPriceAndTOBResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.mid_price, Some(human_to_dec("10", QUOTE_DECIMALS - BASE_DECIMALS))); + assert_eq!(res.best_buy_price, Some(human_to_dec("9.9", QUOTE_DECIMALS - BASE_DECIMALS))); + assert_eq!(res.best_sell_price, Some(human_to_dec("10.1", QUOTE_DECIMALS - BASE_DECIMALS))); +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_spot_market_orderbook() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Buy, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), liquidity_orders.clone()); + + let query_msg = QueryMsg::TestSpotMarketOrderbook { + market_id: MarketId::new(market_id.clone()).unwrap(), + side: OrderSide::Unspecified, + limit_cumulative_quantity: None, + limit_cumulative_notional: None, + }; + + let res: QueryOrderbookResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + let buys_price_level = res.buys_price_level; + let sells_price_level = res.sells_price_level; + assert_eq!(buys_price_level.len(), 2); + assert_eq!(sells_price_level.len(), 2); + assert_eq!( + buys_price_level[0], + PriceLevel { + p: human_to_dec(liquidity_orders[2].price.as_str(), QUOTE_DECIMALS - BASE_DECIMALS), + q: human_to_dec(liquidity_orders[2].quantity.as_str(), BASE_DECIMALS), + } + ); + assert_eq!( + buys_price_level[1], + PriceLevel { + p: human_to_dec(liquidity_orders[3].price.as_str(), QUOTE_DECIMALS - BASE_DECIMALS), + q: human_to_dec(liquidity_orders[3].quantity.as_str(), BASE_DECIMALS), + } + ); + + assert_eq!( + sells_price_level[0], + PriceLevel { + p: human_to_dec(liquidity_orders[1].price.as_str(), QUOTE_DECIMALS - BASE_DECIMALS), + q: human_to_dec(liquidity_orders[1].quantity.as_str(), BASE_DECIMALS), + } + ); + assert_eq!( + sells_price_level[1], + PriceLevel { + p: human_to_dec(liquidity_orders[0].price.as_str(), QUOTE_DECIMALS - BASE_DECIMALS), + q: human_to_dec(liquidity_orders[0].quantity.as_str(), BASE_DECIMALS), + } + ); +} + + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_perpetual_market_info() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + + let ticker = "INJ/USDT".to_string(); + let derivative_market_id = get_perpetual_market_id(&exchange, ticker.to_owned()); + let market_id = MarketId::new(derivative_market_id.clone()).unwrap(); + let query_msg = QueryMsg::TestPerpetualMarketInfo { + market_id: market_id.clone(), + }; + let res: PerpetualMarketInfoResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + + assert!(res.info.is_some()); + let market_info = res.info.clone().unwrap(); + assert_eq!(market_info.market_id, market_id); + assert_eq!(market_info.funding_interval, 3600i64); +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_perpetual_market_funding() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + + let ticker = "INJ/USDT".to_string(); + let derivative_market_id = get_perpetual_market_id(&exchange, ticker.to_owned()); + let market_id = MarketId::new(derivative_market_id.clone()).unwrap(); + let query_msg = QueryMsg::TestPerpetualMarketFunding { + market_id: market_id.clone(), + }; + let res: PerpetualMarketFundingResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert!(res.state.is_some()); + let state = res.state.unwrap(); + assert_eq!(state.cumulative_funding, FPDecimal::ZERO); + assert_eq!(state.cumulative_price, FPDecimal::ZERO); + +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_market_volatility() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Buy, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), liquidity_orders.clone()); + + let query_msg = QueryMsg::TestMarketVolatility { + market_id: MarketId::new(market_id.clone()).unwrap(), + trade_grouping_sec: 0, + max_age: 0, + include_raw_history: false, + include_metadata: true, + }; + + let res: MarketVolatilityResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.volatility, None); + + // consume liquidity + let new_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "15".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.0".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), new_orders.clone()); + let res: MarketVolatilityResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.volatility, Some(FPDecimal::ZERO)); +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_derivative_market_mid_price_and_tob() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Buy, + }, + ]; + add_derivative_orders(&env.app, market_id.clone(), liquidity_orders, None); + + let query_msg = QueryMsg::TestDerivativeMarketMidPriceAndTob { + market_id: MarketId::new(market_id.clone()).unwrap(), + }; + + let res: MarketMidPriceAndTOBResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.mid_price, Some(human_to_dec("10", QUOTE_DECIMALS))); + assert_eq!(res.best_buy_price, Some(human_to_dec("9.9", QUOTE_DECIMALS))); + assert_eq!(res.best_sell_price, Some(human_to_dec("10.1", QUOTE_DECIMALS))); +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_aggregate_market_volume() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Buy, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), liquidity_orders); + + let query_msg = QueryMsg::TestAggregateMarketVolume { + market_id: MarketId::new(market_id.clone()).unwrap(), + }; + + let res: QueryAggregateMarketVolumeResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.volume.clone().unwrap().maker_volume, "0"); + assert_eq!(res.volume.clone().unwrap().taker_volume, "0"); + + // consume liquidity + let new_orders: Vec = vec![ + HumanOrder { + price: "10.1".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Sell, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), new_orders); + let res: QueryAggregateMarketVolumeResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.volume.clone().unwrap().maker_volume, "150500000"); + assert_eq!(res.volume.clone().unwrap().taker_volume, "150500000"); +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_aggregate_account_volume() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "10".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "5".to_string(), + order_type: OrderType::Buy, + }, + ]; + + add_spot_orders(&env.app, market_id.clone(), liquidity_orders); + + let query_msg = QueryMsg::TestAggregateAccountVolume { + account_id: env.users[0].account.address().to_string(), + }; + + let res: RunnerResult = wasm.query(&env.contract_address, &query_msg); + println!("{:?}", res); + assert_eq!(1,2); +} + + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_derivative_market_orderbook() { + let env = Setup::new(ExchangeType::Derivative); + let wasm = Wasm::new(&env.app); + let exchange = Exchange::new(&env.app); + let market_id = env.market_id.unwrap(); + + let liquidity_orders: Vec = vec![ + HumanOrder { + price: "10.2".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "10.1".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Sell, + }, + HumanOrder { + price: "9.9".to_string(), + quantity: "1".to_string(), + order_type: OrderType::Buy, + }, + HumanOrder { + price: "9.8".to_string(), + quantity: "2".to_string(), + order_type: OrderType::Buy, + }, + ]; + add_derivative_orders(&env.app, market_id.clone(), liquidity_orders.to_owned(), None); + + let query_msg = QueryMsg::TestDerivativeMarketOrderbook { + market_id: MarketId::new(market_id.clone()).unwrap(), + limit_cumulative_notional: FPDecimal::MAX , + }; + + let res: QueryOrderbookResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + let buys_price_level = res.buys_price_level; + let sells_price_level = res.sells_price_level; + assert_eq!(buys_price_level.len(), 2); + assert_eq!(sells_price_level.len(), 2); + assert_eq!( + buys_price_level[0], + PriceLevel { + p: human_to_dec(liquidity_orders[2].price.as_str(), QUOTE_DECIMALS), + q: FPDecimal::must_from_str(liquidity_orders[2].quantity.as_str()), + } + ); + assert_eq!( + buys_price_level[1], + PriceLevel { + p: human_to_dec(liquidity_orders[3].price.as_str(), QUOTE_DECIMALS), + q: FPDecimal::must_from_str(liquidity_orders[3].quantity.as_str()), + } + ); + + assert_eq!( + sells_price_level[0], + PriceLevel { + p: human_to_dec(liquidity_orders[1].price.as_str(), QUOTE_DECIMALS), + q: FPDecimal::must_from_str(liquidity_orders[1].quantity.as_str()), + } + ); + assert_eq!( + sells_price_level[1], + PriceLevel { + p: human_to_dec(liquidity_orders[0].price.as_str(), QUOTE_DECIMALS), + q: FPDecimal::must_from_str(liquidity_orders[0].quantity.as_str()), + } + ); +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_market_atomic_execution_fee_multiplier() { + let env = Setup::new(ExchangeType::Spot); + let wasm = Wasm::new(&env.app); + let market_id = env.market_id.unwrap(); + let query_msg = QueryMsg::TestMarketAtomicExecutionFeeMultiplier { + market_id: MarketId::new(market_id.clone()).unwrap(), + }; + let res: QueryMarketAtomicExecutionFeeMultiplierResponse = wasm.query(&env.contract_address, &query_msg).unwrap(); + assert_eq!(res.multiplier, human_to_dec("0.0000025", QUOTE_DECIMALS)); +} + + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_spot_orders_to_cancel_up_to_amount() { + +} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_derivative_orders_to_cancel_up_to_amount() {} + + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_trader_transient_spot_orders() {} + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_query_trader_transient_derivative_orders() {} \ No newline at end of file diff --git a/contracts/injective-cosmwasm-mock/src/utils.rs b/contracts/injective-cosmwasm-mock/src/utils.rs index d4219ba2..d8a0d1d2 100644 --- a/contracts/injective-cosmwasm-mock/src/utils.rs +++ b/contracts/injective-cosmwasm-mock/src/utils.rs @@ -2,11 +2,12 @@ use crate::msg::InstantiateMsg; use cosmwasm_std::{coin, Addr, Coin}; use injective_cosmwasm::{checked_address_to_subaccount_id, SubaccountId}; use injective_math::{scale::Scaled, FPDecimal}; -use injective_test_tube::{Account, Bank, Gov, InjectiveTestApp, Insurance, Module, Oracle, SigningAccount, Wasm}; +use injective_test_tube::{Account, Bank, Exchange, Gov, InjectiveTestApp, Insurance, Module, Oracle, SigningAccount, Wasm}; use prost::Message; use std::{collections::HashMap, str::FromStr}; +use injective_std::types::injective::exchange::v1beta1::{DerivativeOrder, MsgCreateDerivativeLimitOrder, MsgCreateSpotLimitOrder, MsgInstantPerpetualMarketLaunch, MsgInstantSpotMarketLaunch, OrderInfo, OrderType, QueryDerivativeMarketsRequest, QuerySpotMarketsRequest, SpotOrder}; use injective_std::types::injective::insurance::v1beta1::MsgCreateInsuranceFund; use injective_std::types::injective::oracle::v1beta1::OracleType; use injective_std::{ @@ -20,6 +21,7 @@ use injective_std::{ injective::oracle::v1beta1::{GrantPriceFeederPrivilegeProposal, MsgRelayPriceFeedPrice}, }, }; +use injective_test_tube::injective_cosmwasm::get_default_subaccount_id_for_checked_address; pub const EXCHANGE_DECIMALS: i32 = 18i32; pub const BASE_DECIMALS: i32 = 18i32; @@ -43,12 +45,20 @@ pub struct Setup { pub denoms: HashMap, pub contract_address: String, pub code_id: u64, + pub market_id: Option, +} + +pub enum ExchangeType { + Spot, + Derivative, + None, } impl Setup { - pub fn new() -> Self { + pub fn new(exchange_type: ExchangeType) -> Self { let app = InjectiveTestApp::new(); let wasm = Wasm::new(&app); + let mut market_id = None; let mut denoms = HashMap::new(); denoms.insert("atom".to_string(), ATOM_DENOM.to_string()); @@ -118,6 +128,18 @@ impl Setup { human_to_dec("10.01", BASE_DECIMALS).to_string(), ); + match exchange_type { + ExchangeType::Spot => { + let exchange = Exchange::new(&app); + market_id = Some(launch_spot_market(&exchange, &owner, "INJ/USDT".to_string())); + } + ExchangeType::Derivative => { + let exchange = Exchange::new(&app); + market_id = Some(launch_perp_market(&exchange, &owner, "INJ/USDT".to_string())); + } + ExchangeType::None => {} + } + Self { app, owner, @@ -127,13 +149,14 @@ impl Setup { denoms, contract_address, code_id, + market_id, } } } impl Default for Setup { fn default() -> Self { - Self::new() + Self::new(ExchangeType::None) } } @@ -161,14 +184,6 @@ pub fn str_coin(human_amount: &str, denom: &str, decimals: i32) -> Coin { coin(as_int, denom) } -pub fn human_to_proto(raw_number: &str, decimals: i32) -> String { - FPDecimal::must_from_str(&raw_number.replace('_', "")).scaled(18 + decimals).to_string() -} - -pub fn human_to_dec(raw_number: &str, decimals: i32) -> FPDecimal { - FPDecimal::must_from_str(&raw_number.replace('_', "")).scaled(decimals) -} - pub fn send(bank: &Bank, amount: &str, denom: &str, from: &SigningAccount, to: &SigningAccount) { bank.send( MsgSend { @@ -286,3 +301,204 @@ pub fn launch_insurance_fund( ) .unwrap(); } + +pub fn launch_spot_market(exchange: &Exchange, signer: &SigningAccount, ticker: String) -> String { + exchange + .instant_spot_market_launch( + MsgInstantSpotMarketLaunch { + sender: signer.address(), + ticker: ticker.clone(), + base_denom: BASE_DENOM.to_string(), + quote_denom: QUOTE_DENOM.to_string(), + min_price_tick_size: dec_to_proto(FPDecimal::must_from_str("0.000000000000001")), + min_quantity_tick_size: dec_to_proto(FPDecimal::must_from_str("1000000000000000")), + }, + signer, + ) + .unwrap(); + + get_spot_market_id(exchange, ticker) +} + +pub fn get_spot_market_id(exchange: &Exchange, ticker: String) -> String { + let spot_markets = exchange + .query_spot_markets(&QuerySpotMarketsRequest { + status: "Active".to_string(), + market_ids: vec![], + }) + .unwrap() + .markets; + + let market = spot_markets.iter().find(|m| m.ticker == ticker).unwrap(); + market.market_id.to_string() +} + +pub fn launch_perp_market(exchange: &Exchange, signer: &SigningAccount, ticker: String) -> String { + exchange + .instant_perpetual_market_launch( + MsgInstantPerpetualMarketLaunch { + sender: signer.address(), + ticker: ticker.to_owned(), + quote_denom: "usdt".to_string(), + oracle_base: "inj".to_string(), + oracle_quote: "usdt".to_string(), + oracle_scale_factor: 6u32, + oracle_type: 2i32, + maker_fee_rate: "0".to_owned(), + taker_fee_rate: "0".to_owned(), + initial_margin_ratio: "195000000000000000".to_owned(), + maintenance_margin_ratio: "50000000000000000".to_owned(), + min_price_tick_size: "1000000000000000000000".to_owned(), + min_quantity_tick_size: "1000000000000000".to_owned(), + }, + signer, + ) + .unwrap(); + + get_perpetual_market_id(exchange, ticker) +} + +pub fn get_perpetual_market_id(exchange: &Exchange, ticker: String) -> String { + let perpetual_markets = exchange + .query_derivative_markets(&QueryDerivativeMarketsRequest { + status: "Active".to_string(), + market_ids: vec![], + with_mid_price_and_tob: false, + }) + .unwrap() + .markets; + + let market = perpetual_markets + .iter() + .filter(|m| m.market.is_some()) + .find(|m| m.market.as_ref().unwrap().ticker == ticker) + .unwrap() + .market + .as_ref() + .unwrap(); + + market.market_id.to_string() +} + +#[derive(Clone)] +pub struct HumanOrder { + pub price: String, + pub quantity: String, + pub order_type: OrderType, +} +pub fn add_spot_orders(app: &InjectiveTestApp, market_id: String, orders: Vec) { + let trader = app + .init_account(&[ + str_coin("1000000", BASE_DENOM, BASE_DECIMALS), + str_coin("1000000", QUOTE_DENOM, QUOTE_DECIMALS), + ]) + .unwrap(); + + let exchange = Exchange::new(app); + + for order in orders { + let (price, quantity) = scale_price_quantity_for_spot_market(order.price.as_str(), order.quantity.as_str(), &BASE_DECIMALS, "E_DECIMALS); + exchange + .create_spot_limit_order( + MsgCreateSpotLimitOrder { + sender: trader.address(), + order: Some(SpotOrder { + market_id: market_id.to_owned(), + order_info: Some(OrderInfo { + subaccount_id: get_default_subaccount_id_for_checked_address(&Addr::unchecked(trader.address())) + .as_str() + .to_string(), + fee_recipient: trader.address(), + price, + quantity, + }), + order_type: order.order_type.into(), + trigger_price: "".to_string(), + }), + }, + &trader, + ) + .unwrap(); + } +} + + +pub fn add_derivative_orders(app: &InjectiveTestApp, market_id: String, orders: Vec, margin:Option) { + + let trader = app + .init_account(&[ + str_coin("1000000", BASE_DENOM, BASE_DECIMALS), + str_coin("1000000", QUOTE_DENOM, QUOTE_DECIMALS), + ]) + .unwrap(); + + let exchange = Exchange::new(app); + let margin = margin.unwrap_or("2".into()); + + for order in orders { + let (price, quantity, order_margin) = scale_price_quantity_perp_market(order.price.as_str(), order.quantity.as_str(), &margin, "E_DECIMALS); + exchange + .create_derivative_limit_order( + MsgCreateDerivativeLimitOrder { + sender: trader.address(), + order: Some(DerivativeOrder { + market_id: market_id.to_owned(), + order_info: Some(OrderInfo { + subaccount_id: get_default_subaccount_id_for_checked_address( + &Addr::unchecked(trader.address()), + ) + .as_str() + .to_string(), + fee_recipient: trader.address(), + price, + quantity, + }), + margin: order_margin, + order_type: order.order_type.into(), + trigger_price: "".to_string(), + }), + }, + &trader, + ) + .unwrap(); + } +} + + +// Human Utils +pub fn human_to_proto(raw_number: &str, decimals: i32) -> String { + FPDecimal::must_from_str(&raw_number.replace('_', "")).scaled(18 + decimals).to_string() +} + +pub fn human_to_dec(raw_number: &str, decimals: i32) -> FPDecimal { + FPDecimal::must_from_str(&raw_number.replace('_', "")).scaled(decimals) +} + +pub fn dec_to_proto(val: FPDecimal) -> String { + val.scaled(18).to_string() +} + + + +pub fn scale_price_quantity_for_spot_market(price: &str, quantity: &str, base_decimals: &i32, quote_decimals: &i32) -> (String, String) { + let price_dec = FPDecimal::must_from_str(price.replace('_', "").as_str()); + let quantity_dec = FPDecimal::must_from_str(quantity.replace('_', "").as_str()); + + let scaled_price = price_dec.scaled(quote_decimals - base_decimals); + let scaled_quantity = quantity_dec.scaled(*base_decimals); + + (dec_to_proto(scaled_price), dec_to_proto(scaled_quantity)) +} + +pub fn scale_price_quantity_perp_market(price: &str, quantity: &str, margin_ratio: &str, quote_decimals: &i32) -> (String, String, String) { + let price_dec = FPDecimal::must_from_str(price.replace('_', "").as_str()); + let quantity_dec = FPDecimal::must_from_str(quantity.replace('_', "").as_str()); + let margin_ratio_dec = FPDecimal::must_from_str(margin_ratio.replace('_', "").as_str()); + + let scaled_price = price_dec.scaled(*quote_decimals); + let scaled_quantity = quantity_dec; + + let scaled_margin = (price_dec * quantity_dec * margin_ratio_dec).scaled(*quote_decimals); + + (dec_to_proto(scaled_price), dec_to_proto(scaled_quantity), dec_to_proto(scaled_margin)) +} diff --git a/packages/injective-cosmwasm/src/querier.rs b/packages/injective-cosmwasm/src/querier.rs index f0f0bd3d..e7af446a 100644 --- a/packages/injective-cosmwasm/src/querier.rs +++ b/packages/injective-cosmwasm/src/querier.rs @@ -9,10 +9,10 @@ use crate::exchange::{ order::OrderSide, response::{ DerivativeMarketResponse, ExchangeParamsResponse, MarketMidPriceAndTOBResponse, MarketVolatilityResponse, OracleVolatilityResponse, - PerpetualMarketFundingResponse, PerpetualMarketInfoResponse, QueryAggregateVolumeResponse, QueryDenomDecimalResponse, - QueryDenomDecimalsResponse, QueryMarketAtomicExecutionFeeMultiplierResponse, QueryOrderbookResponse, SpotMarketResponse, - SubaccountDepositResponse, SubaccountEffectivePositionInMarketResponse, SubaccountPositionInMarketResponse, TraderDerivativeOrdersResponse, - TraderSpotOrdersResponse, + PerpetualMarketFundingResponse, PerpetualMarketInfoResponse, QueryAggregateMarketVolumeResponse, QueryAggregateVolumeResponse, + QueryDenomDecimalResponse, QueryDenomDecimalsResponse, QueryMarketAtomicExecutionFeeMultiplierResponse, QueryOrderbookResponse, + SpotMarketResponse, SubaccountDepositResponse, SubaccountEffectivePositionInMarketResponse, SubaccountPositionInMarketResponse, + TraderDerivativeOrdersResponse, TraderSpotOrdersResponse, }, types::{MarketId, SubaccountId}, }; @@ -340,7 +340,7 @@ impl<'a> InjectiveQuerier<'a> { Ok(res) } - pub fn query_aggregate_market_volume + Clone>(&self, market_id: &'a T) -> StdResult { + pub fn query_aggregate_market_volume + Clone>(&self, market_id: &'a T) -> StdResult { let request = InjectiveQueryWrapper { route: InjectiveRoute::Exchange, query_data: InjectiveQuery::AggregateMarketVolume { @@ -348,7 +348,7 @@ impl<'a> InjectiveQuerier<'a> { }, }; - let res: QueryAggregateVolumeResponse = self.querier.query(&request.into())?; + let res: QueryAggregateMarketVolumeResponse = self.querier.query(&request.into())?; Ok(res) } @@ -359,7 +359,6 @@ impl<'a> InjectiveQuerier<'a> { account: account_id.clone().into(), }, }; - let res: QueryAggregateVolumeResponse = self.querier.query(&request.into())?; Ok(res) }