diff --git a/README.md b/README.md index 85d8f92..668a7db 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,17 @@ A spot order is fulfilled when the specified price set by the trader aligns with - **Stop Loss Functionality**: Enable users to set automated orders that trigger when the asset's price reaches a specified lower limit, minimizing potential losses. - **Limit Sell Functionality**: Allow users to set automated orders that execute when the asset's price reaches a specified upper limit, securing profits. -- **Limit Buy Fuctionality**: Allow user to set automated orders that execute when the limit price is reaches, securing profits. +- **Limit Buy Fuctionality**: Allow users to set automated orders that execute when the limit price is reaches, securing profits. - **Market Buy Fuctionality**: Allow users to set automated orders that will execute at market price ### Margin Order A margin order involves trading an asset using funds provided by a third party. It allows users to trade larger positions with a smaller initial capital outlay. -- **Long Position Functionality**: Allows users to borrow an asset to sell it, hoping to repurchase it later at a lower price, thus profiting from the difference. -- **Short Position Functionality**: Allows users to borrow an asset, selling it with the expectation of buying it back at a lower price to repay the loan and pocket the difference. -- **Unspecified Position Functionality**: A neutral or unspecified position in a margin order, potentially indicating no active trading or a position that isn't explicitly long or short. +- **Stop Loss Functionality**: Enable users to set automated orders that trigger when the asset's price reaches a specified lower limit, minimizing potential losses. +- **Limit Sell Functionality**: Allow users to set automated orders that execute when the asset's price reaches a specified upper limit, securing profits. +- **Limit Buy Fuctionality**: Allow users to set automated orders that execute when the limit price is reaches, securing profits. +- **Market Buy Fuctionality**: Allow users to set automated orders that will execute at market price ## Getting Started diff --git a/front_end_script/README.md b/front_end_script/README.md index 68edd86..96072b9 100644 --- a/front_end_script/README.md +++ b/front_end_script/README.md @@ -152,7 +152,7 @@ getSpotOrders( ); ``` -### 6. createMarginOrder(position, collateral, leverage, borrow_asset, take_profit_price) +### 6. createMarginOrder(position, collateral, leverage, borrow_asset, take_profit_price, order_type, trigger_price) This function allows you to create a margin order by sending a transaction to the CosmWasm contract. @@ -163,6 +163,8 @@ This function allows you to create a margin order by sending a transaction to th - `leverage` (String): The leverage for the margin order. - `borrow_asset` (String): The asset to borrow for the margin order. - `take_profit_price` (String): The price at which the order will take profit. +- `order_type` (String): The type of the order (e.g., "stop_loss", "limit_sell", "limit_buy"). +- `trigger_price` ({`base_denom`:String, `quote_denom`:String, `rate` :String}): Price relates two assets exchange rate that the user should define #### Usage @@ -172,7 +174,9 @@ createMarginOrder( "collateral", "leverage_value", "borrow_asset", - "take_profit_price" + "take_profit_price", + "order_type", + "trigger_price" ); ``` @@ -185,6 +189,8 @@ createMarginOrder( "4.3", "ueth", "2.2" + "limit_buy", + { base_denom: "ueth", quote_denom: "uusdc", rate: "2076.5" } ); ``` @@ -208,7 +214,7 @@ cancelMarginOrder("your_order_id_here"); cancelMarginOrder("1"); ``` -### 8. getMarginOrder(address,id) +### 8. getMarginOrder(id) This function retrieves information about a specific margin order by querying a CosmWasm contract on the blockchain. @@ -226,10 +232,31 @@ getMarginOrder("your_order_id_here"); #### Exemple ```js -getMarginOrder("255"); +getMarginOrder("2"); +``` + +### 9. getMarginPosition(address,id) + +This function retrieves information about a specific margin order by querying a CosmWasm contract on the blockchain. + +#### Parameters + +- `address` (String): The address associated with the margin order. +- `order_id` (String): The unique identifier for the order you want to retrieve. + +#### Usage + +```javascript +getMarginPosition("your_address", "your_order_id_here"); +``` + +#### Exemple + +```js +getMarginPosition("elys1x5fehwug2vtkyn4vpunwkfn9zxkpxl8jg0lwuu", "255"); ``` -### 9. getMarginOrders(pagination) +### 10. getMarginPositions(pagination) This function retrieves multiple margin orders by querying a CosmWasm contract on the blockchain. @@ -240,16 +267,16 @@ This function retrieves multiple margin orders by querying a CosmWasm contract o #### Usage ```javascript -getMarginOrders("pagination"); +getMarginPositions("pagination"); ``` #### Exemple ```js -getMarginOrders({ count_total: true, limit: 10, reverse: false, key: null }); +getMarginPositions({ count_total: true, limit: 10, reverse: false, key: null }); ``` -### 10. SwapEstimationByDenom(amount, denom_in, denom_out) +### 11. SwapEstimationByDenom(amount, denom_in, denom_out) This function retrieves an estimation of the value obtained by swapping one asset for another. diff --git a/front_end_script/upload.js b/front_end_script/upload.js index 0aedaa1..f108978 100644 --- a/front_end_script/upload.js +++ b/front_end_script/upload.js @@ -180,3 +180,126 @@ async function SwapEstimationByDenom(amount, denom_in, denom_out) { ); console.log(`Result: `, result); } + +async function createMarginOrder( + position_type, + collateral, + leverage_value, + borrow_asset, + take_profit_price, + order_type, + trigger_price +) { + const gasPrice = GasPrice.fromString(GASPRICE); + const sender_wallet = await DirectSecp256k1HdWallet.fromMnemonic( + sender.mnemonic, + { prefix: "elys" } + ); + const sender_client = await SigningCosmWasmClient.connectWithSigner( + rpcEndpoint, + sender_wallet + ); + const executeFee = calculateFee(300_000, gasPrice); + const msg = { + create_margin_order: { + position_type: position_type, + collateral: collateral, + leverage_value: leverage_value, + borrow_asset: borrow_asset, + take_profit_price: take_profit_price, + order_type: order_type, + trigger_price: trigger_price, + }, + }; + + const create_margin_order_res = await sender_client.execute( + sender.address, + trade_shield_contract_addr, + msg, + executeFee, + "", + coins(amount_send, denom_send) + ); + console.log("create_margin_order_res:", create_margin_order_res); +} + +async function cancelMarginOrder(order_id) { + const gasPrice = GasPrice.fromString(GASPRICE); + const sender_wallet = await DirectSecp256k1HdWallet.fromMnemonic( + sender.mnemonic, + { prefix: "elys" } + ); + const sender_client = await SigningCosmWasmClient.connectWithSigner( + rpcEndpoint, + sender_wallet + ); + const executeFee = calculateFee(300_000, gasPrice); + const msg = { + cancel_margin_order: { + order_id: order_id, + }, + }; + + const create_margin_order_res = await sender_client.execute( + sender.address, + trade_shield_contract_addr, + msg, + executeFee, + "" + ); + console.log("create_margin_order_res:", create_margin_order_res); +} + +async function getMarginOrder(order_id) { + const sender_wallet = await DirectSecp256k1HdWallet.fromMnemonic( + sender.mnemonic, + { prefix: "elys" } + ); + const sender_client = await SigningCosmWasmClient.connectWithSigner( + rpcEndpoint, + sender_wallet + ); + const result = await sender_client.queryContractSmart( + trade_shield_contract_addr, + { + get_margin_order: { order_id: order_id }, + } + ); + console.log(`Result: `, result); +} + +async function getMarginPosition(id, address) { + const sender_wallet = await DirectSecp256k1HdWallet.fromMnemonic( + sender.mnemonic, + { prefix: "elys" } + ); + const sender_client = await SigningCosmWasmClient.connectWithSigner( + rpcEndpoint, + sender_wallet + ); + const result = await sender_client.queryContractSmart( + trade_shield_contract_addr, + { + get_margin_position: { id: id, address: address }, + } + ); + console.log(`Result: `, result); +} + +async function getMarginPositions(pagination) { + const sender_wallet = await DirectSecp256k1HdWallet.fromMnemonic( + sender.mnemonic, + { prefix: "elys" } + ); + const sender_client = await SigningCosmWasmClient.connectWithSigner( + rpcEndpoint, + sender_wallet + ); + const result = await sender_client.queryContractSmart( + trade_shield_contract_addr, + { + get_margin_positions: { pagination: pagination }, + } + ); + console.log(`Result: `, result); +} diff --git a/src/action/execute/cancel_margin_order.rs b/src/action/execute/cancel_margin_order.rs index 358f2ea..76399fc 100644 --- a/src/action/execute/cancel_margin_order.rs +++ b/src/action/execute/cancel_margin_order.rs @@ -1,7 +1,3 @@ -use cosmwasm_std::{to_json_binary, SubMsg}; - -use crate::msg::ReplyType; - use super::*; pub fn cancel_margin_order( @@ -16,34 +12,28 @@ pub fn cancel_margin_order( None => return Err(ContractError::OrderNotFound { order_id }), }; - if order.creator == info.sender.to_string() { + if order.owner != info.sender.to_string() { return Err(ContractError::Unauthorized { sender: info.sender, }); } - let meta_data = Some(to_json_binary(&order_id)?); - - let cancel_msg = ElysMsg::margin_close_position(order.creator, order_id); - - let mut reply_infos = REPLY_INFO.load(deps.storage)?; - - let reply_info_id = - if let Some(reply_info) = reply_infos.iter().max_by_key(|reply_info| reply_info.id) { - reply_info.id + 1 - } else { - 0 - }; + let orders: Vec = orders + .iter() + .filter(|order| order.order_id != order_id) + .cloned() + .collect(); - let reply_info = ReplyInfo { - id: reply_info_id, - reply_type: ReplyType::MarginClosePosition, - data: meta_data, + let refund_msg = BankMsg::Send { + to_address: order.owner, + amount: vec![order.collateral], }; - reply_infos.push(reply_info); + let resp = Response::new() + .add_message(CosmosMsg::Bank(refund_msg)) + .add_attribute("order_id", order.order_id.to_string()); - let resp = Response::new().add_submessage(SubMsg::reply_always(cancel_msg, reply_info_id)); + MARGIN_ORDER.save(deps.storage, &orders)?; Ok(resp) } diff --git a/src/action/execute/cancel_spot_orders.rs b/src/action/execute/cancel_spot_orders.rs index 5d34ae7..3381d84 100644 --- a/src/action/execute/cancel_spot_orders.rs +++ b/src/action/execute/cancel_spot_orders.rs @@ -7,7 +7,7 @@ pub fn cancel_spot_orders( deps: DepsMut, order_ids: Option>, owner_address: String, - order_type: Option, + order_type: Option, ) -> Result, ContractError> { if info.sender.as_str() != owner_address { return Err(ContractError::Unauthorized { @@ -95,7 +95,7 @@ fn filter_order_by_id( fn filter_order_by_type( orders: Vec, - order_type: Option, + order_type: Option, ) -> Result, ContractError> { let order_type = match order_type { Some(order_type) => order_type, diff --git a/src/action/execute/create_margin_order.rs b/src/action/execute/create_margin_order.rs index 0c03648..12ce51f 100644 --- a/src/action/execute/create_margin_order.rs +++ b/src/action/execute/create_margin_order.rs @@ -1,17 +1,18 @@ -use cosmwasm_std::{to_json_binary, Coin, Decimal, Int128, StdError, SubMsg}; - -use crate::msg::ReplyType; - use super::*; +use crate::msg::ReplyType; +use cosmwasm_std::{Coin, Decimal, Int128, StdError, StdResult, Storage, SubMsg}; pub fn create_margin_order( info: MessageInfo, deps: DepsMut, + env: Env, position: MarginPosition, collateral: Coin, leverage: Decimal, borrow_asset: String, take_profit_price: Decimal, + order_type: OrderType, + trigger_price: OrderPrice, ) -> Result, ContractError> { if info.funds.len() != 1 { return Err(ContractError::CoinNumber); @@ -21,52 +22,76 @@ pub fn create_margin_order( return Err(ContractError::CollateralAmount); } - if position == MarginPosition::Short && collateral.denom == "uusdc" { + if position == MarginPosition::Short && collateral.denom != "uusdc" { return Err( - StdError::generic_err("the collateral asset for a short can only be USDC").into(), + StdError::generic_err("the collateral asset for a short can only be UUSDC").into(), ); } cw_utils::must_pay(&info, &info.funds[0].denom)?; - let borrow_token = Coin { - denom: borrow_asset.clone(), - amount: (leverage - Decimal::one()) * collateral.amount, - }; + let mut order_vec = MARGIN_ORDER.load(deps.storage)?; - let meta_data = to_json_binary(&MarginOrder::new( - position.clone(), + let order = MarginOrder::new( + &position, + &collateral, + borrow_asset, &info.sender, - collateral.clone(), - leverage, - borrow_token, - take_profit_price, - ))?; - - let sub_msg = ElysMsg::margin_open_position( - &info.sender, - &collateral.denom, - Int128::from(collateral.amount.u128() as i128), - &borrow_asset, - position, - leverage, - take_profit_price, + &leverage, + &take_profit_price, + &order_type, + &trigger_price, + &order_vec, ); - let mut reply_info = REPLY_INFO.load(deps.storage)?; + let resp = create_response(deps.storage, &order, env.contract.address)?; + + if order.order_type != OrderType::MarketBuy { + order_vec.push(order); + + MARGIN_ORDER.save(deps.storage, &order_vec)?; + } + + Ok(resp) +} + +fn create_response( + storage: &mut dyn Storage, + order: &MarginOrder, + contract_addr: impl Into, +) -> StdResult> { + if order.order_type != OrderType::MarketBuy { + return Ok(Response::new().add_attribute("order_id", order.order_id.to_string())); + } - let new_info_id = match reply_info.iter().max_by_key(|info| info.id) { - Some(max_info) => max_info.id + 1, + let mut reply_infos = REPLY_INFO.load(storage)?; + + let reply_info_id = match reply_infos.iter().max_by_key(|info| info.id) { + Some(info) => info.id + 1, None => 0, }; - reply_info.push(ReplyInfo { - id: new_info_id, - reply_type: ReplyType::MarginOpenPosition, - data: Some(meta_data), - }); + let reply_info = ReplyInfo { + id: reply_info_id, + reply_type: ReplyType::MarginBrokerOpenMarketBuy, + data: None, + }; + + reply_infos.push(reply_info); - REPLY_INFO.save(deps.storage, &reply_info)?; + let submsg: SubMsg = SubMsg::reply_always( + ElysMsg::margin_broker_open_position( + contract_addr, + &order.collateral.denom, + Int128::new(order.collateral.amount.u128() as i128), + &order.borrow_asset, + order.position.clone() as i32, + order.leverage, + order.take_profit_price, + &order.owner, + ), + reply_info_id, + ); - Ok(Response::new().add_submessage(SubMsg::reply_always(sub_msg, new_info_id))) + Ok(Response::new().add_submessage(submsg)) } diff --git a/src/action/execute/create_spot_order.rs b/src/action/execute/create_spot_order.rs index da9af6e..c73637c 100644 --- a/src/action/execute/create_spot_order.rs +++ b/src/action/execute/create_spot_order.rs @@ -9,10 +9,10 @@ pub fn create_spot_order( env: Env, deps: DepsMut, info: MessageInfo, - order_type: SpotOrderType, + order_type: OrderType, order_source_denom: String, order_target_denom: String, - order_price: SpotOrderPrice, + order_price: OrderPrice, order_amm_routes: Option>, ) -> Result, ContractError> { if info.funds.len() != 1 { @@ -74,8 +74,8 @@ pub fn create_spot_order( fn check_denom_error( order_source_denom: &str, order_target_denom: &str, - order_price: &SpotOrderPrice, - order_type: &SpotOrderType, + order_price: &OrderPrice, + order_type: &OrderType, funds_send_denom: &str, ) -> Result<(), ContractError> { if order_source_denom != funds_send_denom { @@ -86,7 +86,7 @@ fn check_denom_error( return Err(ContractError::SpotOrderSameDenom); } - if order_type == &SpotOrderType::MarketBuy { + if order_type == &OrderType::MarketBuy { return Ok(()); } @@ -95,7 +95,7 @@ fn check_denom_error( || (order_price.quote_denom != order_source_denom && order_price.quote_denom != order_target_denom) { - return Err(ContractError::SpotOrderPriceDenom); + return Err(ContractError::OrderPriceDenom); } Ok(()) @@ -111,7 +111,7 @@ fn create_resp( .add_attribute("order_id", new_order.order_id.to_string()) .add_message(bank_msg); // information message - if new_order.order_type != SpotOrderType::MarketBuy { + if new_order.order_type != OrderType::MarketBuy { return Ok(resp); } diff --git a/src/action/execute/process_spot_orders.rs b/src/action/execute/process_spot_orders.rs index 67c9c9b..cd56175 100644 --- a/src/action/execute/process_spot_orders.rs +++ b/src/action/execute/process_spot_orders.rs @@ -70,7 +70,7 @@ fn send_token( } fn check_order(order: &SpotOrder, querier: &ElysQuerier) -> bool { - if order.order_type == SpotOrderType::MarketBuy { + if order.order_type == OrderType::MarketBuy { return true; } @@ -88,11 +88,11 @@ fn check_order(order: &SpotOrder, querier: &ElysQuerier) -> bool { let order_token_out = order_spot_price * order.order_amount.amount; match order.order_type { - SpotOrderType::LimitBuy => order_token_out <= amm_swap_estimation.token_out.amount, + OrderType::LimitBuy => order_token_out <= amm_swap_estimation.token_out.amount, - SpotOrderType::LimitSell => order_token_out <= amm_swap_estimation.token_out.amount, + OrderType::LimitSell => order_token_out <= amm_swap_estimation.token_out.amount, - SpotOrderType::StopLoss => order_token_out >= amm_swap_estimation.token_out.amount, + OrderType::StopLoss => order_token_out >= amm_swap_estimation.token_out.amount, _ => false, } } @@ -104,10 +104,10 @@ fn process_order( reply_infos: &mut Vec, ) -> StdResult<()> { let token_out_min_amount: Int128 = match order.order_type { - SpotOrderType::LimitBuy => calculate_token_out_min_amount(order), - SpotOrderType::LimitSell => calculate_token_out_min_amount(order), - SpotOrderType::StopLoss => Int128::zero(), - SpotOrderType::MarketBuy => Int128::zero(), + OrderType::LimitBuy => calculate_token_out_min_amount(order), + OrderType::LimitSell => calculate_token_out_min_amount(order), + OrderType::StopLoss => Int128::zero(), + OrderType::MarketBuy => Int128::zero(), }; let msg = ElysMsg::amm_swap_exact_amount_in( diff --git a/src/action/mod.rs b/src/action/mod.rs index 75e3c22..8f2a759 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -6,7 +6,8 @@ pub mod query { mod asset_info; mod get_all_price; mod get_margin_order; - mod get_margin_orders; + mod get_margin_position; + mod get_margin_positions; mod get_spot_order; mod get_spot_orders; mod swap_estimation_by_denom; @@ -20,7 +21,8 @@ pub mod query { pub use asset_info::asset_info; pub use get_all_price::get_all_prices; pub use get_margin_order::get_margin_order; - pub use get_margin_orders::get_margin_orders; + pub use get_margin_position::get_margin_position; + pub use get_margin_positions::get_margin_positions; pub use get_spot_order::get_spot_order; pub use get_spot_orders::get_spot_orders; pub use swap_estimation_by_denom::swap_estimation_by_denom; diff --git a/src/action/query/get_margin_order.rs b/src/action/query/get_margin_order.rs index 960840b..db6a091 100644 --- a/src/action/query/get_margin_order.rs +++ b/src/action/query/get_margin_order.rs @@ -1,19 +1,13 @@ -use cosmwasm_std::StdError; - use super::*; pub fn get_margin_order( deps: Deps, - address: String, id: u64, -) -> Result { - let querier = ElysQuerier::new(&deps.querier); - - let resp: MarginMtpResponse = querier.mtp(address, id)?; +) -> Result { + let orders = MARGIN_ORDER.load(deps.storage)?; - if let Some(mtp) = resp.mtp { - Ok(mtp) - } else { - Err(StdError::not_found("margin trading prosition").into()) + match orders.iter().find(|order| order.order_id == id).cloned() { + Some(order) => Ok(GetMarginOrderResp { order }), + None => Err(ContractError::OrderNotFound { order_id: id }), } } diff --git a/src/action/query/get_margin_position.rs b/src/action/query/get_margin_position.rs new file mode 100644 index 0000000..4f96fff --- /dev/null +++ b/src/action/query/get_margin_position.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::StdError; + +use super::*; + +pub fn get_margin_position( + deps: Deps, + address: String, + id: u64, +) -> Result { + let querier = ElysQuerier::new(&deps.querier); + + let resp: MarginMtpResponse = querier.mtp(address, id)?; + + if let Some(_) = &resp.mtp { + Ok(resp) + } else { + Err(StdError::not_found("margin trading prosition").into()) + } +} diff --git a/src/action/query/get_margin_orders.rs b/src/action/query/get_margin_positions.rs similarity index 90% rename from src/action/query/get_margin_orders.rs rename to src/action/query/get_margin_positions.rs index 9764298..2c9a188 100644 --- a/src/action/query/get_margin_orders.rs +++ b/src/action/query/get_margin_positions.rs @@ -1,6 +1,6 @@ use super::*; -pub fn get_margin_orders( +pub fn get_margin_positions( deps: Deps, pagination: PageRequest, ) -> Result { diff --git a/src/action/query/get_spot_orders.rs b/src/action/query/get_spot_orders.rs index 8d832c4..758d162 100644 --- a/src/action/query/get_spot_orders.rs +++ b/src/action/query/get_spot_orders.rs @@ -4,7 +4,7 @@ pub fn get_spot_orders( deps: Deps, pagination: PageRequest, order_owner: Option, - order_type: Option, + order_type: Option, ) -> Result { let orders = SPOT_ORDER.load(deps.storage)?; diff --git a/src/action/reply/close_margin_position.rs b/src/action/reply/close_margin_position.rs index 69cb164..e084974 100644 --- a/src/action/reply/close_margin_position.rs +++ b/src/action/reply/close_margin_position.rs @@ -1,27 +1,24 @@ -use cosmwasm_std::SubMsgResult; - -use crate::helper::get_response_from_reply; +use cosmwasm_std::{from_json, StdError, SubMsgResult}; use super::*; pub fn reply_to_close_margin_order( - deps: DepsMut, module_resp: SubMsgResult, ) -> Result, ContractError> { - let close_resp: MarginCloseResponse = match get_response_from_reply(module_resp) { - Ok(close_resp) => close_resp, - Err(err) => return Ok(err), + let response = match module_resp.into_result() { + Ok(response) => response, + Err(err) => return Err(StdError::generic_err(err).into()), }; - let orders: Vec = MARGIN_ORDER.load(deps.storage)?; - - let orders: Vec = orders - .iter() - .filter(|order| order.order_id != close_resp.id) - .cloned() - .collect(); + let data = match response.data { + Some(data) => data, + None => return Err(StdError::generic_err("No Data").into()), + }; - MARGIN_ORDER.save(deps.storage, &orders)?; + let close_resp: MarginBrokerCloseResResponse = match from_json(&data) { + Ok(resp) => resp, + Err(err) => return Err(err.into()), + }; let resp = Response::new().add_attribute("order_id", close_resp.id.to_string()); diff --git a/src/action/reply/create_margin_order.rs b/src/action/reply/create_margin_order.rs index 1ac1acf..b807c8e 100644 --- a/src/action/reply/create_margin_order.rs +++ b/src/action/reply/create_margin_order.rs @@ -1,29 +1,26 @@ -use crate::helper::get_response_from_reply; - use super::*; -use cosmwasm_std::{from_json, Binary, SubMsgResult}; +use cosmwasm_std::{from_json, StdError, SubMsgResult}; pub fn reply_to_create_margin_order( - deps: DepsMut, - data: Option, module_resp: SubMsgResult, ) -> Result, ContractError> { - let open_resp: MarginOpenResponse = match get_response_from_reply(module_resp) { - Ok(open_resp) => open_resp, - Err(err) => return Ok(err), + let response = match module_resp.into_result() { + Ok(response) => response, + Err(err) => return Err(StdError::generic_err(err).into()), }; - let mut order: MarginOrder = match data { - Some(ref data) => from_json(data)?, - None => return Ok(Response::new().add_attribute("error", "no meta_data".to_string())), + let data = match response.data { + Some(data) => data, + None => return Err(StdError::generic_err("No Data").into()), }; - order.order_id = open_resp.id; - - let mut orders = MARGIN_ORDER.load(deps.storage)?; - orders.push(order); + let margin_resp: MarginBrokerOpenResResponse = match from_json(&data) { + Ok(resp) => resp, + Err(err) => return Err(err.into()), + }; - let resp = Response::new().add_attribute("order_id", open_resp.id.to_string()); + let resp = + Response::new().add_attribute("margin_trading_position_id", margin_resp.id.to_string()); Ok(resp) } diff --git a/src/entry_point/execute.rs b/src/entry_point/execute.rs index 55e95b0..3732dcc 100644 --- a/src/entry_point/execute.rs +++ b/src/entry_point/execute.rs @@ -43,14 +43,19 @@ pub fn execute( leverage, borrow_asset, take_profit_price, + order_type, + trigger_price, } => create_margin_order( info, deps, + env, position, collateral, leverage, borrow_asset, take_profit_price, + order_type, + trigger_price, ), CancelMarginOrder { order_id } => cancel_margin_order(info, deps, order_id), } diff --git a/src/entry_point/query.rs b/src/entry_point/query.rs index 345bb3e..6d27693 100644 --- a/src/entry_point/query.rs +++ b/src/entry_point/query.rs @@ -10,10 +10,10 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result Ok(to_json_binary(&query::get_spot_order(deps, order_id)?)?), GetAllPrices {} => Ok(to_json_binary(&query::get_all_prices(deps)?)?), AssetInfo { denom } => Ok(to_json_binary(&query::asset_info(deps, denom)?)?), - GetMarginOrder { address, id } => Ok(to_json_binary(&query::get_margin_order( + GetMarginPosition { address, id } => Ok(to_json_binary(&query::get_margin_position( deps, address, id, )?)?), - GetMarginOrders { pagination } => Ok(to_json_binary(&query::get_margin_orders( + GetMarginPositions { pagination } => Ok(to_json_binary(&query::get_margin_positions( deps, pagination, )?)?), GetSpotOrders { @@ -33,5 +33,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result Ok(to_json_binary(&query::swap_estimation_by_denom( deps, amount, denom_in, denom_out, )?)?), + GetMarginOrder { id } => Ok(to_json_binary(&query::get_margin_order(deps, id)?)?), } } diff --git a/src/entry_point/reply.rs b/src/entry_point/reply.rs index d74f841..5932966 100644 --- a/src/entry_point/reply.rs +++ b/src/entry_point/reply.rs @@ -1,6 +1,6 @@ use super::*; use crate::{action::reply::*, states::REPLY_INFO, types::ReplyInfo}; -use cosmwasm_std::Reply; +use cosmwasm_std::{Reply, StdError}; use msg::ReplyType; #[cfg_attr(not(feature = "library"), entry_point)] @@ -30,7 +30,8 @@ pub fn reply( match info.reply_type { ReplyType::SpotOrder => reply_to_spot_order(deps, info.data, module_resp), - ReplyType::MarginOpenPosition => reply_to_create_margin_order(deps, info.data, module_resp), - ReplyType::MarginClosePosition => reply_to_close_margin_order(deps, module_resp), + ReplyType::MarginBrokerOpenMarketBuy => reply_to_create_margin_order(module_resp), + ReplyType::MarginBrokerClose => reply_to_close_margin_order(module_resp), + _ => return Err(StdError::generic_err("submsg unimplemented").into()), } } diff --git a/src/error.rs b/src/error.rs index 7e36099..2586371 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,11 +17,11 @@ pub enum ContractError { #[error("Incorrect number of funds. Only one fund is allowed.")] CoinNumber, #[error("order price already been reached")] - SpotOrderPriceReached, + OrderPriceReached, #[error("order_source_denom and order_target_denom cannot be the same")] SpotOrderSameDenom, #[error("denom in order_price not used")] - SpotOrderPriceDenom, + OrderPriceDenom, #[error("fund not used by the order")] SpotOrderWrongFund, #[error("{sender} is no autorized to use the process_orders endpoint")] diff --git a/src/msg/execute_msg.rs b/src/msg/execute_msg.rs index d6c105b..a96f9bb 100644 --- a/src/msg/execute_msg.rs +++ b/src/msg/execute_msg.rs @@ -1,14 +1,14 @@ -use crate::types::{MarginPosition, SpotOrderPrice, SpotOrderType, SwapAmountInRoute}; +use crate::types::{MarginPosition, OrderPrice, OrderType, SwapAmountInRoute}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Decimal}; #[cw_serde] pub enum ExecuteMsg { CreateSpotOrder { - order_type: SpotOrderType, + order_type: OrderType, order_source_denom: String, order_target_denom: String, - order_price: SpotOrderPrice, + order_price: OrderPrice, order_amm_routes: Option>, }, CancelSpotOrder { @@ -18,7 +18,7 @@ pub enum ExecuteMsg { CancelSpotOrders { order_ids: Option>, owner_address: String, - order_type: Option, + order_type: Option, }, ProcessSpotOrders {}, @@ -29,6 +29,8 @@ pub enum ExecuteMsg { leverage: Decimal, borrow_asset: String, take_profit_price: Decimal, + order_type: OrderType, + trigger_price: OrderPrice, }, CancelMarginOrder { diff --git a/src/msg/mod.rs b/src/msg/mod.rs index 67acdf7..e720f19 100644 --- a/src/msg/mod.rs +++ b/src/msg/mod.rs @@ -10,9 +10,12 @@ pub use reply_type::ReplyType; pub mod query_resp { mod get_all_prices_resp; + mod get_margin_order_resp; mod get_spot_order_resp; mod get_spot_orders_resp; + pub use get_all_prices_resp::GetAllPricesResponse; + pub use get_margin_order_resp::GetMarginOrderResp; pub use get_spot_order_resp::GetSpotOrderResp; pub use get_spot_orders_resp::GetSpotOrdersResp; } diff --git a/src/msg/query_msg.rs b/src/msg/query_msg.rs index 00f13df..88550b7 100644 --- a/src/msg/query_msg.rs +++ b/src/msg/query_msg.rs @@ -1,6 +1,6 @@ #[allow(unused_imports)] use super::query_resp::*; -use crate::types::SpotOrderType; +use crate::types::OrderType; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::Coin; #[allow(unused_imports)] @@ -16,15 +16,13 @@ pub enum QueryMsg { GetAllPrices {}, #[returns(OracleAssetInfoResponse)] AssetInfo { denom: String }, - #[returns(MarginMtpResponse)] - GetMarginOrder { address: String, id: u64 }, - #[returns(MarginQueryPositionsResponse)] - GetMarginOrders { pagination: PageRequest }, + #[returns(GetMarginOrderResp)] + GetMarginOrder { id: u64 }, #[returns(GetSpotOrdersResp)] GetSpotOrders { pagination: PageRequest, order_owner: Option, - order_type: Option, + order_type: Option, }, #[returns(AmmSwapEstimationByDenomResponse)] SwapEstimationByDenom { @@ -32,4 +30,8 @@ pub enum QueryMsg { denom_in: String, denom_out: String, }, + #[returns(MarginMtpResponse)] + GetMarginPosition { id: u64, address: String }, + #[returns(MarginQueryPositionsResponse)] + GetMarginPositions { pagination: PageRequest }, } diff --git a/src/msg/query_resp/get_margin_order_resp.rs b/src/msg/query_resp/get_margin_order_resp.rs new file mode 100644 index 0000000..62abe6c --- /dev/null +++ b/src/msg/query_resp/get_margin_order_resp.rs @@ -0,0 +1,7 @@ +use crate::types::MarginOrder; +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct GetMarginOrderResp { + pub order: MarginOrder, +} diff --git a/src/msg/reply_type.rs b/src/msg/reply_type.rs index b787a58..a8458d3 100644 --- a/src/msg/reply_type.rs +++ b/src/msg/reply_type.rs @@ -3,6 +3,7 @@ use cosmwasm_schema::cw_serde; #[cw_serde] pub enum ReplyType { SpotOrder, - MarginOpenPosition, - MarginClosePosition, + MarginBrokerOpen, + MarginBrokerClose, + MarginBrokerOpenMarketBuy, } diff --git a/src/tests/cancel_margin_order/not_found.rs b/src/tests/cancel_margin_order/not_found.rs new file mode 100644 index 0000000..6f0b105 --- /dev/null +++ b/src/tests/cancel_margin_order/not_found.rs @@ -0,0 +1,44 @@ +use super::*; + +#[test] +fn not_found() { + // Initialize the ElysApp. + let mut app = ElysApp::new(); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMockMsg { + process_order_executor: "owner".to_string(), + spot_orders: vec![], + margin_orders: vec![], + }; + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_reply(reply); + let code_id = app.store_code(Box::new(code)); + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + let err = app + .execute_contract( + Addr::unchecked("user"), + addr.clone(), + &ExecuteMsg::CancelMarginOrder { order_id: 0 }, + &[], + ) + .unwrap_err(); + + assert_eq!( + ContractError::OrderNotFound { order_id: 0 }, + err.downcast().unwrap() + ); +} diff --git a/src/tests/cancel_margin_order/succesful_cancel_an_order.rs b/src/tests/cancel_margin_order/succesful_cancel_an_order.rs new file mode 100644 index 0000000..883a839 --- /dev/null +++ b/src/tests/cancel_margin_order/succesful_cancel_an_order.rs @@ -0,0 +1,82 @@ +use super::*; +use cw_multi_test::BankSudo; + +#[test] +fn succesful_cancel_an_order() { + // Initialize the ElysApp. + let mut app = ElysApp::new(); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMockMsg { + process_order_executor: "owner".to_string(), + spot_orders: vec![], + margin_orders: vec![MarginOrder::new( + &MarginPosition::Long, + &coin(255, "usdc"), + "btc", + "user", + &Decimal::one(), + &Decimal::one(), + &OrderType::LimitBuy, + &OrderPrice { + base_denom: "btc".to_string(), + quote_denom: "usdc".to_string(), + rate: Decimal::one(), + }, + &vec![], + )], + }; + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_reply(reply); + let code_id = app.store_code(Box::new(code)); + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + // Mint the token from the order to simulate that the tokens are already locked. + + app.sudo( + BankSudo::Mint { + to_address: addr.to_string(), + amount: coins(255, "usdc"), + } + .into(), + ) + .unwrap(); + + app.execute_contract( + Addr::unchecked("user"), + addr.clone(), + &ExecuteMsg::CancelMarginOrder { order_id: 0 }, + &[], + ) + .unwrap(); + + assert_eq!( + app.wrap() + .query_balance(&addr, "usdc") + .unwrap() + .amount + .u128(), + 0 + ); + + assert_eq!( + app.wrap() + .query_balance("user", "usdc") + .unwrap() + .amount + .u128(), + 255 + ); +} diff --git a/src/tests/cancel_margin_order/unauthorize.rs b/src/tests/cancel_margin_order/unauthorize.rs new file mode 100644 index 0000000..2d4bbb0 --- /dev/null +++ b/src/tests/cancel_margin_order/unauthorize.rs @@ -0,0 +1,90 @@ +use super::*; +use cw_multi_test::BankSudo; + +#[test] +fn unauthorize() { + // Initialize the ElysApp. + let mut app = ElysApp::new(); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMockMsg { + process_order_executor: "owner".to_string(), + spot_orders: vec![], + margin_orders: vec![MarginOrder::new( + &MarginPosition::Long, + &coin(255, "usdc"), + "btc", + "user", + &Decimal::one(), + &Decimal::one(), + &OrderType::LimitBuy, + &OrderPrice { + base_denom: "btc".to_string(), + quote_denom: "usdc".to_string(), + rate: Decimal::one(), + }, + &vec![], + )], + }; + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_reply(reply); + let code_id = app.store_code(Box::new(code)); + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + // Mint the token from the order to simulate that the tokens are already locked. + + app.sudo( + BankSudo::Mint { + to_address: addr.to_string(), + amount: coins(255, "usdc"), + } + .into(), + ) + .unwrap(); + + let err = app + .execute_contract( + Addr::unchecked("not-user"), + addr.clone(), + &ExecuteMsg::CancelMarginOrder { order_id: 0 }, + &[], + ) + .unwrap_err(); + + assert_eq!( + app.wrap() + .query_balance(&addr, "usdc") + .unwrap() + .amount + .u128(), + 255 + ); + + assert_eq!( + app.wrap() + .query_balance("not-user", "usdc") + .unwrap() + .amount + .u128(), + 0 + ); + + assert_eq!( + ContractError::Unauthorized { + sender: Addr::unchecked("not-user") + }, + err.downcast().unwrap() + ); +} diff --git a/src/tests/cancel_spot_order/not_found.rs b/src/tests/cancel_spot_order/not_found.rs index ae710e0..13ab82b 100644 --- a/src/tests/cancel_spot_order/not_found.rs +++ b/src/tests/cancel_spot_order/not_found.rs @@ -7,7 +7,8 @@ fn not_found() { // Create a mock message to instantiate the contract with an empty list of orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Specify the order ID that the user wants to cancel. diff --git a/src/tests/cancel_spot_order/process_spot_order_processing.rs b/src/tests/cancel_spot_order/process_spot_order_processing.rs index 5f36602..38db088 100644 --- a/src/tests/cancel_spot_order/process_spot_order_processing.rs +++ b/src/tests/cancel_spot_order/process_spot_order_processing.rs @@ -22,8 +22,8 @@ fn process_spot_order_processing() { // Create a "limit buy" order (dummy order) with a specific rate and balance. let dummy_order = SpotOrder::new( - SpotOrderType::LimitBuy, - SpotOrderPrice { + OrderType::LimitBuy, + OrderPrice { base_denom: "ubtc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(38), 0).unwrap(), // Rate at which ubtc will be bought (38 USDC per ubtc). @@ -38,7 +38,8 @@ fn process_spot_order_processing() { // Create a mock message to instantiate the contract with the dummy order. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![dummy_order], + spot_orders: vec![dummy_order], + margin_orders: vec![], }; // Create an execution message to process orders. diff --git a/src/tests/cancel_spot_order/successful_cancel_order_with_created_order.rs b/src/tests/cancel_spot_order/successful_cancel_order_with_created_order.rs index bb3e429..182c401 100644 --- a/src/tests/cancel_spot_order/successful_cancel_order_with_created_order.rs +++ b/src/tests/cancel_spot_order/successful_cancel_order_with_created_order.rs @@ -13,7 +13,8 @@ fn successful_cancel_order_with_created_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -38,8 +39,8 @@ fn successful_cancel_order_with_created_order() { Addr::unchecked("user"), addr.clone(), &ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::StopLoss, - order_price: SpotOrderPrice { + order_type: OrderType::StopLoss, + order_price: OrderPrice { rate: Decimal::from_atomics(Uint128::new(18), 0).unwrap(), base_denom: "btc".to_string(), quote_denom: "eth".to_string(), diff --git a/src/tests/cancel_spot_order/successful_cancel_order_with_dummy_order.rs b/src/tests/cancel_spot_order/successful_cancel_order_with_dummy_order.rs index 7e37c4d..6427f96 100644 --- a/src/tests/cancel_spot_order/successful_cancel_order_with_dummy_order.rs +++ b/src/tests/cancel_spot_order/successful_cancel_order_with_dummy_order.rs @@ -15,7 +15,8 @@ fn successful_cancel_order_with_dummy_order() { // Create a mock message to instantiate the contract with the dummy order. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![dummy_order.clone()], + spot_orders: vec![dummy_order.clone()], + margin_orders: vec![], }; // Create a contract wrapper and store its code. diff --git a/src/tests/cancel_spot_order/unauthorized.rs b/src/tests/cancel_spot_order/unauthorized.rs index a4089b2..248d3b3 100644 --- a/src/tests/cancel_spot_order/unauthorized.rs +++ b/src/tests/cancel_spot_order/unauthorized.rs @@ -8,11 +8,12 @@ fn unauthorized() { // Create a mock message to instantiate the contract with an order owned by the "user" let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![SpotOrder::new_dummy()], + spot_orders: vec![SpotOrder::new_dummy()], + margin_orders: vec![], }; // Retrieve the order ID from the instantiated message for later use. - let id = instantiate_msg.orders[0].order_id.clone().to_owned(); + let id = instantiate_msg.spot_orders[0].order_id.clone().to_owned(); // Create a contract wrapper and store its code. let code = ContractWrapper::new(execute, instantiate, query); diff --git a/src/tests/cancel_spot_orders/successfully_cancel_orders.rs b/src/tests/cancel_spot_orders/successfully_cancel_orders.rs index 9f1f797..cb1f240 100644 --- a/src/tests/cancel_spot_orders/successfully_cancel_orders.rs +++ b/src/tests/cancel_spot_orders/successfully_cancel_orders.rs @@ -9,11 +9,11 @@ fn successfully_cancel_orders() { vec![coin(16, "btc"), coin(5, "eth"), coin(20, "usdt")], )]; - let orders = vec![ + let spot_orders = vec![ SpotOrder { - order_type: SpotOrderType::LimitBuy, + order_type: OrderType::LimitBuy, order_id: 0, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -24,9 +24,9 @@ fn successfully_cancel_orders() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 1, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -37,9 +37,9 @@ fn successfully_cancel_orders() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 2, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -50,9 +50,9 @@ fn successfully_cancel_orders() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 3, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -69,7 +69,8 @@ fn successfully_cancel_orders() { // Create a mock message to instantiate the contract with an empty list of orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders, + spot_orders, + margin_orders: vec![], }; let code = ContractWrapper::new(execute, instantiate, query); diff --git a/src/tests/cancel_spot_orders/successfully_cancel_orders_id.rs b/src/tests/cancel_spot_orders/successfully_cancel_orders_id.rs index 7b60ce2..714c3b8 100644 --- a/src/tests/cancel_spot_orders/successfully_cancel_orders_id.rs +++ b/src/tests/cancel_spot_orders/successfully_cancel_orders_id.rs @@ -9,11 +9,11 @@ fn successfully_cancel_orders_ids() { vec![coin(16, "btc"), coin(5, "eth"), coin(20, "usdt")], )]; - let orders = vec![ + let spot_orders = vec![ SpotOrder { - order_type: SpotOrderType::LimitBuy, + order_type: OrderType::LimitBuy, order_id: 0, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -24,9 +24,9 @@ fn successfully_cancel_orders_ids() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 1, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -37,9 +37,9 @@ fn successfully_cancel_orders_ids() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 2, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -50,9 +50,9 @@ fn successfully_cancel_orders_ids() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 3, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -69,7 +69,8 @@ fn successfully_cancel_orders_ids() { // Create a mock message to instantiate the contract with an empty list of orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders, + spot_orders, + margin_orders: vec![], }; let code = ContractWrapper::new(execute, instantiate, query); diff --git a/src/tests/cancel_spot_orders/successfully_cancel_orders_type.rs b/src/tests/cancel_spot_orders/successfully_cancel_orders_type.rs index b871365..4616af1 100644 --- a/src/tests/cancel_spot_orders/successfully_cancel_orders_type.rs +++ b/src/tests/cancel_spot_orders/successfully_cancel_orders_type.rs @@ -9,11 +9,11 @@ fn successfully_cancel_orders_type() { vec![coin(16, "btc"), coin(5, "eth"), coin(20, "usdt")], )]; - let orders = vec![ + let spot_orders = vec![ SpotOrder { - order_type: SpotOrderType::LimitBuy, + order_type: OrderType::LimitBuy, order_id: 0, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -24,9 +24,9 @@ fn successfully_cancel_orders_type() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 1, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -37,9 +37,9 @@ fn successfully_cancel_orders_type() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 2, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -50,9 +50,9 @@ fn successfully_cancel_orders_type() { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 3, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), @@ -69,7 +69,8 @@ fn successfully_cancel_orders_type() { // Create a mock message to instantiate the contract with an empty list of orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders, + spot_orders, + margin_orders: vec![], }; let code = ContractWrapper::new(execute, instantiate, query); @@ -121,7 +122,7 @@ fn successfully_cancel_orders_type() { &&ExecuteMsg::CancelSpotOrders { order_ids: None, owner_address: "user".to_string(), - order_type: Some(SpotOrderType::LimitBuy), + order_type: Some(OrderType::LimitBuy), }, &[], ) diff --git a/src/tests/cancel_spot_orders/unauthorize.rs b/src/tests/cancel_spot_orders/unauthorize.rs index cc97b8f..2d1d24a 100644 --- a/src/tests/cancel_spot_orders/unauthorize.rs +++ b/src/tests/cancel_spot_orders/unauthorize.rs @@ -7,7 +7,8 @@ fn unauthorize() { // Create a mock message to instantiate the contract with an empty list of orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; let code = ContractWrapper::new(execute, instantiate, query); diff --git a/src/tests/create_margin_order/coin_number.rs b/src/tests/create_margin_order/coin_number.rs index a34fe0c..e7df831 100644 --- a/src/tests/create_margin_order/coin_number.rs +++ b/src/tests/create_margin_order/coin_number.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use super::*; #[test] @@ -8,7 +10,8 @@ fn coin_number() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. let code = ContractWrapper::new(execute, instantiate, query); @@ -34,10 +37,16 @@ fn coin_number() { addr, &ExecuteMsg::CreateMarginOrder { position: MarginPosition::Short, - collateral: coin(600, "usdc"), + collateral: coin(600, "uusdc"), leverage: Decimal::from_atomics(Uint128::new(500), 2).unwrap(), borrow_asset: "uatom".to_string(), take_profit_price: Decimal::from_atomics(Uint128::new(500), 2).unwrap(), + order_type: OrderType::LimitSell, + trigger_price: OrderPrice { + base_denom: "uatom".to_string(), + quote_denom: "uusdc".to_string(), + rate: Decimal::from_str("1.5").unwrap(), + }, }, &[], ) diff --git a/src/tests/create_margin_order/collateral_amount.rs b/src/tests/create_margin_order/collateral_amount.rs index 8ce5bf3..0c8fe69 100644 --- a/src/tests/create_margin_order/collateral_amount.rs +++ b/src/tests/create_margin_order/collateral_amount.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use super::*; #[test] @@ -10,7 +12,8 @@ fn collateral_amount() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. let code = ContractWrapper::new(execute, instantiate, query); @@ -40,6 +43,12 @@ fn collateral_amount() { leverage: Decimal::from_atomics(Uint128::new(500), 2).unwrap(), borrow_asset: "uatom".to_string(), take_profit_price: Decimal::from_atomics(Uint128::new(500), 2).unwrap(), + order_type: OrderType::StopLoss, + trigger_price: OrderPrice { + base_denom: "uatom".to_string(), + quote_denom: "uusdc".to_string(), + rate: Decimal::from_str("1.25").unwrap(), + }, }, &[coin(45, "usdc")], ) diff --git a/src/tests/create_margin_order/succsessful_create_short_order.rs b/src/tests/create_margin_order/non_usdc_collateral_for_short.rs similarity index 63% rename from src/tests/create_margin_order/succsessful_create_short_order.rs rename to src/tests/create_margin_order/non_usdc_collateral_for_short.rs index 0391a99..4698307 100644 --- a/src/tests/create_margin_order/succsessful_create_short_order.rs +++ b/src/tests/create_margin_order/non_usdc_collateral_for_short.rs @@ -1,9 +1,11 @@ -use crate::tests::get_order_id_from_events::get_order_id_from_events; +use std::str::FromStr; + +use cosmwasm_std::StdError; use super::*; #[test] -fn successful_create_short_order() { - // Create a wallet for the "user" with an initial balance of 100 BTC. +fn non_usdc_collateral_for_short() { + // Create a wallet for the "user" with an initial balance of 10 BTC. let wallet = vec![("user", coins(10, "btc"))]; // Initialize the ElysApp instance with the specified wallet. @@ -12,7 +14,8 @@ fn successful_create_short_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -31,8 +34,8 @@ fn successful_create_short_order() { ) .unwrap(); - // User "user" creates a "short position" margin order for BTC, specifying the maximum price in BTC. - let resp = app + // User "user" creates a Short position Margin order with non uusdc as collaterals + let err = app .execute_contract( Addr::unchecked("user"), addr.clone(), @@ -42,31 +45,21 @@ fn successful_create_short_order() { leverage: Decimal::from_atomics(Uint128::new(215), 2).unwrap(), borrow_asset: "btc".to_string(), take_profit_price: Decimal::from_atomics(Uint128::new(200), 2).unwrap(), + order_type: OrderType::LimitSell, + trigger_price: OrderPrice { + base_denom: "btc".to_string(), + quote_denom: "usdc".to_string(), + rate: Decimal::from_str("1.7").unwrap(), + }, }, &coins(10, "btc"), // User's BTC balance. ) - .unwrap(); - - // Verify that the "user" no longer has any BTC after creating the order. - assert_eq!( - app.wrap() - .query_balance("user", "btc") - .unwrap() - .amount - .u128(), - 0 - ); + .unwrap_err(); - // Verify that the contract address sends the BTC to the margin module. assert_eq!( - app.wrap() - .query_balance(&addr, "btc") - .unwrap() - .amount - .u128(), - 0 + ContractError::StdError(StdError::generic_err( + "the collateral asset for a short can only be UUSDC" + )), + err.downcast().unwrap(), ); - - // Verify that an order ID is emitted in the contract's events. - assert!(get_order_id_from_events(&resp.events).is_some()); } diff --git a/src/tests/create_margin_order/successful_create_margin_market_order.rs b/src/tests/create_margin_order/successful_create_margin_market_order.rs new file mode 100644 index 0000000..d1c897c --- /dev/null +++ b/src/tests/create_margin_order/successful_create_margin_market_order.rs @@ -0,0 +1,82 @@ +use std::str::FromStr; + +use super::*; + +#[test] +fn successful_create_margin_market_order() { + // Create a wallet for the "user" with an initial balance of 10 BTC. + let wallet = vec![("user", coins(10, "btc"))]; + + // Initialize the ElysApp instance with the specified wallet. + let mut app = ElysApp::new_with_wallets(wallet); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMockMsg { + process_order_executor: "owner".to_string(), + spot_orders: vec![], + margin_orders: vec![], + }; + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_reply(reply); + let code_id = app.store_code(Box::new(code)); + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + // User "user" creates a "MakerBuy" margin order for BTC + app.execute_contract( + Addr::unchecked("user"), + addr.clone(), + &ExecuteMsg::CreateMarginOrder { + position: MarginPosition::Long, + collateral: coin(10, "btc"), + leverage: Decimal::from_atomics(Uint128::new(215), 2).unwrap(), + borrow_asset: "btc".to_string(), + take_profit_price: Decimal::from_atomics(Uint128::new(200), 2).unwrap(), + order_type: OrderType::MarketBuy, + trigger_price: OrderPrice { + base_denom: "btc".to_string(), + quote_denom: "usdc".to_string(), + rate: Decimal::from_str("1.7").unwrap(), + }, + }, + &coins(10, "btc"), // User's BTC balance. + ) + .unwrap(); + + // Verify that the "user" no longer has any BTC after creating the order. + assert_eq!( + app.wrap() + .query_balance("user", "btc") + .unwrap() + .amount + .u128(), + 0 + ); + + // Verify that the contract address send the BTC to the Margin Module. + assert_eq!( + app.wrap() + .query_balance(&addr, "btc") + .unwrap() + .amount + .u128(), + 0 + ); + + let last_module = app + .init_modules(|router, _, store| router.custom.get_last_module(store).unwrap()) + .unwrap(); + + assert_eq!(last_module, "MarginBrokerOpen"); +} diff --git a/src/tests/create_margin_order/successful_create_long_order.rs b/src/tests/create_margin_order/successful_create_margin_order.rs similarity index 79% rename from src/tests/create_margin_order/successful_create_long_order.rs rename to src/tests/create_margin_order/successful_create_margin_order.rs index 5738185..36d041f 100644 --- a/src/tests/create_margin_order/successful_create_long_order.rs +++ b/src/tests/create_margin_order/successful_create_margin_order.rs @@ -1,9 +1,11 @@ +use std::str::FromStr; + use crate::tests::get_order_id_from_events::get_order_id_from_events; use super::*; #[test] -fn successful_create_long_order() { +fn successful_create_margin_order() { // Create a wallet for the "user" with an initial balance of 10 BTC. let wallet = vec![("user", coins(10, "btc"))]; @@ -13,7 +15,8 @@ fn successful_create_long_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -32,7 +35,7 @@ fn successful_create_long_order() { ) .unwrap(); - // User "user" creates a "long position" margin order for BTC, specifying the maximum price in BTC. + // User "user" creates a non "MakerBuy" margin order for BTC let resp = app .execute_contract( Addr::unchecked("user"), @@ -43,6 +46,12 @@ fn successful_create_long_order() { leverage: Decimal::from_atomics(Uint128::new(215), 2).unwrap(), borrow_asset: "btc".to_string(), take_profit_price: Decimal::from_atomics(Uint128::new(200), 2).unwrap(), + order_type: OrderType::LimitSell, + trigger_price: OrderPrice { + base_denom: "btc".to_string(), + quote_denom: "usdc".to_string(), + rate: Decimal::from_str("1.7").unwrap(), + }, }, &coins(10, "btc"), // User's BTC balance. ) @@ -58,14 +67,14 @@ fn successful_create_long_order() { 0 ); - // Verify that the contract address sends the BTC to the margin module. + // Verify that the contract address locked the BTC. assert_eq!( app.wrap() .query_balance(&addr, "btc") .unwrap() .amount .u128(), - 0 + 10 ); // Verify that an order ID is emitted in the contract's events. diff --git a/src/tests/create_spot_order/coin_number.rs b/src/tests/create_spot_order/coin_number.rs index 9824477..79d6a66 100644 --- a/src/tests/create_spot_order/coin_number.rs +++ b/src/tests/create_spot_order/coin_number.rs @@ -10,7 +10,8 @@ fn coin_number() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -35,8 +36,8 @@ fn coin_number() { Addr::unchecked("user"), addr, &ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::StopLoss, - order_price: SpotOrderPrice { + order_type: OrderType::StopLoss, + order_price: OrderPrice { rate: Decimal::from_atomics(Uint128::new(17), 0).unwrap(), base_denom: "btc".to_string(), quote_denom: "eth".to_string(), diff --git a/src/tests/create_spot_order/not_enough_fund.rs b/src/tests/create_spot_order/not_enough_fund.rs index e8b0d80..c8f7c6a 100644 --- a/src/tests/create_spot_order/not_enough_fund.rs +++ b/src/tests/create_spot_order/not_enough_fund.rs @@ -14,13 +14,14 @@ fn not_enough_fund() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Define the parameters for creating an order with insufficient funds. let create_order_msg = ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::LimitSell, - order_price: SpotOrderPrice { + order_type: OrderType::LimitSell, + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "eth".to_string(), rate: Decimal::from_atomics(Uint128::new(19), 0).unwrap(), diff --git a/src/tests/create_spot_order/order_price_denom.rs b/src/tests/create_spot_order/order_price_denom.rs index 2dc704a..9ffd81b 100644 --- a/src/tests/create_spot_order/order_price_denom.rs +++ b/src/tests/create_spot_order/order_price_denom.rs @@ -12,12 +12,13 @@ fn order_price_denom() { let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; let create_order_msg = ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::LimitSell, - order_price: SpotOrderPrice { + order_type: OrderType::LimitSell, + order_price: OrderPrice { base_denom: "eth".to_string(), quote_denom: "usdc".to_string(), // Invalid pair. rate: Decimal::from_atomics(Uint128::new(1700), 0).unwrap(), @@ -50,7 +51,7 @@ fn order_price_denom() { ) .unwrap_err(); - let error_msg = ContractError::SpotOrderPriceDenom; + let error_msg = ContractError::OrderPriceDenom; assert_eq!(error_msg, err.downcast().unwrap()); diff --git a/src/tests/create_spot_order/order_same_denom.rs b/src/tests/create_spot_order/order_same_denom.rs index 7f9fe2a..b115068 100644 --- a/src/tests/create_spot_order/order_same_denom.rs +++ b/src/tests/create_spot_order/order_same_denom.rs @@ -10,12 +10,13 @@ fn order_same_denom() { let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; let create_order_msg = ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::LimitSell, - order_price: SpotOrderPrice { + order_type: OrderType::LimitSell, + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "eth".to_string(), rate: Decimal::from_atomics(Uint128::new(19), 0).unwrap(), diff --git a/src/tests/create_spot_order/order_wrong_fund.rs b/src/tests/create_spot_order/order_wrong_fund.rs index 70b07c0..30c0f15 100644 --- a/src/tests/create_spot_order/order_wrong_fund.rs +++ b/src/tests/create_spot_order/order_wrong_fund.rs @@ -11,12 +11,13 @@ fn order_wrong_fund() { let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; let create_order_msg = ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::LimitSell, - order_price: SpotOrderPrice { + order_type: OrderType::LimitSell, + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "eth".to_string(), rate: Decimal::from_atomics(Uint128::new(19), 0).unwrap(), diff --git a/src/tests/create_spot_order/successful_create_limit_buy_order.rs b/src/tests/create_spot_order/successful_create_limit_buy_order.rs index c050ca4..0af2d09 100644 --- a/src/tests/create_spot_order/successful_create_limit_buy_order.rs +++ b/src/tests/create_spot_order/successful_create_limit_buy_order.rs @@ -15,7 +15,8 @@ fn successful_create_limit_buy_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -40,8 +41,8 @@ fn successful_create_limit_buy_order() { Addr::unchecked("user"), addr.clone(), &ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::LimitBuy, - order_price: SpotOrderPrice { + order_type: OrderType::LimitBuy, + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(30000), 0).unwrap(), // The maximum price of 30000 USDC per BTC. diff --git a/src/tests/create_spot_order/successful_create_limit_sell_order.rs b/src/tests/create_spot_order/successful_create_limit_sell_order.rs index a32f927..6f7ff33 100644 --- a/src/tests/create_spot_order/successful_create_limit_sell_order.rs +++ b/src/tests/create_spot_order/successful_create_limit_sell_order.rs @@ -15,7 +15,8 @@ fn successful_create_limit_sell_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -40,8 +41,8 @@ fn successful_create_limit_sell_order() { Addr::unchecked("user"), addr.clone(), &ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::LimitSell, - order_price: SpotOrderPrice { + order_type: OrderType::LimitSell, + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(40000), 0).unwrap(), // The desired selling price of 40000 USDC per BTC. diff --git a/src/tests/create_spot_order/successful_create_market_order.rs b/src/tests/create_spot_order/successful_create_market_order.rs index f06ff74..324d9da 100644 --- a/src/tests/create_spot_order/successful_create_market_order.rs +++ b/src/tests/create_spot_order/successful_create_market_order.rs @@ -27,7 +27,8 @@ fn successful_create_stop_loss_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -52,9 +53,9 @@ fn successful_create_stop_loss_order() { Addr::unchecked("user"), addr.clone(), &ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::MarketBuy, + order_type: OrderType::MarketBuy, // Empty order price - not utilized in market orders - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "".to_string(), quote_denom: "".to_string(), rate: Decimal::zero(), diff --git a/src/tests/create_spot_order/successful_create_stop_loss_order.rs b/src/tests/create_spot_order/successful_create_stop_loss_order.rs index ce78500..8b730a3 100644 --- a/src/tests/create_spot_order/successful_create_stop_loss_order.rs +++ b/src/tests/create_spot_order/successful_create_stop_loss_order.rs @@ -16,7 +16,8 @@ fn successful_create_stop_loss_order() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Create a contract wrapper and store its code. @@ -41,8 +42,8 @@ fn successful_create_stop_loss_order() { Addr::unchecked("user"), addr.clone(), &ExecuteMsg::CreateSpotOrder { - order_type: SpotOrderType::StopLoss, - order_price: SpotOrderPrice { + order_type: OrderType::StopLoss, + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(30000), 0).unwrap(), // The trigger price of 30000 USDC per BTC. diff --git a/src/tests/get_margin_order/not_found.rs b/src/tests/get_margin_order/not_found.rs new file mode 100644 index 0000000..a84c6a1 --- /dev/null +++ b/src/tests/get_margin_order/not_found.rs @@ -0,0 +1,55 @@ +use cosmwasm_std::StdError; + +use crate::msg::query_resp::GetMarginOrderResp; + +use super::*; + +// This test case verifies that querying a non-existent order in the contract results in an "OrderNotFound" error. +#[test] +fn not_found() { + // Initialize the ElysApp instance. + let mut app = ElysApp::new(); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMockMsg { + process_order_executor: "owner".to_string(), + spot_orders: vec![], + margin_orders: vec![], + }; + + // Define an order ID that does not exist in the contract (e.g., 0). + let id: u64 = 0; + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query); + let code_id: u64 = app.store_code(Box::new(code)); + + // Instantiate the contract with "owner" as the deployer. + let addr: Addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + // Query the contract for the non-existent order and expect an "OrderNotFound" error. + let err: StdError = app + .wrap() + .query_wasm_smart::(&addr, &QueryMsg::GetMarginOrder { id }) + .unwrap_err(); + + // Define the expected error message. + let error_reference = StdError::GenericErr { + msg: format!( + "Querier contract error: {}", + ContractError::OrderNotFound { order_id: id }.to_string() + ), + }; + + // Verify that the error message matches the expected error. + assert_eq!(err, error_reference); +} diff --git a/src/tests/get_margin_order/successful_query_message.rs b/src/tests/get_margin_order/successful_query_message.rs new file mode 100644 index 0000000..9e49324 --- /dev/null +++ b/src/tests/get_margin_order/successful_query_message.rs @@ -0,0 +1,60 @@ +use crate::msg::query_resp::GetMarginOrderResp; + +use super::*; +// This test case verifies the successful query of an existing order in the contract. +#[test] +fn successful_query_message() { + // Initialize the ElysApp instance. + let mut app = ElysApp::new(); + + let order = MarginOrder::new( + &MarginPosition::Long, + &coin(255, "usdc"), + "btc", + "user", + &Decimal::one(), + &Decimal::one(), + &OrderType::LimitBuy, + &OrderPrice { + base_denom: "btc".to_string(), + quote_denom: "usdc".to_string(), + rate: Decimal::one(), + }, + &vec![], + ); + + // Create a mock message to instantiate the contract with an initial dummy order. + let instantiate_msg = InstantiateMockMsg { + process_order_executor: "owner".to_string(), + spot_orders: vec![], + margin_orders: vec![order.clone()], + }; + + // Extract the order ID from the dummy order. + let id = instantiate_msg.margin_orders[0].order_id; + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query); + let code_id = app.store_code(Box::new(code)); + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + // Query the contract for the existing order. + let resp: GetMarginOrderResp = app + .wrap() + .query_wasm_smart(&addr, &QueryMsg::GetMarginOrder { id }) + .unwrap(); + + // Verify that the response matches the expected order (the initial dummy order). + assert_eq!(resp, GetMarginOrderResp { order }); +} diff --git a/src/tests/get_spot_order/not_found.rs b/src/tests/get_spot_order/not_found.rs index 076e839..392af4b 100644 --- a/src/tests/get_spot_order/not_found.rs +++ b/src/tests/get_spot_order/not_found.rs @@ -9,7 +9,8 @@ fn not_found() { // Create a mock message to instantiate the contract with no initial orders. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; // Define an order ID that does not exist in the contract (e.g., 0). diff --git a/src/tests/get_spot_order/successful_query_message.rs b/src/tests/get_spot_order/successful_query_message.rs index 7820227..046bbee 100644 --- a/src/tests/get_spot_order/successful_query_message.rs +++ b/src/tests/get_spot_order/successful_query_message.rs @@ -9,11 +9,12 @@ fn successful_query_message() { // Create a mock message to instantiate the contract with an initial dummy order. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![SpotOrder::new_dummy()], + spot_orders: vec![SpotOrder::new_dummy()], + margin_orders: vec![], }; // Extract the order ID from the dummy order. - let id = instantiate_msg.orders[0].order_id.clone().to_owned(); + let id = instantiate_msg.spot_orders[0].order_id.clone().to_owned(); // Create a contract wrapper and store its code. let code = ContractWrapper::new(execute, instantiate, query); diff --git a/src/tests/get_spot_orders/get_spot_orders.rs b/src/tests/get_spot_orders/get_spot_orders.rs index eb2cdd6..c81c190 100644 --- a/src/tests/get_spot_orders/get_spot_orders.rs +++ b/src/tests/get_spot_orders/get_spot_orders.rs @@ -4,12 +4,13 @@ use super::*; #[test] fn get_spot_orders() { - let orders: Vec = create_orders(); + let spot_orders: Vec = create_orders(); let mut app = ElysApp::new(); let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: orders.clone(), + spot_orders: spot_orders.clone(), + margin_orders: vec![], }; let code = ContractWrapper::new(execute, instantiate, query); @@ -40,7 +41,7 @@ fn get_spot_orders() { ) .unwrap(); - let (first_third, the_rest) = orders.split_at(2); + let (first_third, the_rest) = spot_orders.split_at(2); assert_eq!(&resp.orders, first_third); @@ -82,9 +83,9 @@ fn get_spot_orders() { fn create_orders() -> Vec { vec![ SpotOrder { - order_type: SpotOrderType::LimitBuy, + order_type: OrderType::LimitBuy, order_id: 0, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "btc".to_owned(), quote_denom: "usdc".to_owned(), rate: Decimal::from_atomics(Uint128::new(25), 1).unwrap(), @@ -95,9 +96,9 @@ fn create_orders() -> Vec { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 1, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "eth".to_owned(), quote_denom: "usdt".to_owned(), rate: Decimal::from_atomics(Uint128::new(10), 1).unwrap(), @@ -108,9 +109,9 @@ fn create_orders() -> Vec { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 2, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "xrp".to_owned(), quote_denom: "usdt".to_owned(), rate: Decimal::from_atomics(Uint128::new(5), 1).unwrap(), @@ -121,9 +122,9 @@ fn create_orders() -> Vec { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 3, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "ltc".to_owned(), quote_denom: "usdc".to_owned(), rate: Decimal::from_atomics(Uint128::new(15), 1).unwrap(), @@ -134,9 +135,9 @@ fn create_orders() -> Vec { order_amm_routes: vec![], }, SpotOrder { - order_type: SpotOrderType::LimitBuy, + order_type: OrderType::LimitBuy, order_id: 4, - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "ada".to_owned(), quote_denom: "usdt".to_owned(), rate: Decimal::from_atomics(Uint128::new(3), 1).unwrap(), diff --git a/src/tests/mock/instantiate.rs b/src/tests/mock/instantiate.rs index 83f9b48..9e95021 100644 --- a/src/tests/mock/instantiate.rs +++ b/src/tests/mock/instantiate.rs @@ -1,4 +1,7 @@ -use crate::{states::*, types::SpotOrder}; +use crate::{ + states::*, + types::{MarginOrder, SpotOrder}, +}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, StdResult}; use elys_bindings::{ElysMsg, ElysQuery}; @@ -6,7 +9,8 @@ use elys_bindings::{ElysMsg, ElysQuery}; #[cw_serde] pub struct InstantiateMockMsg { pub process_order_executor: String, - pub orders: Vec, + pub spot_orders: Vec, + pub margin_orders: Vec, } pub fn instantiate( @@ -15,12 +19,12 @@ pub fn instantiate( _info: MessageInfo, msg: InstantiateMockMsg, ) -> StdResult> { - SPOT_ORDER.save(deps.storage, &msg.orders)?; + SPOT_ORDER.save(deps.storage, &msg.spot_orders)?; deps.querier .query_balance(msg.process_order_executor.clone(), "usdc")?; PROCESS_SPOT_ORDER_EXECUTOR.save(deps.storage, &Addr::unchecked(msg.process_order_executor))?; PROCESSED_SPOT_ORDER.save(deps.storage, &vec![])?; - MARGIN_ORDER.save(deps.storage, &vec![])?; + MARGIN_ORDER.save(deps.storage, &msg.margin_orders)?; REPLY_INFO.save(deps.storage, &vec![])?; Ok(Response::new()) } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 3313deb..bcf2c76 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -67,8 +67,23 @@ mod create_margin_order { use super::*; mod coin_number; mod collateral_amount; - mod successful_create_long_order; - mod succsessful_create_short_order; + mod non_usdc_collateral_for_short; + mod successful_create_margin_market_order; + mod successful_create_margin_order; +} + +mod cancel_margin_order { + use super::*; + mod not_found; + mod succesful_cancel_an_order; + mod unauthorize; +} + +mod get_margin_order { + use super::*; + + mod not_found; + mod successful_query_message; } pub use mock::instantiate::*; diff --git a/src/tests/process_spot_order/succesful_process_limit_buy_order.rs b/src/tests/process_spot_order/succesful_process_limit_buy_order.rs index ee3bc62..af90aa1 100644 --- a/src/tests/process_spot_order/succesful_process_limit_buy_order.rs +++ b/src/tests/process_spot_order/succesful_process_limit_buy_order.rs @@ -32,8 +32,8 @@ fn successful_process_limit_buy_order() { // Create a "limit buy" order (dummy order) with a specific rate and balance. let dummy_order = SpotOrder::new( - SpotOrderType::LimitBuy, - SpotOrderPrice { + OrderType::LimitBuy, + OrderPrice { base_denom: "ubtc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(38), 0).unwrap(), // Rate at which ubtc will be bought (38 USDC per ubtc). @@ -48,7 +48,8 @@ fn successful_process_limit_buy_order() { // Create a mock message to instantiate the contract with the dummy order. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![dummy_order], + spot_orders: vec![dummy_order], + margin_orders: vec![], }; // Create an execution message to process orders. diff --git a/src/tests/process_spot_order/successful_process_5_of_10_orders.rs b/src/tests/process_spot_order/successful_process_5_of_10_orders.rs index 52c63aa..546385a 100644 --- a/src/tests/process_spot_order/successful_process_5_of_10_orders.rs +++ b/src/tests/process_spot_order/successful_process_5_of_10_orders.rs @@ -31,11 +31,12 @@ fn successful_process_5_of_10_orders() { let code = ContractWrapper::new(execute, instantiate, query).with_reply(reply); let code_id = app.store_code(Box::new(code)); - let orders = create_dummy_orders(); + let spot_orders = create_dummy_orders(); let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders, + spot_orders, + margin_orders: vec![], }; let execute_msg = ExecuteMsg::ProcessSpotOrders {}; @@ -178,11 +179,11 @@ fn successful_process_5_of_10_orders() { let order_ids: Vec = read_processed_order_id(resp); - assert!(order_ids.contains(&instantiate_msg.orders[7].order_id)); - assert!(order_ids.contains(&instantiate_msg.orders[3].order_id)); - assert!(order_ids.contains(&instantiate_msg.orders[0].order_id)); - assert!(order_ids.contains(&instantiate_msg.orders[6].order_id)); - assert!(order_ids.contains(&instantiate_msg.orders[8].order_id)); + assert!(order_ids.contains(&instantiate_msg.spot_orders[7].order_id)); + assert!(order_ids.contains(&instantiate_msg.spot_orders[3].order_id)); + assert!(order_ids.contains(&instantiate_msg.spot_orders[0].order_id)); + assert!(order_ids.contains(&instantiate_msg.spot_orders[6].order_id)); + assert!(order_ids.contains(&instantiate_msg.spot_orders[8].order_id)); assert_eq!( app.wrap() @@ -238,25 +239,25 @@ fn successful_process_5_of_10_orders() { fn create_dummy_orders() -> Vec { vec![ SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 0, order_target_denom: "usdc".to_string(), order_amount: coin(1, "eth"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "eth".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(1700), 0).unwrap(), }, }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 1, order_amount: coin(2, "btc"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(12000), 0).unwrap(), @@ -264,12 +265,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 2, order_amount: coin(3, "btc"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(10000), 0).unwrap(), @@ -277,12 +278,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 3, order_amount: coin(5, "eth"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "eth".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(1800), 0).unwrap(), @@ -290,12 +291,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_id: 4, order_amount: coin(1, "eth"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "eth".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(1200), 0).unwrap(), @@ -303,12 +304,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 5, order_amount: coin(1, "eth"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "eth".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(2500), 0).unwrap(), @@ -316,12 +317,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 6, order_amount: coin(3, "btc"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(21000), 0).unwrap(), @@ -329,12 +330,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 7, order_amount: coin(2, "btc"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(25000), 0).unwrap(), @@ -342,12 +343,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 8, order_amount: coin(1, "btc"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(30000), 0).unwrap(), @@ -355,12 +356,12 @@ fn create_dummy_orders() -> Vec { order_target_denom: "usdc".to_string(), }, SpotOrder { - order_type: SpotOrderType::LimitSell, + order_type: OrderType::LimitSell, order_id: 9, order_amount: coin(1, "eth"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![SwapAmountInRoute::new(1, "usdc")], - order_price: SpotOrderPrice { + order_price: OrderPrice { base_denom: "eth".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(2100), 0).unwrap(), diff --git a/src/tests/process_spot_order/successful_process_limit_sell_order.rs b/src/tests/process_spot_order/successful_process_limit_sell_order.rs index 679d5ee..93b4e96 100644 --- a/src/tests/process_spot_order/successful_process_limit_sell_order.rs +++ b/src/tests/process_spot_order/successful_process_limit_sell_order.rs @@ -35,8 +35,8 @@ fn successful_process_limit_sell_order() { // Create a "limit sell" order (dummy order) with a specific rate and balance. let dummy_order = SpotOrder::new( - SpotOrderType::LimitSell, - SpotOrderPrice { + OrderType::LimitSell, + OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(30000), 0).unwrap(), // Rate at which BTC will be sold (30,000 USDC per BTC). @@ -51,7 +51,8 @@ fn successful_process_limit_sell_order() { // Create a mock message to instantiate the contract with the dummy order. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![dummy_order], + spot_orders: vec![dummy_order], + margin_orders: vec![], }; // Create an execution message to process orders. diff --git a/src/tests/process_spot_order/successful_process_stop_loss_order.rs b/src/tests/process_spot_order/successful_process_stop_loss_order.rs index 95d0c43..882bb53 100644 --- a/src/tests/process_spot_order/successful_process_stop_loss_order.rs +++ b/src/tests/process_spot_order/successful_process_stop_loss_order.rs @@ -36,8 +36,8 @@ fn successful_process_stop_loss_order() { // Create a "stop-loss" order (dummy order) with trigger price and balance. let dummy_order = SpotOrder::new( - SpotOrderType::StopLoss, - SpotOrderPrice { + OrderType::StopLoss, + OrderPrice { base_denom: "btc".to_string(), quote_denom: "usdc".to_string(), rate: Decimal::from_atomics(Uint128::new(20000), 0).unwrap(), // Trigger price of 20,000 USDC per BTC. @@ -52,7 +52,8 @@ fn successful_process_stop_loss_order() { // Create a mock message to instantiate the contract with the dummy order. let instantiate_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![dummy_order], + spot_orders: vec![dummy_order], + margin_orders: vec![], }; // Create an execution message to process orders. diff --git a/src/tests/process_spot_order/unauthorize.rs b/src/tests/process_spot_order/unauthorize.rs index 7e055a8..6bcbdf1 100644 --- a/src/tests/process_spot_order/unauthorize.rs +++ b/src/tests/process_spot_order/unauthorize.rs @@ -7,7 +7,8 @@ fn unauthorize() { let code_id = app.store_code(Box::new(code)); let init_msg = InstantiateMockMsg { process_order_executor: "owner".to_string(), - orders: vec![], + spot_orders: vec![], + margin_orders: vec![], }; let random_user = Addr::unchecked("random"); let exec_msg = ExecuteMsg::ProcessSpotOrders {}; diff --git a/src/types/margin_order.rs b/src/types/margin_order.rs new file mode 100644 index 0000000..bf8d778 --- /dev/null +++ b/src/types/margin_order.rs @@ -0,0 +1,49 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Coin, Decimal}; +use elys_bindings::types::MarginPosition; + +use super::{OrderPrice, OrderType}; + +#[cw_serde] +pub struct MarginOrder { + pub order_id: u64, + pub position: MarginPosition, + pub collateral: Coin, + pub borrow_asset: String, + pub owner: String, + pub leverage: Decimal, + pub take_profit_price: Decimal, + pub order_type: OrderType, + pub trigger_price: OrderPrice, +} + +impl MarginOrder { + pub fn new( + position: &MarginPosition, + collateral: &Coin, + borrow_asset: impl Into, + owner: impl Into, + leverage: &Decimal, + take_profit_price: &Decimal, + order_type: &OrderType, + trigger_price: &OrderPrice, + order_vec: &Vec, + ) -> Self { + let order_id: u64 = match order_vec.iter().max_by_key(|s| s.order_id) { + Some(x) => x.order_id + 1, + None => 0, + }; + + Self { + order_id, + position: position.to_owned(), + collateral: collateral.to_owned(), + borrow_asset: borrow_asset.into(), + owner: owner.into(), + leverage: leverage.to_owned(), + take_profit_price: take_profit_price.to_owned(), + order_type: order_type.to_owned(), + trigger_price: trigger_price.to_owned(), + } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 279d399..70d8304 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,5 +1,5 @@ +mod order_type; mod spot_order_price; -mod spot_order_type; mod spot_order { pub mod spot_order; mod impls { @@ -8,10 +8,12 @@ mod spot_order { mod new_dummy; } } +mod margin_order; mod reply_info; pub use elys_bindings::types::*; +pub use margin_order::MarginOrder; +pub use order_type::OrderType; pub use reply_info::ReplyInfo; pub use spot_order::spot_order::SpotOrder; -pub use spot_order_price::SpotOrderPrice; -pub use spot_order_type::SpotOrderType; +pub use spot_order_price::OrderPrice; diff --git a/src/types/spot_order_type.rs b/src/types/order_type.rs similarity index 80% rename from src/types/spot_order_type.rs rename to src/types/order_type.rs index c18ec7c..97bb98a 100644 --- a/src/types/spot_order_type.rs +++ b/src/types/order_type.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::cw_serde; #[cw_serde] -pub enum SpotOrderType { +pub enum OrderType { StopLoss, LimitSell, LimitBuy, diff --git a/src/types/spot_order/impls/new.rs b/src/types/spot_order/impls/new.rs index 2f7cc71..f6e5784 100644 --- a/src/types/spot_order/impls/new.rs +++ b/src/types/spot_order/impls/new.rs @@ -3,8 +3,8 @@ use cosmwasm_std::{Addr, Coin}; impl SpotOrder { pub fn new( - order_type: SpotOrderType, - order_price: SpotOrderPrice, + order_type: OrderType, + order_price: OrderPrice, order_amount: Coin, owner_address: Addr, order_target_denom: String, diff --git a/src/types/spot_order/impls/new_dummy.rs b/src/types/spot_order/impls/new_dummy.rs index ad68a09..28db732 100644 --- a/src/types/spot_order/impls/new_dummy.rs +++ b/src/types/spot_order/impls/new_dummy.rs @@ -5,11 +5,11 @@ impl SpotOrder { pub fn new_dummy() -> SpotOrder { SpotOrder { order_id: 777, - order_type: SpotOrderType::StopLoss, + order_type: OrderType::StopLoss, order_amount: coin(1000, "btc"), owner_address: Addr::unchecked("user"), order_amm_routes: vec![], - order_price: SpotOrderPrice { + order_price: OrderPrice { quote_denom: "eth".to_string(), base_denom: "btc".to_string(), rate: Decimal::from_atomics(Uint128::new(5), 0).unwrap(), diff --git a/src/types/spot_order/spot_order.rs b/src/types/spot_order/spot_order.rs index caada60..3cb7c90 100644 --- a/src/types/spot_order/spot_order.rs +++ b/src/types/spot_order/spot_order.rs @@ -1,12 +1,12 @@ -use crate::types::{spot_order_type::SpotOrderType, SpotOrderPrice, SwapAmountInRoute}; +use crate::types::{order_type::OrderType, OrderPrice, SwapAmountInRoute}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin}; #[cw_serde] pub struct SpotOrder { - pub order_type: SpotOrderType, + pub order_type: OrderType, pub order_id: u64, - pub order_price: SpotOrderPrice, + pub order_price: OrderPrice, pub order_amount: Coin, pub owner_address: Addr, pub order_target_denom: String, diff --git a/src/types/spot_order_price.rs b/src/types/spot_order_price.rs index 506395f..6302c2c 100644 --- a/src/types/spot_order_price.rs +++ b/src/types/spot_order_price.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Decimal; #[cw_serde] -pub struct SpotOrderPrice { +pub struct OrderPrice { pub base_denom: String, pub quote_denom: String, pub rate: Decimal,