mostro_client/cli/
new_order.rs

1use crate::cli::Context;
2use crate::parser::orders::print_order_preview;
3use crate::util::{print_dm_events, send_dm, uppercase_first, wait_for_dm};
4use anyhow::Result;
5use mostro_core::prelude::*;
6use std::collections::HashMap;
7use std::io::{stdin, stdout, BufRead, Write};
8use std::process;
9use std::str::FromStr;
10use uuid::Uuid;
11
12pub type FiatNames = HashMap<String, String>;
13
14#[allow(clippy::too_many_arguments)]
15pub async fn execute_new_order(
16    kind: &str,
17    fiat_code: &str,
18    fiat_amount: &(i64, Option<i64>),
19    amount: &i64,
20    payment_method: &str,
21    premium: &i64,
22    invoice: &Option<String>,
23    ctx: &Context,
24    expiration_days: &i64,
25) -> Result<()> {
26    // Uppercase currency
27    let fiat_code = fiat_code.to_uppercase();
28    // Check if fiat currency selected is available on Yadio and eventually force user to set amount
29    // this is in the case of crypto <--> crypto offer for example
30    if *amount == 0 {
31        // Get Fiat list
32        let api_req_string = "https://api.yadio.io/currencies".to_string();
33        let fiat_list_check = reqwest::get(api_req_string)
34            .await?
35            .json::<FiatNames>()
36            .await?
37            .contains_key(&fiat_code);
38        if !fiat_list_check {
39            println!("{} is not present in the fiat market, please specify an amount with -a flag to fix the rate", fiat_code);
40            process::exit(0);
41        }
42    }
43    let kind = uppercase_first(kind);
44    // New check against strings
45    let kind_checked = mostro_core::order::Kind::from_str(&kind)
46        .map_err(|_| anyhow::anyhow!("Invalid order kind"))?;
47    let expires_at = match *expiration_days {
48        0 => None,
49        _ => {
50            let now = chrono::Utc::now();
51            let expires_at = now + chrono::Duration::days(*expiration_days);
52            Some(expires_at.timestamp())
53        }
54    };
55
56    // Get the type of neworder
57    // if both tuple field are valid than it's a range order
58    // otherwise use just fiat amount value as before
59    let amt = if fiat_amount.1.is_some() {
60        (0, Some(fiat_amount.0), fiat_amount.1)
61    } else {
62        (fiat_amount.0, None, None)
63    };
64
65    let small_order = SmallOrder::new(
66        None,
67        Some(kind_checked),
68        Some(Status::Pending),
69        *amount,
70        fiat_code.clone(),
71        amt.1,
72        amt.2,
73        amt.0,
74        payment_method.to_owned(),
75        *premium,
76        None,
77        None,
78        invoice.as_ref().to_owned().cloned(),
79        Some(0),
80        expires_at,
81    );
82
83    // Create new order for mostro
84    let order_content = Payload::Order(small_order.clone());
85
86    // Print order preview
87    let ord_preview = print_order_preview(order_content.clone())
88        .map_err(|e| anyhow::anyhow!("Failed to generate order preview: {}", e))?;
89    println!("{ord_preview}");
90    let mut user_input = String::new();
91    let _input = stdin();
92    print!("Check your order! Is it correct? (Y/n) > ");
93    stdout().flush()?;
94
95    let mut answer = stdin().lock();
96    answer.read_line(&mut user_input)?;
97
98    match user_input.to_lowercase().as_str().trim_end() {
99        "y" | "" => {}
100        "n" => {
101            println!("Ok you have cancelled the order, create another one please");
102            process::exit(0);
103        }
104        &_ => {
105            println!("Can't get what you're sayin!");
106            process::exit(0);
107        }
108    };
109    let request_id = Uuid::new_v4().as_u128() as u64;
110    // Create NewOrder message
111    let message = Message::new_order(
112        None,
113        Some(request_id),
114        Some(ctx.trade_index),
115        Action::NewOrder,
116        Some(order_content),
117    );
118
119    // Send dm to receiver pubkey
120    println!(
121        "SENDING DM with trade index: {} and trade keys: {:?}",
122        ctx.trade_index,
123        ctx.trade_keys.public_key().to_hex()
124    );
125
126    // Serialize the message
127    let message_json = message
128        .as_json()
129        .map_err(|_| anyhow::anyhow!("Failed to serialize message"))?;
130
131    // Send the DM
132    let sent_message = send_dm(
133        &ctx.client,
134        Some(&ctx.identity_keys),
135        &ctx.trade_keys,
136        &ctx.mostro_pubkey,
137        message_json,
138        None,
139        false,
140    );
141
142    // Wait for the DM to be sent from mostro
143    let recv_event = wait_for_dm(ctx, None, sent_message).await?;
144
145    // Parse the incoming DM
146    print_dm_events(recv_event, request_id, ctx, None).await?;
147
148    Ok(())
149}