The kind-14 ingest (dispatch_mostro_message in rust/src/api/orders.rs) has no arm
for an inbound Action::AddInvoice — it falls into the catch-all that only logs.
AddInvoice is only handled as a reply to our own take (classify_take_reply).
But add-invoice is exactly what the daemon sends to a maker buyer: they create
a buy order, a seller takes it and pays the hold invoice, and the daemon then asks
the buyer for their invoice. Since the message is dropped, the trade status never
becomes WaitingBuyerInvoice, the UI never offers the add-invoice action, and the
daemon cancels the order by timeout.
Reproduction (real daemon, order 56c1d85b)
- Created a buy order as maker; a seller took it.
23:34:59 — WaitingSellerToPay arrives → handled, status synced to WaitingPayment.
23:35:20 — AddInvoice with Payload::Order(status=WaitingBuyerInvoice, amount=484)
arrives, decrypts fine — and nothing happens (no status log line follows; catch-all).
23:35:21 — a second AddInvoice with Payload::Peer (counterparty reputation) — also dropped.
23:36:35 — daemon cancels the order by timeout: the user was never asked to act.
Proposed fix
Add an Action::AddInvoice arm to the ingest, mirroring the existing PayInvoice arm:
Payload::Order variant → persist status WaitingBuyerInvoice + sats amount to the
order book and trade DB (keyed by the message's order_id — the message arrives with
trade_index=None on the global subscription, so the order id is the only key).
Payload::Peer variant → ignore for now (reputation info).
With the status persisted, the existing MyOrderScreen polling already navigates the
maker buyer to the add-invoice screen (waitingBuyerInvoice && !isSelling branch).
Side effect fixed too: the buyer's trade no longer shows the misleading WaitingPayment
status (inherited from WaitingSellerToPay) while it's actually the buyer who must act.
The kind-14 ingest (
dispatch_mostro_messageinrust/src/api/orders.rs) has no armfor an inbound
Action::AddInvoice— it falls into the catch-all that only logs.AddInvoiceis only handled as a reply to our own take (classify_take_reply).But
add-invoiceis exactly what the daemon sends to a maker buyer: they createa buy order, a seller takes it and pays the hold invoice, and the daemon then asks
the buyer for their invoice. Since the message is dropped, the trade status never
becomes
WaitingBuyerInvoice, the UI never offers the add-invoice action, and thedaemon cancels the order by timeout.
Reproduction (real daemon, order
56c1d85b)23:34:59—WaitingSellerToPayarrives → handled, status synced toWaitingPayment.23:35:20—AddInvoicewithPayload::Order(status=WaitingBuyerInvoice, amount=484)arrives, decrypts fine — and nothing happens (no status log line follows; catch-all).
23:35:21— a secondAddInvoicewithPayload::Peer(counterparty reputation) — also dropped.23:36:35— daemon cancels the order by timeout: the user was never asked to act.Proposed fix
Add an
Action::AddInvoicearm to the ingest, mirroring the existingPayInvoicearm:Payload::Ordervariant → persist statusWaitingBuyerInvoice+ sats amount to theorder book and trade DB (keyed by the message's
order_id— the message arrives withtrade_index=Noneon the global subscription, so the order id is the only key).Payload::Peervariant → ignore for now (reputation info).With the status persisted, the existing
MyOrderScreenpolling already navigates themaker buyer to the add-invoice screen (
waitingBuyerInvoice && !isSellingbranch).Side effect fixed too: the buyer's trade no longer shows the misleading
WaitingPaymentstatus (inherited from
WaitingSellerToPay) while it's actually the buyer who must act.