mostro_client/cli/
new_order.rs1use anyhow::Result;
2use mostro_core::prelude::*;
3use nostr_sdk::prelude::*;
4use std::collections::HashMap;
5use std::io::{stdin, stdout, BufRead, Write};
6use std::process;
7use std::str::FromStr;
8use uuid::Uuid;
9
10use crate::db::{connect, Order, User};
11use crate::pretty_table::print_order_preview;
12use crate::util::{send_message_sync, uppercase_first};
13
14pub type FiatNames = HashMap<String, String>;
15
16#[allow(clippy::too_many_arguments)]
17pub async fn execute_new_order(
18 kind: &str,
19 fiat_code: &str,
20 fiat_amount: &(i64, Option<i64>),
21 amount: &i64,
22 payment_method: &str,
23 premium: &i64,
24 invoice: &Option<String>,
25 identity_keys: &Keys,
26 trade_keys: &Keys,
27 trade_index: i64,
28 mostro_key: PublicKey,
29 client: &Client,
30 expiration_days: &i64,
31) -> Result<()> {
32 let fiat_code = fiat_code.to_uppercase();
34 if *amount == 0 {
37 let api_req_string = "https://api.yadio.io/currencies".to_string();
39 let fiat_list_check = reqwest::get(api_req_string)
40 .await?
41 .json::<FiatNames>()
42 .await?
43 .contains_key(&fiat_code);
44 if !fiat_list_check {
45 println!("{} is not present in the fiat market, please specify an amount with -a flag to fix the rate", fiat_code);
46 process::exit(0);
47 }
48 }
49 let kind = uppercase_first(kind);
50 let kind_checked = mostro_core::order::Kind::from_str(&kind).unwrap();
52 let expires_at = match *expiration_days {
53 0 => None,
54 _ => {
55 let now = chrono::Utc::now();
56 let expires_at = now + chrono::Duration::days(*expiration_days);
57 Some(expires_at.timestamp())
58 }
59 };
60
61 let amt = if fiat_amount.1.is_some() {
65 (0, Some(fiat_amount.0), fiat_amount.1)
66 } else {
67 (fiat_amount.0, None, None)
68 };
69 let small_order = SmallOrder::new(
70 None,
71 Some(kind_checked),
72 Some(Status::Pending),
73 *amount,
74 fiat_code.clone(),
75 amt.1,
76 amt.2,
77 amt.0,
78 payment_method.to_owned(),
79 *premium,
80 None,
81 None,
82 invoice.as_ref().to_owned().cloned(),
83 Some(0),
84 expires_at,
85 );
86
87 let order_content = Payload::Order(small_order.clone());
89
90 let ord_preview = print_order_preview(order_content.clone()).unwrap();
92 println!("{ord_preview}");
93 let mut user_input = String::new();
94 let _input = stdin();
95 print!("Check your order! Is it correct? (Y/n) > ");
96 stdout().flush()?;
97
98 let mut answer = stdin().lock();
99 answer.read_line(&mut user_input)?;
100
101 match user_input.to_lowercase().as_str().trim_end() {
102 "y" | "" => {}
103 "n" => {
104 println!("Ok you have cancelled the order, create another one please");
105 process::exit(0);
106 }
107 &_ => {
108 println!("Can't get what you're sayin!");
109 process::exit(0);
110 }
111 };
112 let request_id = Uuid::new_v4().as_u128() as u64;
113 let message = Message::new_order(
115 None,
116 Some(request_id),
117 Some(trade_index),
118 Action::NewOrder,
119 Some(order_content),
120 );
121
122 let dm = send_message_sync(
123 client,
124 Some(identity_keys),
125 trade_keys,
126 mostro_key,
127 message,
128 true,
129 false,
130 )
131 .await?;
132 let order_id = dm
133 .iter()
134 .find_map(|el| {
135 let message = el.0.get_inner_message_kind();
136 if message.request_id == Some(request_id) {
137 match message.action {
138 Action::NewOrder => {
139 if let Some(Payload::Order(order)) = message.payload.as_ref() {
140 return order.id;
141 }
142 }
143 Action::CantDo => {
144 if let Some(Payload::CantDo(Some(cant_do_reason))) = &message.payload {
145 match cant_do_reason {
146 CantDoReason::OutOfRangeFiatAmount | CantDoReason::OutOfRangeSatsAmount => {
147 println!("Error: Amount is outside the allowed range. Please check the order's min/max limits.");
148 }
149 _ => {
150 println!("Unknown reason: {:?}", message.payload);
151 }
152 }
153 } else {
154 println!("Unknown reason: {:?}", message.payload);
155 return None;
156 }
157 }
158 _ => {
159 println!("Unknown action: {:?}", message.action);
160 return None;
161 }
162 }
163 }
164 None
165 })
166 .or_else(|| {
167 println!("Error: No matching order found in response");
168 None
169 });
170
171 if let Some(order_id) = order_id {
172 println!("Order id {} created", order_id);
173 let pool = connect().await?;
175 let db_order = Order::new(&pool, small_order, trade_keys, Some(request_id as i64))
176 .await
177 .map_err(|e| anyhow::anyhow!("Failed to create DB order: {:?}", e))?;
178 match User::get(&pool).await {
180 Ok(mut user) => {
181 user.set_last_trade_index(trade_index);
182 if let Err(e) = user.save(&pool).await {
183 println!("Failed to update user: {}", e);
184 }
185 }
186 Err(e) => println!("Failed to get user: {}", e),
187 }
188 let db_order_id = db_order
189 .id
190 .clone()
191 .ok_or(anyhow::anyhow!("Missing order id"))?;
192 Order::save_new_id(&pool, db_order_id, order_id.to_string()).await?;
193 }
194 Ok(())
195}