From c32c3320e83717fa7884279bbe79ebcbf513c1cd Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Wed, 21 Feb 2024 22:31:01 -0800 Subject: [PATCH 1/2] fix: remove migrate msg orderbook, add operator in query info & add migrate test --- .../oraiswap_limit_order/src/contract.rs | 8 +++- .../src/testing/migrate_test.rs | 39 +++++++++++++++++++ .../oraiswap_limit_order/src/testing/mod.rs | 1 + packages/oraiswap/src/limit_order.rs | 5 +-- 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 contracts/oraiswap_limit_order/src/testing/migrate_test.rs diff --git a/contracts/oraiswap_limit_order/src/contract.rs b/contracts/oraiswap_limit_order/src/contract.rs index 19f8bc07..96850872 100644 --- a/contracts/oraiswap_limit_order/src/contract.rs +++ b/contracts/oraiswap_limit_order/src/contract.rs @@ -605,11 +605,15 @@ pub fn query_contract_info(deps: Deps) -> StdResult { admin: deps.api.addr_humanize(&info.admin)?, commission_rate: info.commission_rate, reward_address: deps.api.addr_humanize(&info.reward_address)?, + operator: if let Some(operator) = info.operator { + Some(deps.api.addr_humanize(&operator)?) + } else { + None + }, }) } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult { - store_config(deps.storage, &msg.new_config)?; +pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { Ok(Response::default()) } diff --git a/contracts/oraiswap_limit_order/src/testing/migrate_test.rs b/contracts/oraiswap_limit_order/src/testing/migrate_test.rs new file mode 100644 index 00000000..5a9383df --- /dev/null +++ b/contracts/oraiswap_limit_order/src/testing/migrate_test.rs @@ -0,0 +1,39 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{testing::mock_dependencies, Api, CanonicalAddr, StdResult, Storage}; +use cosmwasm_storage::singleton; + +use crate::state::read_config; + +#[cw_serde] +pub struct ContractInfoOld { + pub name: String, + pub version: String, + // admin can update the parameter, may be multisig + pub admin: CanonicalAddr, + pub commission_rate: String, + pub reward_address: CanonicalAddr, +} + +static CONTRACT_INFO: &[u8] = b"contract_info"; // contract info + +pub fn store_config_old(storage: &mut dyn Storage, config: &ContractInfoOld) -> StdResult<()> { + singleton(storage, CONTRACT_INFO).save(config) +} + +#[test] +fn test_migrate_contract_info() { + // fixture + let mut deps = mock_dependencies(); + let contract_info = ContractInfoOld { + name: "foo".to_string(), + version: "1".to_string(), + admin: deps.api.addr_canonicalize("admin").unwrap(), + commission_rate: "1".to_string(), + reward_address: deps.api.addr_canonicalize("reward").unwrap(), + }; + store_config_old(deps.as_mut().storage, &contract_info).unwrap(); + + let config = read_config(deps.as_ref().storage).unwrap(); + assert_eq!(config.name, "foo".to_string()); + assert_eq!(config.operator, None); +} diff --git a/contracts/oraiswap_limit_order/src/testing/mod.rs b/contracts/oraiswap_limit_order/src/testing/mod.rs index f109ba96..375a8e45 100644 --- a/contracts/oraiswap_limit_order/src/testing/mod.rs +++ b/contracts/oraiswap_limit_order/src/testing/mod.rs @@ -1,4 +1,5 @@ mod contract_test; +mod migrate_test; mod orderbook_test; #[macro_export] diff --git a/packages/oraiswap/src/limit_order.rs b/packages/oraiswap/src/limit_order.rs index 997351cf..4070d6fd 100644 --- a/packages/oraiswap/src/limit_order.rs +++ b/packages/oraiswap/src/limit_order.rs @@ -224,6 +224,7 @@ pub struct ContractInfoResponse { pub admin: Addr, pub commission_rate: String, pub reward_address: Addr, + pub operator: Option, } #[cw_serde] @@ -285,6 +286,4 @@ pub struct BaseAmountResponse { /// We currently take no arguments for migrations #[cw_serde] -pub struct MigrateMsg { - pub new_config: ContractInfo, -} +pub struct MigrateMsg {} From 1da99d69113dba0d37599c56768b53e2b4da601a Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Sat, 24 Feb 2024 08:18:00 -0800 Subject: [PATCH 2/2] comment market order --- .../oraiswap_limit_order/src/contract.rs | 308 ++--- .../src/testing/contract_test.rs | 1228 ++++++++--------- packages/oraiswap/src/limit_order.rs | 34 +- 3 files changed, 785 insertions(+), 785 deletions(-) diff --git a/contracts/oraiswap_limit_order/src/contract.rs b/contracts/oraiswap_limit_order/src/contract.rs index 19f8bc07..6159f5ab 100644 --- a/contracts/oraiswap_limit_order/src/contract.rs +++ b/contracts/oraiswap_limit_order/src/contract.rs @@ -154,86 +154,86 @@ pub fn execute( // then submit order submit_order(deps, &orderbook_pair, info.sender, direction, paid_assets) } - ExecuteMsg::SubmitMarketOrder { - direction, - asset_infos, - base_amount, - quote_amount, - slippage, - } => { - let pair_key = pair_key(&[ - asset_infos[0].to_raw(deps.api)?, - asset_infos[1].to_raw(deps.api)?, - ]); - let orderbook_pair = read_orderbook(deps.storage, &pair_key)?; - - let offer_asset_info = match direction { - OrderDirection::Buy => orderbook_pair.quote_coin_info.to_normal(deps.api)?, - OrderDirection::Sell => orderbook_pair.base_coin_info.to_normal(deps.api)?, - }; - let provided_asset = get_native_asset(&info, offer_asset_info)?; - - let expected_offer_amount = match direction { - OrderDirection::Buy => quote_amount, - OrderDirection::Sell => base_amount, - }; - - if let Some(slippage) = slippage { - if slippage >= Decimal::one() { - return Err(ContractError::SlippageMustLessThanOne { slippage }); - } - } - - let base_amount_response = query_price_by_base_amount( - deps.as_ref(), - &orderbook_pair, - direction, - base_amount, - slippage, - )?; - - // Return error if cannot find opposite side market order - if base_amount_response.market_price.is_zero() { - return Err(ContractError::UnableToFindMarketOrder {}); - } - - let (paid_assets, quote_asset) = get_market_asset( - deps.api, - &orderbook_pair, - direction, - base_amount_response.market_price, - base_amount_response.expected_base_amount, - )?; - - if provided_asset.amount < expected_offer_amount { - return Err(ContractError::AssetMismatch {}); - } - - // require minimum amount for quote asset - if quote_asset.amount.lt(&orderbook_pair.min_quote_coin_amount) { - return Err(ContractError::TooSmallQuoteAsset { - quote_coin: quote_asset.info.to_string(), - min_quote_amount: orderbook_pair.min_quote_coin_amount, - }); - } - - // calculate refund_amount - let refund_amount = provided_asset - .amount - .checked_sub(paid_assets[0].amount) - .unwrap_or_default(); - - // submit market order - submit_market_order( - deps, - env.contract.address, - &orderbook_pair, - info.sender, - direction, - paid_assets, - refund_amount, - ) - } + // ExecuteMsg::SubmitMarketOrder { + // direction, + // asset_infos, + // base_amount, + // quote_amount, + // slippage, + // } => { + // let pair_key = pair_key(&[ + // asset_infos[0].to_raw(deps.api)?, + // asset_infos[1].to_raw(deps.api)?, + // ]); + // let orderbook_pair = read_orderbook(deps.storage, &pair_key)?; + + // let offer_asset_info = match direction { + // OrderDirection::Buy => orderbook_pair.quote_coin_info.to_normal(deps.api)?, + // OrderDirection::Sell => orderbook_pair.base_coin_info.to_normal(deps.api)?, + // }; + // let provided_asset = get_native_asset(&info, offer_asset_info)?; + + // let expected_offer_amount = match direction { + // OrderDirection::Buy => quote_amount, + // OrderDirection::Sell => base_amount, + // }; + + // if let Some(slippage) = slippage { + // if slippage >= Decimal::one() { + // return Err(ContractError::SlippageMustLessThanOne { slippage }); + // } + // } + + // let base_amount_response = query_price_by_base_amount( + // deps.as_ref(), + // &orderbook_pair, + // direction, + // base_amount, + // slippage, + // )?; + + // // Return error if cannot find opposite side market order + // if base_amount_response.market_price.is_zero() { + // return Err(ContractError::UnableToFindMarketOrder {}); + // } + + // let (paid_assets, quote_asset) = get_market_asset( + // deps.api, + // &orderbook_pair, + // direction, + // base_amount_response.market_price, + // base_amount_response.expected_base_amount, + // )?; + + // if provided_asset.amount < expected_offer_amount { + // return Err(ContractError::AssetMismatch {}); + // } + + // // require minimum amount for quote asset + // if quote_asset.amount.lt(&orderbook_pair.min_quote_coin_amount) { + // return Err(ContractError::TooSmallQuoteAsset { + // quote_coin: quote_asset.info.to_string(), + // min_quote_amount: orderbook_pair.min_quote_coin_amount, + // }); + // } + + // // calculate refund_amount + // let refund_amount = provided_asset + // .amount + // .checked_sub(paid_assets[0].amount) + // .unwrap_or_default(); + + // // submit market order + // submit_market_order( + // deps, + // env.contract.address, + // &orderbook_pair, + // info.sender, + // direction, + // paid_assets, + // refund_amount, + // ) + // } ExecuteMsg::CancelOrder { order_id, asset_infos, @@ -404,80 +404,80 @@ pub fn receive_cw20( // then submit order submit_order(deps, &orderbook_pair, sender, direction, paid_assets) } - Ok(Cw20HookMsg::SubmitMarketOrder { - direction, - asset_infos, - base_amount, - quote_amount, - slippage, - }) => { - let pair_key = pair_key(&[ - asset_infos[0].to_raw(deps.api)?, - asset_infos[1].to_raw(deps.api)?, - ]); - let orderbook_pair = read_orderbook(deps.storage, &pair_key)?; - - let expected_offer_amount = match direction { - OrderDirection::Buy => quote_amount, - OrderDirection::Sell => base_amount, - }; - - if let Some(slippage) = slippage { - if slippage >= Decimal::one() { - return Err(ContractError::SlippageMustLessThanOne { slippage }); - } - } - - let base_amount_response = query_price_by_base_amount( - deps.as_ref(), - &orderbook_pair, - direction, - base_amount, - slippage, - )?; - - // Return error if cannot find opposite side market order - if base_amount_response.market_price.is_zero() { - return Err(ContractError::UnableToFindMarketOrder {}); - } - - let (paid_assets, quote_asset) = get_market_asset( - deps.api, - &orderbook_pair, - direction, - base_amount_response.market_price, - base_amount_response.expected_base_amount, - )?; - - if provided_asset.amount < expected_offer_amount { - return Err(ContractError::AssetMismatch {}); - } - - // require minimum amount for quote asset - if quote_asset.amount.lt(&orderbook_pair.min_quote_coin_amount) { - return Err(ContractError::TooSmallQuoteAsset { - quote_coin: quote_asset.info.to_string(), - min_quote_amount: orderbook_pair.min_quote_coin_amount, - }); - } - - // calculate refund_amount - let refund_amount = provided_asset - .amount - .checked_sub(paid_assets[0].amount) - .unwrap_or_default(); - - // submit market order - submit_market_order( - deps, - env.contract.address, - &orderbook_pair, - sender, - direction, - paid_assets, - refund_amount, - ) - } + // Ok(Cw20HookMsg::SubmitMarketOrder { + // direction, + // asset_infos, + // base_amount, + // quote_amount, + // slippage, + // }) => { + // let pair_key = pair_key(&[ + // asset_infos[0].to_raw(deps.api)?, + // asset_infos[1].to_raw(deps.api)?, + // ]); + // let orderbook_pair = read_orderbook(deps.storage, &pair_key)?; + + // let expected_offer_amount = match direction { + // OrderDirection::Buy => quote_amount, + // OrderDirection::Sell => base_amount, + // }; + + // if let Some(slippage) = slippage { + // if slippage >= Decimal::one() { + // return Err(ContractError::SlippageMustLessThanOne { slippage }); + // } + // } + + // let base_amount_response = query_price_by_base_amount( + // deps.as_ref(), + // &orderbook_pair, + // direction, + // base_amount, + // slippage, + // )?; + + // // Return error if cannot find opposite side market order + // if base_amount_response.market_price.is_zero() { + // return Err(ContractError::UnableToFindMarketOrder {}); + // } + + // let (paid_assets, quote_asset) = get_market_asset( + // deps.api, + // &orderbook_pair, + // direction, + // base_amount_response.market_price, + // base_amount_response.expected_base_amount, + // )?; + + // if provided_asset.amount < expected_offer_amount { + // return Err(ContractError::AssetMismatch {}); + // } + + // // require minimum amount for quote asset + // if quote_asset.amount.lt(&orderbook_pair.min_quote_coin_amount) { + // return Err(ContractError::TooSmallQuoteAsset { + // quote_coin: quote_asset.info.to_string(), + // min_quote_amount: orderbook_pair.min_quote_coin_amount, + // }); + // } + + // // calculate refund_amount + // let refund_amount = provided_asset + // .amount + // .checked_sub(paid_assets[0].amount) + // .unwrap_or_default(); + + // // submit market order + // submit_market_order( + // deps, + // env.contract.address, + // &orderbook_pair, + // sender, + // direction, + // paid_assets, + // refund_amount, + // ) + // } Err(_) => Err(ContractError::InvalidCw20HookMessage {}), } } diff --git a/contracts/oraiswap_limit_order/src/testing/contract_test.rs b/contracts/oraiswap_limit_order/src/testing/contract_test.rs index beba13bf..bb5967d8 100644 --- a/contracts/oraiswap_limit_order/src/testing/contract_test.rs +++ b/contracts/oraiswap_limit_order/src/testing/contract_test.rs @@ -2223,620 +2223,620 @@ fn submit_order_with_spread_cw20_token() { assert_eq!(order_6.clone(), orders.orders[1]); } -#[test] -fn submit_market_order_native_token() { - let mut app = MockApp::new(&[ - ( - &"addr0000".to_string(), - &[ - Coin { - denom: ORAI_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - Coin { - denom: USDT_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - ], - ), - ( - &"addr0001".to_string(), - &[ - Coin { - denom: ORAI_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - Coin { - denom: USDT_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - ], - ), - ]); - - app.set_token_contract(Box::new(create_entry_points_testing!(oraiswap_token))); - - app.set_token_balances(&[( - &"asset".to_string(), - &[(&"addr0000".to_string(), &Uint128::from(1000000000u128))], - )]); - - let msg = InstantiateMsg { - name: None, - version: None, - admin: None, - commission_rate: None, - operator: None, - reward_address: REWARD_ADDR.to_string(), - }; - let code_id = app.upload(Box::new(create_entry_points_testing!(crate))); - let limit_order_addr = app - .instantiate( - code_id, - Addr::unchecked("addr0000"), - &msg, - &[], - "limit order", - ) - .unwrap(); - - // create order book for pair [orai, usdt] - let msg = ExecuteMsg::CreateOrderBookPair { - base_coin_info: AssetInfo::NativeToken { - denom: ORAI_DENOM.to_string(), - }, - quote_coin_info: AssetInfo::NativeToken { - denom: USDT_DENOM.to_string(), - }, - spread: Some(Decimal::percent(10)), - min_quote_coin_amount: Uint128::from(10u128), - }; - let _res = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[], - ) - .unwrap(); - - let mut base_amount = Uint128::from(1_000_000u128); - let mut quote_amount = Uint128::from(5_000_000u128); - let mut assets = [ - Asset { - info: AssetInfo::NativeToken { - denom: ORAI_DENOM.to_string(), - }, - amount: base_amount, - }, - Asset { - info: AssetInfo::NativeToken { - denom: USDT_DENOM.to_string(), - }, - amount: quote_amount, - }, - ]; - let asset_infos = assets.clone().map(|asset| asset.info); - - // CASE 1: submit first order on buy side => no check spread price, buy_price = 5 - let msg = ExecuteMsg::SubmitOrder { - direction: OrderDirection::Buy, - assets: assets.clone(), - }; - - let _ = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: assets[1].info.to_string(), - amount: quote_amount, - }], - ) - .unwrap(); - - // query buy ticks - buy side has one tick = 5 - let ticks = app - .query::( - limit_order_addr.clone(), - &QueryMsg::Ticks { - asset_infos: asset_infos.clone(), - direction: OrderDirection::Buy, - start_after: None, - end: None, - limit: None, - order_by: Some(1), - }, - ) - .unwrap(); - - // assert price - assert_eq!( - ticks.ticks[0].price, - Decimal::from_ratio(quote_amount, base_amount) - ); - - // query price with base_amount - let base_amount_res = app - .query::( - limit_order_addr.clone(), - &QueryMsg::PriceByBaseAmount { - asset_infos: asset_infos.clone(), - base_amount, - direction: OrderDirection::Buy, - slippage: None, - }, - ) - .unwrap(); - - // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 0 & BaseAmountResponse.expected_base_amount = 0 - assert_eq!( - base_amount_res, - BaseAmountResponse { - market_price: Decimal::zero(), - expected_base_amount: Uint128::zero() - } - ); - - // query price with base_amount - let base_amount_res = app - .query::( - limit_order_addr.clone(), - &QueryMsg::PriceByBaseAmount { - asset_infos: asset_infos.clone(), - base_amount, - direction: OrderDirection::Sell, - slippage: None, - }, - ) - .unwrap(); - - // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 5 & BaseAmountResponse.expected_base_amount = 100 - assert_eq!( - base_amount_res, - BaseAmountResponse { - market_price: Decimal::from_ratio(quote_amount, base_amount), - expected_base_amount: base_amount - } - ); - - // CASE 2: submit market sell order - let msg = ExecuteMsg::SubmitMarketOrder { - direction: OrderDirection::Sell, - asset_infos: asset_infos.clone(), - base_amount, - quote_amount, - slippage: None, - }; - - let res = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: asset_infos[0].to_string(), - amount: base_amount, - }], - ) - .unwrap(); - println!("matching result: {:?}", res); - - // CASE 3: failure case - slippage larger than 1 - let base_amount_3 = Uint128::from(2_000_000u128); - let quote_amount_3 = Uint128::from(base_amount_3 * base_amount_res.market_price); - let msg = ExecuteMsg::SubmitMarketOrder { - direction: OrderDirection::Buy, - asset_infos: asset_infos.clone(), - base_amount: base_amount_3, - quote_amount: quote_amount_3, - slippage: Some(Decimal::one()), - }; - - let res = app.execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: asset_infos[1].to_string(), - amount: quote_amount_3, - }], - ); - app.assert_fail(res); - - // CASE 4: buy market order with slippage = 0.005 - base_amount = Uint128::from(800_000u128); - quote_amount = Uint128::from(4_000_000u128); - assets[0].amount = base_amount; - assets[1].amount = quote_amount; - let msg = ExecuteMsg::SubmitOrder { - direction: OrderDirection::Sell, - assets: assets.clone(), - }; - - let _ = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: assets[0].info.to_string(), - amount: base_amount, - }], - ) - .unwrap(); - - let base_amount_4 = Uint128::from(999_123u128); - let slippage = Decimal::from_str("0.005").unwrap(); - let base_amount_res_4 = app - .query::( - limit_order_addr.clone(), - &QueryMsg::PriceByBaseAmount { - asset_infos: asset_infos.clone(), - base_amount: base_amount_4, - direction: OrderDirection::Buy, - slippage: Some(slippage), - }, - ) - .unwrap(); - assert_eq!( - base_amount_res_4, - BaseAmountResponse { - market_price: Decimal::from_ratio(quote_amount, base_amount) - .checked_mul(Decimal::one() + slippage) - .unwrap(), - expected_base_amount: base_amount - } - ); - - let quote_amount_4 = Uint128::from(base_amount_4 * base_amount_res_4.market_price); - let msg = ExecuteMsg::SubmitMarketOrder { - direction: OrderDirection::Buy, - asset_infos: asset_infos.clone(), - base_amount: base_amount_4, - quote_amount: quote_amount_4, - slippage: Some(slippage), - }; - - let res = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: asset_infos[1].to_string(), - amount: quote_amount_4.into(), - }], - ) - .unwrap(); - - println!("matching result: {:?}", res); -} - -#[test] -fn submit_market_order_cw20_token() { - let mut app = MockApp::new(&[ - ( - &"addr0000".to_string(), - &[ - Coin { - denom: ORAI_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - Coin { - denom: USDT_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - ], - ), - ( - &"addr0001".to_string(), - &[ - Coin { - denom: ORAI_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - Coin { - denom: USDT_DENOM.to_string(), - amount: Uint128::from(1000000000u128), - }, - ], - ), - ]); - - app.set_token_contract(Box::new(create_entry_points_testing!(oraiswap_token))); - - let usdt_token = app.set_token_balances(&[( - &"asset".to_string(), - &[ - (&"addr0000".to_string(), &Uint128::from(1000000000u128)), - (&"addr0001".to_string(), &Uint128::from(1000000000u128)), - ], - )]); - - let msg = InstantiateMsg { - name: None, - version: None, - admin: None, - commission_rate: None, - operator: None, - reward_address: REWARD_ADDR.to_string(), - }; - let code_id = app.upload(Box::new(create_entry_points_testing!(crate))); - let limit_order_addr = app - .instantiate( - code_id, - Addr::unchecked("addr0000"), - &msg, - &[], - "limit order", - ) - .unwrap(); - - // create order book for pair [orai, usdt_token] - let msg = ExecuteMsg::CreateOrderBookPair { - base_coin_info: AssetInfo::NativeToken { - denom: ORAI_DENOM.to_string(), - }, - quote_coin_info: AssetInfo::Token { - contract_addr: usdt_token[0].clone(), - }, - spread: Some(Decimal::percent(10)), - min_quote_coin_amount: Uint128::zero(), - }; - let _res = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[], - ) - .unwrap(); - - let mut base_amount = Uint128::from(1_000_000u128); - let mut quote_amount = Uint128::from(5_000_000u128); - let mut assets = [ - Asset { - info: AssetInfo::NativeToken { - denom: ORAI_DENOM.to_string(), - }, - amount: base_amount, - }, - Asset { - info: AssetInfo::Token { - contract_addr: usdt_token[0].clone(), - }, - amount: quote_amount, - }, - ]; - let asset_infos = assets.clone().map(|asset| asset.info); - - // CASE 1: submit first order on buy side => no check spread price, buy_price = 5 - let msg = cw20::Cw20ExecuteMsg::Send { - contract: limit_order_addr.to_string(), - amount: quote_amount, - msg: to_binary(&Cw20HookMsg::SubmitOrder { - direction: OrderDirection::Buy, - assets: assets.clone(), - }) - .unwrap(), - }; - - let _ = app - .execute( - Addr::unchecked("addr0000"), - usdt_token[0].clone(), - &msg, - &[], - ) - .unwrap(); - - // query buy ticks - buy side has one tick = 5 - let ticks = app - .query::( - limit_order_addr.clone(), - &QueryMsg::Ticks { - asset_infos: asset_infos.clone(), - direction: OrderDirection::Buy, - start_after: None, - end: None, - limit: None, - order_by: Some(1), - }, - ) - .unwrap(); - - // assert price - assert_eq!( - ticks.ticks[0].price, - Decimal::from_ratio(quote_amount, base_amount) - ); - - // query price with base_amount - let base_amount_res = app - .query::( - limit_order_addr.clone(), - &QueryMsg::PriceByBaseAmount { - asset_infos: asset_infos.clone(), - base_amount, - direction: OrderDirection::Buy, - slippage: None, - }, - ) - .unwrap(); - - // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 0 & BaseAmountResponse.expected_base_amount = 0 - assert_eq!( - base_amount_res, - BaseAmountResponse { - market_price: Decimal::zero(), - expected_base_amount: Uint128::zero() - } - ); - - // query price with base_amount - let base_amount_res = app - .query::( - limit_order_addr.clone(), - &QueryMsg::PriceByBaseAmount { - asset_infos: asset_infos.clone(), - base_amount, - direction: OrderDirection::Sell, - slippage: None, - }, - ) - .unwrap(); - - // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 5 & BaseAmountResponse.expected_base_amount = 100 - assert_eq!( - base_amount_res, - BaseAmountResponse { - market_price: Decimal::from_ratio(quote_amount, base_amount), - expected_base_amount: base_amount - } - ); - - // CASE 2: submit market sell order - let msg = ExecuteMsg::SubmitMarketOrder { - direction: OrderDirection::Sell, - asset_infos: asset_infos.clone(), - base_amount, - quote_amount, - slippage: None, - }; - - let res = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: asset_infos[0].to_string(), - amount: base_amount, - }], - ) - .unwrap(); - println!("matching result: {:?}", res); - - // CASE 3: failure case - slippage larger than 1 - let base_amount_3 = Uint128::from(2_000_000u128); - let quote_amount_3 = Uint128::from(base_amount_3 * base_amount_res.market_price); - let msg = cw20::Cw20ExecuteMsg::Send { - contract: limit_order_addr.to_string(), - amount: quote_amount, - msg: to_binary(&Cw20HookMsg::SubmitMarketOrder { - direction: OrderDirection::Buy, - asset_infos: asset_infos.clone(), - base_amount: base_amount_3, - quote_amount: quote_amount_3, - slippage: Some(Decimal::one()), - }) - .unwrap(), - }; - - let res = app.execute( - Addr::unchecked("addr0000"), - usdt_token[0].clone(), - &msg, - &[], - ); - app.assert_fail(res); - - // CASE 4: failure case - no buy depth - let base_amount_4 = Uint128::from(2_000_000u128); - let quote_amount_4 = Uint128::from(base_amount_3 * base_amount_res.market_price); - let msg = cw20::Cw20ExecuteMsg::Send { - contract: limit_order_addr.to_string(), - amount: quote_amount, - msg: to_binary(&Cw20HookMsg::SubmitMarketOrder { - direction: OrderDirection::Buy, - asset_infos: asset_infos.clone(), - base_amount: base_amount_4, - quote_amount: quote_amount_4, - slippage: None, - }) - .unwrap(), - }; - - let res = app.execute( - Addr::unchecked("addr0000"), - usdt_token[0].clone(), - &msg, - &[], - ); - app.assert_fail(res); - - // CASE 5: buy market order with slippage = 0.005 - base_amount = Uint128::from(800_000u128); - quote_amount = Uint128::from(4_000_000u128); - assets[0].amount = base_amount; - assets[1].amount = quote_amount; - let msg = ExecuteMsg::SubmitOrder { - direction: OrderDirection::Sell, - assets: assets.clone(), - }; - - let _ = app - .execute( - Addr::unchecked("addr0000"), - limit_order_addr.clone(), - &msg, - &[Coin { - denom: assets[0].info.to_string(), - amount: base_amount, - }], - ) - .unwrap(); - - let base_amount_5 = Uint128::from(999_123u128); - let slippage = Decimal::from_str("0.005").unwrap(); - let base_amount_res_5 = app - .query::( - limit_order_addr.clone(), - &QueryMsg::PriceByBaseAmount { - asset_infos: asset_infos.clone(), - base_amount: base_amount_5, - direction: OrderDirection::Buy, - slippage: Some(slippage), - }, - ) - .unwrap(); - assert_eq!( - base_amount_res_5, - BaseAmountResponse { - market_price: Decimal::from_ratio(quote_amount, base_amount) - .checked_mul(Decimal::one() + slippage) - .unwrap(), - expected_base_amount: base_amount - } - ); - - let quote_amount_5 = Uint128::from(base_amount_5 * base_amount_res_5.market_price); - let msg = cw20::Cw20ExecuteMsg::Send { - contract: limit_order_addr.to_string(), - amount: quote_amount_5, - msg: to_binary(&Cw20HookMsg::SubmitMarketOrder { - direction: OrderDirection::Buy, - asset_infos: asset_infos.clone(), - base_amount: base_amount_5, - quote_amount: quote_amount_5, - slippage: Some(slippage), - }) - .unwrap(), - }; - - let res = app - .execute( - Addr::unchecked("addr0000"), - usdt_token[0].clone(), - &msg, - &[], - ) - .unwrap(); - - println!("matching result: {:?}", res); -} +// #[test] +// fn submit_market_order_native_token() { +// let mut app = MockApp::new(&[ +// ( +// &"addr0000".to_string(), +// &[ +// Coin { +// denom: ORAI_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// Coin { +// denom: USDT_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// ], +// ), +// ( +// &"addr0001".to_string(), +// &[ +// Coin { +// denom: ORAI_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// Coin { +// denom: USDT_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// ], +// ), +// ]); + +// app.set_token_contract(Box::new(create_entry_points_testing!(oraiswap_token))); + +// app.set_token_balances(&[( +// &"asset".to_string(), +// &[(&"addr0000".to_string(), &Uint128::from(1000000000u128))], +// )]); + +// let msg = InstantiateMsg { +// name: None, +// version: None, +// admin: None, +// commission_rate: None, +// operator: None, +// reward_address: REWARD_ADDR.to_string(), +// }; +// let code_id = app.upload(Box::new(create_entry_points_testing!(crate))); +// let limit_order_addr = app +// .instantiate( +// code_id, +// Addr::unchecked("addr0000"), +// &msg, +// &[], +// "limit order", +// ) +// .unwrap(); + +// // create order book for pair [orai, usdt] +// let msg = ExecuteMsg::CreateOrderBookPair { +// base_coin_info: AssetInfo::NativeToken { +// denom: ORAI_DENOM.to_string(), +// }, +// quote_coin_info: AssetInfo::NativeToken { +// denom: USDT_DENOM.to_string(), +// }, +// spread: Some(Decimal::percent(10)), +// min_quote_coin_amount: Uint128::from(10u128), +// }; +// let _res = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[], +// ) +// .unwrap(); + +// let mut base_amount = Uint128::from(1_000_000u128); +// let mut quote_amount = Uint128::from(5_000_000u128); +// let mut assets = [ +// Asset { +// info: AssetInfo::NativeToken { +// denom: ORAI_DENOM.to_string(), +// }, +// amount: base_amount, +// }, +// Asset { +// info: AssetInfo::NativeToken { +// denom: USDT_DENOM.to_string(), +// }, +// amount: quote_amount, +// }, +// ]; +// let asset_infos = assets.clone().map(|asset| asset.info); + +// // CASE 1: submit first order on buy side => no check spread price, buy_price = 5 +// let msg = ExecuteMsg::SubmitOrder { +// direction: OrderDirection::Buy, +// assets: assets.clone(), +// }; + +// let _ = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: assets[1].info.to_string(), +// amount: quote_amount, +// }], +// ) +// .unwrap(); + +// // query buy ticks - buy side has one tick = 5 +// let ticks = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::Ticks { +// asset_infos: asset_infos.clone(), +// direction: OrderDirection::Buy, +// start_after: None, +// end: None, +// limit: None, +// order_by: Some(1), +// }, +// ) +// .unwrap(); + +// // assert price +// assert_eq!( +// ticks.ticks[0].price, +// Decimal::from_ratio(quote_amount, base_amount) +// ); + +// // query price with base_amount +// let base_amount_res = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::PriceByBaseAmount { +// asset_infos: asset_infos.clone(), +// base_amount, +// direction: OrderDirection::Buy, +// slippage: None, +// }, +// ) +// .unwrap(); + +// // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 0 & BaseAmountResponse.expected_base_amount = 0 +// assert_eq!( +// base_amount_res, +// BaseAmountResponse { +// market_price: Decimal::zero(), +// expected_base_amount: Uint128::zero() +// } +// ); + +// // query price with base_amount +// let base_amount_res = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::PriceByBaseAmount { +// asset_infos: asset_infos.clone(), +// base_amount, +// direction: OrderDirection::Sell, +// slippage: None, +// }, +// ) +// .unwrap(); + +// // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 5 & BaseAmountResponse.expected_base_amount = 100 +// assert_eq!( +// base_amount_res, +// BaseAmountResponse { +// market_price: Decimal::from_ratio(quote_amount, base_amount), +// expected_base_amount: base_amount +// } +// ); + +// // CASE 2: submit market sell order +// let msg = ExecuteMsg::SubmitMarketOrder { +// direction: OrderDirection::Sell, +// asset_infos: asset_infos.clone(), +// base_amount, +// quote_amount, +// slippage: None, +// }; + +// let res = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: asset_infos[0].to_string(), +// amount: base_amount, +// }], +// ) +// .unwrap(); +// println!("matching result: {:?}", res); + +// // CASE 3: failure case - slippage larger than 1 +// let base_amount_3 = Uint128::from(2_000_000u128); +// let quote_amount_3 = Uint128::from(base_amount_3 * base_amount_res.market_price); +// let msg = ExecuteMsg::SubmitMarketOrder { +// direction: OrderDirection::Buy, +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_3, +// quote_amount: quote_amount_3, +// slippage: Some(Decimal::one()), +// }; + +// let res = app.execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: asset_infos[1].to_string(), +// amount: quote_amount_3, +// }], +// ); +// app.assert_fail(res); + +// // CASE 4: buy market order with slippage = 0.005 +// base_amount = Uint128::from(800_000u128); +// quote_amount = Uint128::from(4_000_000u128); +// assets[0].amount = base_amount; +// assets[1].amount = quote_amount; +// let msg = ExecuteMsg::SubmitOrder { +// direction: OrderDirection::Sell, +// assets: assets.clone(), +// }; + +// let _ = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: assets[0].info.to_string(), +// amount: base_amount, +// }], +// ) +// .unwrap(); + +// let base_amount_4 = Uint128::from(999_123u128); +// let slippage = Decimal::from_str("0.005").unwrap(); +// let base_amount_res_4 = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::PriceByBaseAmount { +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_4, +// direction: OrderDirection::Buy, +// slippage: Some(slippage), +// }, +// ) +// .unwrap(); +// assert_eq!( +// base_amount_res_4, +// BaseAmountResponse { +// market_price: Decimal::from_ratio(quote_amount, base_amount) +// .checked_mul(Decimal::one() + slippage) +// .unwrap(), +// expected_base_amount: base_amount +// } +// ); + +// let quote_amount_4 = Uint128::from(base_amount_4 * base_amount_res_4.market_price); +// let msg = ExecuteMsg::SubmitMarketOrder { +// direction: OrderDirection::Buy, +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_4, +// quote_amount: quote_amount_4, +// slippage: Some(slippage), +// }; + +// let res = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: asset_infos[1].to_string(), +// amount: quote_amount_4.into(), +// }], +// ) +// .unwrap(); + +// println!("matching result: {:?}", res); +// } + +// #[test] +// fn submit_market_order_cw20_token() { +// let mut app = MockApp::new(&[ +// ( +// &"addr0000".to_string(), +// &[ +// Coin { +// denom: ORAI_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// Coin { +// denom: USDT_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// ], +// ), +// ( +// &"addr0001".to_string(), +// &[ +// Coin { +// denom: ORAI_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// Coin { +// denom: USDT_DENOM.to_string(), +// amount: Uint128::from(1000000000u128), +// }, +// ], +// ), +// ]); + +// app.set_token_contract(Box::new(create_entry_points_testing!(oraiswap_token))); + +// let usdt_token = app.set_token_balances(&[( +// &"asset".to_string(), +// &[ +// (&"addr0000".to_string(), &Uint128::from(1000000000u128)), +// (&"addr0001".to_string(), &Uint128::from(1000000000u128)), +// ], +// )]); + +// let msg = InstantiateMsg { +// name: None, +// version: None, +// admin: None, +// commission_rate: None, +// operator: None, +// reward_address: REWARD_ADDR.to_string(), +// }; +// let code_id = app.upload(Box::new(create_entry_points_testing!(crate))); +// let limit_order_addr = app +// .instantiate( +// code_id, +// Addr::unchecked("addr0000"), +// &msg, +// &[], +// "limit order", +// ) +// .unwrap(); + +// // create order book for pair [orai, usdt_token] +// let msg = ExecuteMsg::CreateOrderBookPair { +// base_coin_info: AssetInfo::NativeToken { +// denom: ORAI_DENOM.to_string(), +// }, +// quote_coin_info: AssetInfo::Token { +// contract_addr: usdt_token[0].clone(), +// }, +// spread: Some(Decimal::percent(10)), +// min_quote_coin_amount: Uint128::zero(), +// }; +// let _res = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[], +// ) +// .unwrap(); + +// let mut base_amount = Uint128::from(1_000_000u128); +// let mut quote_amount = Uint128::from(5_000_000u128); +// let mut assets = [ +// Asset { +// info: AssetInfo::NativeToken { +// denom: ORAI_DENOM.to_string(), +// }, +// amount: base_amount, +// }, +// Asset { +// info: AssetInfo::Token { +// contract_addr: usdt_token[0].clone(), +// }, +// amount: quote_amount, +// }, +// ]; +// let asset_infos = assets.clone().map(|asset| asset.info); + +// // CASE 1: submit first order on buy side => no check spread price, buy_price = 5 +// let msg = cw20::Cw20ExecuteMsg::Send { +// contract: limit_order_addr.to_string(), +// amount: quote_amount, +// msg: to_binary(&Cw20HookMsg::SubmitOrder { +// direction: OrderDirection::Buy, +// assets: assets.clone(), +// }) +// .unwrap(), +// }; + +// let _ = app +// .execute( +// Addr::unchecked("addr0000"), +// usdt_token[0].clone(), +// &msg, +// &[], +// ) +// .unwrap(); + +// // query buy ticks - buy side has one tick = 5 +// let ticks = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::Ticks { +// asset_infos: asset_infos.clone(), +// direction: OrderDirection::Buy, +// start_after: None, +// end: None, +// limit: None, +// order_by: Some(1), +// }, +// ) +// .unwrap(); + +// // assert price +// assert_eq!( +// ticks.ticks[0].price, +// Decimal::from_ratio(quote_amount, base_amount) +// ); + +// // query price with base_amount +// let base_amount_res = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::PriceByBaseAmount { +// asset_infos: asset_infos.clone(), +// base_amount, +// direction: OrderDirection::Buy, +// slippage: None, +// }, +// ) +// .unwrap(); + +// // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 0 & BaseAmountResponse.expected_base_amount = 0 +// assert_eq!( +// base_amount_res, +// BaseAmountResponse { +// market_price: Decimal::zero(), +// expected_base_amount: Uint128::zero() +// } +// ); + +// // query price with base_amount +// let base_amount_res = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::PriceByBaseAmount { +// asset_infos: asset_infos.clone(), +// base_amount, +// direction: OrderDirection::Sell, +// slippage: None, +// }, +// ) +// .unwrap(); + +// // Order book has 1 limit buy order -> BaseAmountResponse.market_price = 5 & BaseAmountResponse.expected_base_amount = 100 +// assert_eq!( +// base_amount_res, +// BaseAmountResponse { +// market_price: Decimal::from_ratio(quote_amount, base_amount), +// expected_base_amount: base_amount +// } +// ); + +// // CASE 2: submit market sell order +// let msg = ExecuteMsg::SubmitMarketOrder { +// direction: OrderDirection::Sell, +// asset_infos: asset_infos.clone(), +// base_amount, +// quote_amount, +// slippage: None, +// }; + +// let res = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: asset_infos[0].to_string(), +// amount: base_amount, +// }], +// ) +// .unwrap(); +// println!("matching result: {:?}", res); + +// // CASE 3: failure case - slippage larger than 1 +// let base_amount_3 = Uint128::from(2_000_000u128); +// let quote_amount_3 = Uint128::from(base_amount_3 * base_amount_res.market_price); +// let msg = cw20::Cw20ExecuteMsg::Send { +// contract: limit_order_addr.to_string(), +// amount: quote_amount, +// msg: to_binary(&Cw20HookMsg::SubmitMarketOrder { +// direction: OrderDirection::Buy, +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_3, +// quote_amount: quote_amount_3, +// slippage: Some(Decimal::one()), +// }) +// .unwrap(), +// }; + +// let res = app.execute( +// Addr::unchecked("addr0000"), +// usdt_token[0].clone(), +// &msg, +// &[], +// ); +// app.assert_fail(res); + +// // CASE 4: failure case - no buy depth +// let base_amount_4 = Uint128::from(2_000_000u128); +// let quote_amount_4 = Uint128::from(base_amount_3 * base_amount_res.market_price); +// let msg = cw20::Cw20ExecuteMsg::Send { +// contract: limit_order_addr.to_string(), +// amount: quote_amount, +// msg: to_binary(&Cw20HookMsg::SubmitMarketOrder { +// direction: OrderDirection::Buy, +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_4, +// quote_amount: quote_amount_4, +// slippage: None, +// }) +// .unwrap(), +// }; + +// let res = app.execute( +// Addr::unchecked("addr0000"), +// usdt_token[0].clone(), +// &msg, +// &[], +// ); +// app.assert_fail(res); + +// // CASE 5: buy market order with slippage = 0.005 +// base_amount = Uint128::from(800_000u128); +// quote_amount = Uint128::from(4_000_000u128); +// assets[0].amount = base_amount; +// assets[1].amount = quote_amount; +// let msg = ExecuteMsg::SubmitOrder { +// direction: OrderDirection::Sell, +// assets: assets.clone(), +// }; + +// let _ = app +// .execute( +// Addr::unchecked("addr0000"), +// limit_order_addr.clone(), +// &msg, +// &[Coin { +// denom: assets[0].info.to_string(), +// amount: base_amount, +// }], +// ) +// .unwrap(); + +// let base_amount_5 = Uint128::from(999_123u128); +// let slippage = Decimal::from_str("0.005").unwrap(); +// let base_amount_res_5 = app +// .query::( +// limit_order_addr.clone(), +// &QueryMsg::PriceByBaseAmount { +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_5, +// direction: OrderDirection::Buy, +// slippage: Some(slippage), +// }, +// ) +// .unwrap(); +// assert_eq!( +// base_amount_res_5, +// BaseAmountResponse { +// market_price: Decimal::from_ratio(quote_amount, base_amount) +// .checked_mul(Decimal::one() + slippage) +// .unwrap(), +// expected_base_amount: base_amount +// } +// ); + +// let quote_amount_5 = Uint128::from(base_amount_5 * base_amount_res_5.market_price); +// let msg = cw20::Cw20ExecuteMsg::Send { +// contract: limit_order_addr.to_string(), +// amount: quote_amount_5, +// msg: to_binary(&Cw20HookMsg::SubmitMarketOrder { +// direction: OrderDirection::Buy, +// asset_infos: asset_infos.clone(), +// base_amount: base_amount_5, +// quote_amount: quote_amount_5, +// slippage: Some(slippage), +// }) +// .unwrap(), +// }; + +// let res = app +// .execute( +// Addr::unchecked("addr0000"), +// usdt_token[0].clone(), +// &msg, +// &[], +// ) +// .unwrap(); + +// println!("matching result: {:?}", res); +// } #[test] fn cancel_order_native_token() { diff --git a/packages/oraiswap/src/limit_order.rs b/packages/oraiswap/src/limit_order.rs index 997351cf..7bd338a0 100644 --- a/packages/oraiswap/src/limit_order.rs +++ b/packages/oraiswap/src/limit_order.rs @@ -103,16 +103,16 @@ pub enum ExecuteMsg { assets: [Asset; 2], }, - /////////////////////// - /// User Operations /// - /////////////////////// - SubmitMarketOrder { - direction: OrderDirection, // default is buy, with sell then it is reversed - asset_infos: [AssetInfo; 2], - base_amount: Uint128, - quote_amount: Uint128, - slippage: Option, - }, + // /////////////////////// + // /// User Operations /// + // /////////////////////// + // SubmitMarketOrder { + // direction: OrderDirection, // default is buy, with sell then it is reversed + // asset_infos: [AssetInfo; 2], + // base_amount: Uint128, + // quote_amount: Uint128, + // slippage: Option, + // }, CancelOrder { order_id: u64, @@ -141,13 +141,13 @@ pub enum Cw20HookMsg { direction: OrderDirection, assets: [Asset; 2], }, - SubmitMarketOrder { - direction: OrderDirection, // default is buy, with sell then it is reversed - asset_infos: [AssetInfo; 2], - base_amount: Uint128, - quote_amount: Uint128, - slippage: Option, - }, + // SubmitMarketOrder { + // direction: OrderDirection, // default is buy, with sell then it is reversed + // asset_infos: [AssetInfo; 2], + // base_amount: Uint128, + // quote_amount: Uint128, + // slippage: Option, + // }, } #[cw_serde]