mostro_client/cli/
take_order.rs

1use anyhow::Result;
2use lnurl::lightning_address::LightningAddress;
3use mostro_core::prelude::*;
4use std::str::FromStr;
5use uuid::Uuid;
6
7use crate::cli::Context;
8use crate::lightning::is_valid_invoice;
9use crate::parser::common::{
10    create_emoji_field_row, create_field_value_header, create_standard_table,
11};
12use crate::util::{print_dm_events, send_dm, wait_for_dm};
13
14/// Create payload based on action type and parameters
15fn create_take_order_payload(
16    action: Action,
17    invoice: &Option<String>,
18    amount: Option<u32>,
19) -> Result<Option<Payload>> {
20    match action {
21        Action::TakeBuy => Ok(amount.map(|amt: u32| Payload::Amount(amt as i64))),
22        Action::TakeSell => Ok(Some(match invoice {
23            Some(inv) => {
24                let initial_payload = match LightningAddress::from_str(inv) {
25                    Ok(_) => Payload::PaymentRequest(None, inv.to_string(), None),
26                    Err(_) => match is_valid_invoice(inv) {
27                        Ok(i) => Payload::PaymentRequest(None, i.to_string(), None),
28                        Err(e) => {
29                            println!("{}", e);
30                            Payload::PaymentRequest(None, inv.to_string(), None)
31                        }
32                    },
33                };
34
35                match amount {
36                    Some(amt) => match initial_payload {
37                        Payload::PaymentRequest(a, b, _) => {
38                            Payload::PaymentRequest(a, b, Some(amt as i64))
39                        }
40                        payload => payload,
41                    },
42                    None => initial_payload,
43                }
44            }
45            None => amount
46                .map(|amt| Payload::Amount(amt.into()))
47                .unwrap_or(Payload::Amount(0)),
48        })),
49        _ => Err(anyhow::anyhow!("Invalid action for take order")),
50    }
51}
52
53/// Unified function to handle both take buy and take sell orders
54#[allow(clippy::too_many_arguments)]
55pub async fn execute_take_order(
56    order_id: &Uuid,
57    action: Action,
58    invoice: &Option<String>,
59    amount: Option<u32>,
60    ctx: &Context,
61) -> Result<()> {
62    let action_name = match action {
63        Action::TakeBuy => "take buy",
64        Action::TakeSell => "take sell",
65        _ => return Err(anyhow::anyhow!("Invalid action for take order")),
66    };
67
68    println!("🛒 Take Order");
69    println!("═══════════════════════════════════════");
70    let mut table = create_standard_table();
71    table.set_header(create_field_value_header());
72    table.add_row(create_emoji_field_row("📈 ", "Action", action_name));
73    table.add_row(create_emoji_field_row(
74        "📋 ",
75        "Order ID",
76        &order_id.to_string(),
77    ));
78    if let Some(inv) = invoice {
79        table.add_row(create_emoji_field_row("⚡ ", "Invoice", inv));
80    }
81    if let Some(amt) = amount {
82        table.add_row(create_emoji_field_row(
83            "💰 ",
84            "Amount (sats)",
85            &amt.to_string(),
86        ));
87    }
88    table.add_row(create_emoji_field_row(
89        "🎯 ",
90        "Mostro PubKey",
91        &ctx.mostro_pubkey.to_string(),
92    ));
93    println!("{table}");
94    println!("💡 Taking order from Mostro...\n");
95
96    // Create payload based on action type
97    let payload = create_take_order_payload(action.clone(), invoice, amount)?;
98
99    // Create request id
100    let request_id = Uuid::new_v4().as_u128() as u64;
101
102    // Create message
103    let take_order_message = Message::new_order(
104        Some(*order_id),
105        Some(request_id),
106        Some(ctx.trade_index),
107        action.clone(),
108        payload,
109    );
110
111    // Send dm to receiver pubkey
112    println!("📤 Sending Message");
113    println!("─────────────────────────────────────");
114    println!("🔢 Trade Index: {}", ctx.trade_index);
115    println!("🔑 Trade Keys: {}", ctx.trade_keys.public_key().to_hex());
116    println!("💡 Sending DM to Mostro...");
117    println!();
118
119    let message_json = take_order_message
120        .as_json()
121        .map_err(|_| anyhow::anyhow!("Failed to serialize message"))?;
122
123    // Send the DM
124    // This is so we can wait for the gift wrap event in the main thread
125    let sent_message = send_dm(
126        &ctx.client,
127        Some(&ctx.identity_keys),
128        &ctx.trade_keys,
129        &ctx.mostro_pubkey,
130        message_json,
131        None,
132        false,
133    );
134
135    // Wait for the DM to be sent from mostro
136    let recv_event = wait_for_dm(ctx, None, sent_message).await?;
137
138    // Parse the incoming DM
139    print_dm_events(recv_event, request_id, ctx, None).await?;
140
141    Ok(())
142}