From ac419c0da1cae3cdecb6c5d238c731ce2bbe485b Mon Sep 17 00:00:00 2001 From: grunch Date: Mon, 11 May 2026 16:16:14 -0300 Subject: [PATCH 1/2] feat: bump mostro-core to 0.11.0 and handle anti-abuse bond flow The Mostro daemon now ships the taker's anti-abuse bond hold-invoice as a separate Action::PayBondInvoice before the trade hold-invoice. With mostro-core 0.10 the variant was unknown, so the rumor failed to deserialize and takesell/takebuy responses surfaced as "Error serializing message" with no follow-up. Bumps mostro-core to 0.11.0 and wires the new Action::PayBondInvoice and Status::WaitingTakerBond variants into the DM parser/display so takers see the bond invoice and the trade continues to the existing PayInvoice flow afterwards. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/parser/dms.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8fc0078..577b884 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1550,9 +1550,9 @@ dependencies = [ [[package]] name = "mostro-core" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76f4936f520e410ba2abf47113548ae231322209261a9b3a8aefbd10a869082" +checksum = "2f73dc932127909d84e64a3dd1a5cd0e9b6549fdef3eb6ec02a1de26a71472f9" dependencies = [ "bitcoin", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 2fe36bd..61e90c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ reqwest = { version = "0.12.23", default-features = false, features = [ "json", "rustls-tls", ] } -mostro-core = "0.10.0" +mostro-core = "0.11.0" lnurl-rs = { version = "0.9.0", default-features = false, features = ["ureq"] } pretty_env_logger = "0.5.0" sqlx = { version = "0.8.6", features = ["sqlite", "runtime-tokio-rustls"] } diff --git a/src/parser/dms.rs b/src/parser/dms.rs index fdbb0db..f2bc958 100644 --- a/src/parser/dms.rs +++ b/src/parser/dms.rs @@ -78,6 +78,26 @@ fn handle_pay_invoice_display(order: &Option, in println!(); } +fn handle_pay_bond_invoice_display(order: &Option, invoice: &str) { + print_section_header("🪙 Anti-Abuse Bond Invoice"); + if let Some(order) = order { + if let Some(order_id) = order.id { + println!("📋 Order ID: {}", order_id); + } + print_amount_info(order.amount); + print_fiat_code(&order.fiat_code); + println!("💵 Fiat Amount: {}", order.fiat_amount); + } + println!(); + println!("⚡ LIGHTNING BOND INVOICE TO PAY:"); + println!("─────────────────────────────────────"); + println!("{}", invoice); + println!("─────────────────────────────────────"); + println!("💡 Pay this hold invoice to lock your taker bond."); + println!("💡 The trade hold invoice will arrive next."); + println!(); +} + /// Format payload details for DM table display fn format_payload_details(payload: &Payload, action: &Action) -> String { match payload { @@ -113,6 +133,7 @@ fn format_payload_details(payload: &Payload, action: &Action) -> String { Status::Expired => "⏰", Status::SettledHoldInvoice => "💰", Status::InProgress => "🔄", + Status::WaitingTakerBond => "🪙", }; let kind_emoji = match o.kind.as_ref().unwrap_or(&mostro_core::order::Kind::Sell) { @@ -486,6 +507,33 @@ pub async fn print_commands_results(message: &MessageKind, ctx: &Context) -> Res } Ok(()) } + // mostro-core 0.11: anti-abuse bond invoice sent right after a takebuy/takesell. + Action::PayBondInvoice => { + if let Some(Payload::PaymentRequest(order, invoice, _)) = &message.payload { + handle_pay_bond_invoice_display(order, invoice); + + if let Some(order) = order { + if let Some(req_id) = message.request_id { + if let Err(e) = save_order( + order.clone(), + &ctx.trade_keys, + req_id, + ctx.trade_index, + &ctx.pool, + ) + .await + { + println!("❌ Failed to save order: {}", e); + return Err(anyhow::anyhow!("Failed to save order: {}", e)); + } + print_success_message("Order saved successfully!"); + } else { + return Err(anyhow::anyhow!("No request id found in message")); + } + } + } + Ok(()) + } Action::CantDo => { println!("❌ Action Cannot Be Completed"); println!("═══════════════════════════════════════"); @@ -844,6 +892,7 @@ pub async fn print_direct_messages( let action_icon = match inner.action { Action::NewOrder => "🆕", Action::AddInvoice | Action::PayInvoice => "⚡", + Action::PayBondInvoice => "🪙", Action::FiatSent | Action::FiatSentOk => "💸", Action::Release | Action::Released => "🔓", Action::Cancel | Action::Canceled => "🚫", From 130791fcad36ea91896b50fd8e758d19b5993c7d Mon Sep 17 00:00:00 2001 From: grunch Date: Mon, 11 May 2026 17:22:57 -0300 Subject: [PATCH 2/2] fix: error out on malformed PayBondInvoice payload The PayBondInvoice arm in print_commands_results silently returned Ok(()) when the payload was not a PaymentRequest, and when the PaymentRequest carried order = None. Both paths skip save_order, so the CLI loses the order context the bond flow depends on while reporting success. Tighten the arm to return Err with the unexpected payload variant or the missing-order condition, and only return Ok(()) after save_order succeeds. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/parser/dms.rs | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/parser/dms.rs b/src/parser/dms.rs index f2bc958..aed1fc4 100644 --- a/src/parser/dms.rs +++ b/src/parser/dms.rs @@ -509,29 +509,32 @@ pub async fn print_commands_results(message: &MessageKind, ctx: &Context) -> Res } // mostro-core 0.11: anti-abuse bond invoice sent right after a takebuy/takesell. Action::PayBondInvoice => { - if let Some(Payload::PaymentRequest(order, invoice, _)) = &message.payload { - handle_pay_bond_invoice_display(order, invoice); - - if let Some(order) = order { - if let Some(req_id) = message.request_id { - if let Err(e) = save_order( - order.clone(), - &ctx.trade_keys, - req_id, - ctx.trade_index, - &ctx.pool, - ) - .await - { - println!("❌ Failed to save order: {}", e); - return Err(anyhow::anyhow!("Failed to save order: {}", e)); - } - print_success_message("Order saved successfully!"); - } else { - return Err(anyhow::anyhow!("No request id found in message")); - } + let (order, invoice) = match &message.payload { + Some(Payload::PaymentRequest(order, invoice, _)) => (order, invoice), + other => { + return Err(anyhow::anyhow!( + "PayBondInvoice expected Payload::PaymentRequest, got: {:?}", + other + )); } - } + }; + handle_pay_bond_invoice_display(order, invoice); + let order = order.as_ref().ok_or_else(|| { + anyhow::anyhow!("PayBondInvoice payload is missing the SmallOrder") + })?; + let req_id = message + .request_id + .ok_or_else(|| anyhow::anyhow!("No request id found in message"))?; + save_order( + order.clone(), + &ctx.trade_keys, + req_id, + ctx.trade_index, + &ctx.pool, + ) + .await + .map_err(|e| anyhow::anyhow!("Failed to save order: {}", e))?; + print_success_message("Order saved successfully!"); Ok(()) } Action::CantDo => {