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