Skip to content

feat(bond): recognize pay-bond-invoice and add taker bond payment flow - #213

Open
AndreaDiazCorreia wants to merge 29 commits into
mainfrom
feat/taker-pay-bond-invoice
Open

feat(bond): recognize pay-bond-invoice and add taker bond payment flow#213
AndreaDiazCorreia wants to merge 29 commits into
mainfrom
feat/taker-pay-bond-invoice

Conversation

@AndreaDiazCorreia

@AndreaDiazCorreia AndreaDiazCorreia commented Jul 23, 2026

Copy link
Copy Markdown
Member

Closes #208. Part of #145 (anti-abuse bond).

Summary

Phase 1 (taker) of the anti-abuse bond feature. When a taker takes an order on a bond-requiring node, the daemon replies with pay-bond-invoice (a Lightning hold invoice for the taker's anti-abuse bond) instead of progressing the trade. Previously the client rejected that reply with a stable BondRequired marker; this PR replaces the rejection with the real taker bond flow: the bond hold invoice is shown to the taker, and once it is paid the trade advances into the normal take flow.

What changed

Rust (rust/src/api/)

  • Retired the BondRequired rejection branch in classify_take_reply.
  • Added OrderStatus::WaitingTakerBond and mapped it in map_core_status / status_for_action (maker-side WaitingMakerBond stays unmapped — that is Phase 5: maker anti-abuse bond on order creation #191).
  • Added a dedicated bond_invoice field to TradeInfo and DaemonReply::TakeAccepted, kept distinct from hold_invoice (see "Verified against the daemon" below).
  • Added an Action::AddInvoice dispatcher arm so the buyer's trade advances correctly after the bond is paid.

Dart (lib/)

  • New PayBondInvoiceScreen (route /pay_bond_invoice/:orderId) reusing the QR / NWC / external-wallet payment pattern of the seller hold-invoice screen. It watches the order status and forwards the taker to the next step once the bond is paid: add-invoice (buyer), pay-invoice (seller), or trade detail.
  • take_order_screen routes to the bond screen when the take reply comes back as WaitingTakerBond, and the BondRequired error branch was removed.
  • Added tradeBondInfoProvider and the waitingTakerBond cases to the exhaustive OrderStatus switches.

l10n: new bond-screen strings across all 5 locales; retired bondRequired.

Verified against the daemon

Cross-checked against mostro (daemon), mostro-cli, and v1 mobile, which surfaced two issues fixed here:

  1. The daemon stamps the pay-bond-invoice payload's SmallOrder with the wire-mapped Pending status (WaitingTakerBond is never exposed on the NIP-69 wire), so classify_take_reply now derives the bond status from the action, not the payload. Without this the client would never route to the bond screen.
  2. On bond payment the daemon both republishes the Kind 38383 event to WaitingPayment and sends the pay-invoice gift wrap. Reusing the hold_invoice slot for the bond raced these two: the pay-invoice screen could latch the stale bond bolt11. The dedicated bond_invoice field removes the collision (hold_invoice stays null until the escrow arrives).

Testing

  • cargo test (110 passing) and cargo clippy clean.
  • flutter analyze clean and flutter test (120 passing).
  • flutter_rust_bridge bindings regenerated and verified in sync.

Out of scope / follow-ups

  • Cancelling from the bond screen currently pops (consistent with the seller hold-invoice screen). v1 sends a cancel to release the bond; replicating that release path is a candidate follow-up.
  • Re-entering the bond screen from My Trades after popping is not wired yet.
  • Maker-side bond on order creation is tracked separately in Phase 5: maker anti-abuse bond on order creation #191.

Summary by CodeRabbit

  • New Features
    • Added an anti-abuse taker-bond “pay invoice” flow with deep-link navigation and a new bond-payment screen (loading/waiting state, invoice QR, copy/share, and payment confirmation).
    • Introduced a dedicated “waiting for bond” trade status and updated primary actions to route into the bond-payment flow.
  • Bug Fixes
    • Improved bond/invoice progression handling with more resilient polling, one-time navigation as status changes, and updated cancellation/backout behavior.
    • Fixed bond countdown timing to use the bond window while in the bond step, then switch afterward.
  • Documentation
    • Updated localized UI copy for the bond-payment experience across supported languages (including leave/release dialogs and backout messaging).
  • Tests
    • Added coverage for the bond-payment screen, countdown, and new bond state transitions.

Add PayBondInvoiceScreen to handle taker bond payment when taking orders on bond-requiring nodes. The daemon replies with pay-bond-invoice instead of progressing the trade, placing the order at waitingTakerBond status. The screen displays the bond hold invoice as QR code with copy/share/lightning-URI actions, supports NWC auto-payment with manual fallback, and watches order status to forward the taker to the next step (add-invoice for buyers, pay-invoice for sellers, or trade-detail)
Move taker anti-abuse bond hold invoice from `holdInvoice` to dedicated `bondInvoice` field in TradeInfo to prevent collision with escrow hold invoice when trade advances past bond payment. Update classify_take_reply to use action (not payload status) as authoritative for pay-bond-invoice replies since daemon stamps embedded SmallOrder with wire-mapped Pending.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR adds end-to-end taker bond invoice support: Rust recognizes and persists bond invoices, Flutter receives the new status, routes takers to a payment screen, supports multiple Lightning payment methods, updates trade filtering and countdowns, handles bond cancellation, and adds localized UI strings.

Changes

Taker bond invoice flow

Layer / File(s) Summary
Daemon reply and order-state handling
rust/src/api/types.rs, rust/src/api/orders.rs, specs/004-mostro-p2p-client/contracts/*
Adds WaitingTakerBond and separate bond fields, classifies and persists bond-payment replies, handles post-bond AddInvoice, protects fine-grained states from coarse updates, and tests transitions and cancellation behavior.
Rust-Flutter data serialization
rust/src/frb_generated.rs, specs/004-mostro-p2p-client/data-model.md, specs/004-mostro-p2p-client/contracts/types.md
Extends SSE and Dart serialization and documents the new status and bond fields.
Payment screen and take-order navigation
lib/core/app_routes.dart, lib/features/order/..., lib/shared/widgets/nwc_payment_widget.dart
Routes bond-requiring takes to PayBondInvoiceScreen, polls for invoice data, supports NWC and manual Lightning payment, and manages cancellation and payment recovery.
Status presentation and localized payment copy
lib/features/trades/..., lib/l10n/*, test/features/..., test/support/fake_trades.dart
Maps and presents the waiting-bond state, selects bond-specific deadlines and actions, adds translated copy in five locales, and validates the flow with provider and widget tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • MostroP2P/app#199 — Both changes modify trade-detail cancellation and destructive-action handling.

Suggested reviewers: grunch, catrya, bracr10

Sequence Diagram(s)

sequenceDiagram
  participant TakeOrderScreen
  participant OrdersAPI
  participant PayBondInvoiceScreen
  participant LightningWallet
  TakeOrderScreen->>OrdersAPI: takeOrder
  OrdersAPI-->>TakeOrderScreen: WaitingTakerBond and bond invoice
  TakeOrderScreen->>PayBondInvoiceScreen: Open payment route
  PayBondInvoiceScreen->>LightningWallet: Pay invoice
  OrdersAPI-->>PayBondInvoiceScreen: Trade status updates
Loading

Poem

A rabbit hops where invoices gleam,
QR squares sparkle like a dream.
Wallets chime, the spinner waits,
Once paid, the trade moves on.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the taker bond payment flow and pay-bond-invoice recognition.
Linked Issues check ✅ Passed The changes satisfy #208: BondRequired is retired, WaitingTakerBond is added, the bond invoice is shown, and flow continues after payment.
Out of Scope Changes check ✅ Passed The additional routing, localization, tests, and support logic all appear to directly support the taker bond flow rather than unrelated work.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat/taker-pay-bond-invoice
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/taker-pay-bond-invoice

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 315412b9fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/src/api/orders.rs
Comment thread rust/src/api/types.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
rust/src/api/orders.rs (1)

1931-1996: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Correct, but lacks dedicated unit coverage.

The amount-extraction/status-sync logic here is new business logic (mirrors what classify_take_reply does) but, unlike that function, isn't exercised by any unit test in mod tests — it can only be verified indirectly through dispatch_mostro_message, which requires heavy nostr/gift-wrap scaffolding to test. Consider extracting the small "derive amount from the Order payload" step into a pure helper (similar to status_for_action/map_core_status) so it can be unit-tested directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/src/api/orders.rs` around lines 1931 - 1996, Extract the AddInvoice
amount derivation from the Action::AddInvoice arm into a small pure helper that
accepts the payload and returns the optional u64 amount, preserving the existing
positive-amount and non-Order behavior. Add focused unit tests in mod tests
covering valid, non-positive, missing, and non-Order payloads, then reuse the
helper in the status-sync flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/order/screens/pay_bond_invoice_screen.dart`:
- Around line 131-167: Remove the trade amount lookup from the bond invoice
payment flow in the screen’s data builder and stop passing
trade.order.amountSats to NwcPaymentWidget or the manual payInvoiceAmount
fallback. Use the bond invoice’s own amount parsed from the invoice, or the
dedicated bond amount field if one is available in the Rust/contracts model, so
both payment paths charge the bond hold amount.

In `@lib/features/trades/screens/trade_detail_screen.dart`:
- Around line 153-154: Update the status mapping around
OrderStatus.waitingTakerBond so it remains distinct from
OrderStatus.waitingPayment, using a dedicated TradeStatus and UI action for
taker bond payment. Ensure the trade detail screen routes this state to the
bond-payment flow rather than payInvoicePath, with appropriate bond-specific
copy and timer handling; leave ordinary waitingPayment behavior unchanged.

In `@rust/src/api/orders.rs`:
- Around line 259-273: Update the documentation for the bond invoice response
near the order reply classification to match the implementation: state that bond
replies use the separate bond_invoice field, leave hold_invoice unset, and are
distinguished by the action rather than a WaitingTakerBond payload status.
Remove the outdated claim that bond_invoice shares the hold_invoice slot or that
Dart identifies it through WaitingTakerBond.

---

Nitpick comments:
In `@rust/src/api/orders.rs`:
- Around line 1931-1996: Extract the AddInvoice amount derivation from the
Action::AddInvoice arm into a small pure helper that accepts the payload and
returns the optional u64 amount, preserving the existing positive-amount and
non-Order behavior. Add focused unit tests in mod tests covering valid,
non-positive, missing, and non-Order payloads, then reuse the helper in the
status-sync flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 122530d1-1461-40e3-9681-449e35e6d158

📥 Commits

Reviewing files that changed from the base of the PR and between 0d84721 and 315412b.

📒 Files selected for processing (21)
  • lib/core/app_routes.dart
  • lib/features/order/providers/trade_state_provider.dart
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • lib/features/order/screens/take_order_screen.dart
  • lib/features/trades/providers/trades_providers.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • rust/src/api/orders.rs
  • rust/src/api/types.rs
  • rust/src/frb_generated.rs
  • test/features/trades/filtered_trades_provider_test.dart

Comment thread lib/features/order/screens/pay_bond_invoice_screen.dart Outdated
Comment thread lib/features/trades/screens/trade_detail_screen.dart Outdated
Comment thread rust/src/api/orders.rs Outdated
…ut retry

Gate add-invoice state advancement on order being WaitingTakerBond to handle only the post-bond resume case. When a buyer's payout exhausts retries the daemon re-sends add-invoice while the order remains SettledHoldInvoice (sats locked in escrow). Advancing that to WaitingBuyerInvoice would misrender the trade as pre-escrow with cancellation controls.
…oc comment

Update documentation to reflect that pay-bond-invoice replies route the bond bolt11 into the dedicated bond_invoice field rather than hold_invoice, preventing collision with the trade's escrow hold invoice. Clarify that Dart discriminates bond payment by WaitingTakerBond status to route takers to the bond payment screen.
…ing trade amount

Add dedicated bond_amount_sats field to TradeInfo and DaemonReply::TakeAccepted to carry the bond hold invoice amount separately from the trade's amount_sats. Update classify_take_reply to route bond amounts into bond_amount_sats (never amount_sats) when action is PayBondInvoice, preventing the bond figure from overwriting the order's trade amount.
…yment

Split waitingTakerBond from waitingPayment in TradeStatus enum to distinguish taker bond payment from escrow hold invoice flow. Map OrderStatus.waitingTakerBond to new TradeStatus.waitingBond state, render dedicated headline/button routing to pay-bond-invoice screen instead of pay-invoice. Add tradeStatusWaitingBond and tradeHeadlineWaitingBond localization strings across all locales.
Add WaitingTakerBond to OrderStatus enum documentation across contracts/types.md and data-model.md. Document bond_invoice and bond_amount_sats fields in TradeInfo type, clarifying they remain distinct from hold_invoice/amount_sats to prevent collision when trade advances past bond payment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/trades/screens/trade_detail_screen.dart`:
- Line 159: Update _getInstructionText and _timerContext to explicitly handle
TradeStatus.waitingBond, using the localized bond-payment instruction and
corresponding bond deadline context so reopened trades retain the correct
payment state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a85deed4-1dd8-4809-8727-cd2ea2eaa1bc

📥 Commits

Reviewing files that changed from the base of the PR and between 315412b and 8575b41.

📒 Files selected for processing (19)
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • rust/src/api/orders.rs
  • rust/src/api/types.rs
  • rust/src/frb_generated.rs
  • specs/004-mostro-p2p-client/contracts/orders.md
  • specs/004-mostro-p2p-client/contracts/types.md
  • specs/004-mostro-p2p-client/data-model.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • lib/l10n/app_it.arb
  • lib/l10n/app_de.arb
  • lib/l10n/app_localizations_it.dart
  • lib/l10n/app_en.arb
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_es.arb
  • lib/l10n/app_localizations_fr.dart
  • rust/src/api/types.rs
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • rust/src/api/orders.rs

Comment thread lib/features/trades/screens/trade_detail_screen.dart
… detail screen

Add tradeInstructionWaitingBond to render bond payment instruction when status is waitingBond. Add tradeTimerWaitingBondLabel and map waitingBond to same consequence as waitingInvoice/waitingPayment (order returns to book on timeout). Add localization strings across all locales.
…tatus

Add public_status_supersedes guard to prevent coarse NIP-69 buckets (Pending/InProgress) from overwriting fine-grained gift-wrap statuses (WaitingTakerBond/WaitingBuyerInvoice/WaitingPayment) in subscribe_single_order. Terminal statuses always supersede. Flip tradeStatusProvider to prefer persisted trade row over in-memory book since book carries daemon's coarse public status while trade row holds authoritative gift-wrap state.
Strip verbose explanations from bond invoice code comments and docstrings across Rust and Dart. Reduce multi-sentence rationales to concise statements while preserving essential context about bond_invoice/bond_amount_sats separation from hold_invoice/amount_sats, WaitingTakerBond routing, and wire-mapped Pending status handling.
…vances correctly

Add integration test over real SQLite store confirming WaitingTakerBond resists coarse Pending/InProgress buckets from public order book sync, advances to WaitingBuyerInvoice/WaitingPayment with per-role amounts, and yields only to terminal statuses. Update tradeAmountProvider to prefer persisted trade row over order book to surface daemon's calculated per-role sats rather than coarse public 38383 figure.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/order/providers/trade_state_provider.dart`:
- Around line 20-26: Update the polling loop in tradeStatusProvider to catch
transient exceptions from listTrades and getOrder, preserve the stream, and
continue polling using the existing retry behavior established by
tradeBondInfoProvider. Ensure successful sats results still yield and return as
before, and add targeted tests covering failures from both API calls followed by
a successful retry.
- Around line 21-26: Update the trade polling logic around the `trade` lookup so
`getOrder` is called only when no matching trade exists; when a trade row is
present with null `amountSats`, yield null and continue polling instead of using
the public order amount. Add a regression test covering that case with a
non-null public amount and verify polling does not stop.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4c8d372-a586-4389-81ba-9d5499398935

📥 Commits

Reviewing files that changed from the base of the PR and between 8575b41 and 20ea565.

📒 Files selected for processing (17)
  • lib/features/order/providers/trade_state_provider.dart
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • lib/features/order/screens/take_order_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • rust/src/api/orders.rs
  • rust/src/api/types.rs
🚧 Files skipped from review as they are similar to previous changes (14)
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_de.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_it.dart
  • lib/l10n/app_localizations_en.dart
  • rust/src/api/types.rs
  • lib/l10n/app_en.arb
  • lib/features/order/screens/take_order_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/features/order/screens/pay_bond_invoice_screen.dart

Comment thread lib/features/order/providers/trade_state_provider.dart Outdated
Comment thread lib/features/order/providers/trade_state_provider.dart Outdated
… and return order to book

Add _confirmAndCancel method to pay_bond_invoice_screen that sends real cancel request to daemon when taker backs out before paying bond, releasing bond without slash and returning order to book instead of stranding at WaitingTakerBond. Wire cancel button to confirmation dialog and add waitingBond to cancellable statuses set in trade_detail_screen.
…ng and book handling

Add cancelBondBackoutDialogContent localization string to clarify that cancelling before bond payment is unilateral and returns order to book without affecting counterparty. Update pay_bond_invoice_screen and trade_detail_screen to detect WaitingTakerBond status and render bond-specific messaging/snackbar. Modify cancel_order to classify pre-commit cancels (WaitingTakerBond/Pending) and reset book entry to Pending instead of removing it
…y_bond_invoice_screen

Extract _waitingIndicator method to deduplicate CircularProgressIndicator/waitingForPaymentConfirmation text block used in both NWC and manual payment modes. Replace inline Column widgets with method call in both branches.
…nd_invoice_screen

Extract _cancelButton method to deduplicate OutlinedButton/destructiveRed styling used in both NWC and manual payment modes. Replace inline SizedBox/OutlinedButton blocks with method call in both branches. Restructure NWC body from Center to Column with Spacer to position cancel button at bottom when not waiting.
Wrap tradeAmountProvider and tradeStatusProvider poll bodies in try-catch to prevent exceptions from breaking the polling loop. Log failures with debugPrint and continue polling after delay instead of terminating stream on transient API errors.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
rust/src/api/orders.rs (1)

3326-3364: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert bond fields remain empty for non-bond replies.

The classification contract requires normal PayInvoice, AddInvoice, and action-only replies to keep bond_invoice and bond_amount_sats unset, but these test arms do not assert that invariant. Add those assertions to prevent accidental invoice-field leakage.

As per coding guidelines, add targeted tests when expanding protocol handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/src/api/orders.rs` around lines 3326 - 3364, Extend the relevant
`classify_take_reply` test arms for normal `PayInvoice`, `AddInvoice`, and
action-only replies to assert that `bond_invoice` and `bond_amount_sats` remain
unset. Update the `DaemonReply::TakeAccepted` pattern bindings as needed while
preserving the existing status, amount, and hold-invoice assertions, and add
targeted coverage for any action-only reply path.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/src/api/orders.rs`:
- Around line 1340-1350: Implement the IndexedDB-backed trade lookup used by the
is_precommit check and the Action::AddInvoice flow so it returns the persisted
trade and observes WaitingTakerBond. Replace .ok().flatten() with explicit error
handling that does not treat database failures as a missing trade, while
preserving the existing transition behavior for valid statuses; update both
affected paths.

---

Nitpick comments:
In `@rust/src/api/orders.rs`:
- Around line 3326-3364: Extend the relevant `classify_take_reply` test arms for
normal `PayInvoice`, `AddInvoice`, and action-only replies to assert that
`bond_invoice` and `bond_amount_sats` remain unset. Update the
`DaemonReply::TakeAccepted` pattern bindings as needed while preserving the
existing status, amount, and hold-invoice assertions, and add targeted coverage
for any action-only reply path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 63aa3ee0-7253-4cba-bfc1-9ee518451be5

📥 Commits

Reviewing files that changed from the base of the PR and between 20ea565 and cdd737f.

📒 Files selected for processing (15)
  • lib/features/order/providers/trade_state_provider.dart
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • rust/src/api/orders.rs
🚧 Files skipped from review as they are similar to previous changes (12)
  • lib/l10n/app_fr.arb
  • lib/l10n/app_localizations_it.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_en.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations_en.dart
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • lib/features/order/providers/trade_state_provider.dart
  • lib/features/trades/screens/trade_detail_screen.dart

Comment thread rust/src/api/orders.rs Outdated

@grunch grunch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes on cdd737f2129792e5d48626b08ced0db52d18db75. Three correctness blockers remain:

  1. The unresolved IndexedDB finding is valid: the new post-bond AddInvoice and pre-commit cancel gates depend on get_trade_by_order_id, but the WASM backend always returns None. The paid-bond flow therefore cannot advance on web.
  2. The bond screen's implicit back paths bypass the only protocol-aware cancellation path and can strand an unpaid take at WaitingTakerBond until timeout (inline).
  3. tradeAmountProvider still falls back to the coarse public order amount when an authoritative persisted trade exists with a temporarily-null per-role amount, so it can stop polling with the wrong invoice amount (inline).

The exact-head Rust tests pass (113 passed, 6 ignored), and all GitHub checks are green, but those checks do not exercise the affected Flutter navigation/provider paths or the WASM persistence transition.

Comment thread lib/features/order/screens/pay_bond_invoice_screen.dart
Comment thread lib/features/order/providers/trade_state_provider.dart Outdated

@grunch grunch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supplemental exact-head review after completing the full parallel audit. These are additional blockers discovered after the first review was submitted; they are consolidated here to keep the remediation checklist stable:

  1. The cancellation state machine conflates maker cancellation with taker bond back-out, and the later Action::Canceled handling reverses the intended taker book restoration (inline).
  2. The coarse-status guard protects only the persisted trade; both public ingestion paths can still overwrite the in-memory order book with Pending/InProgress (inline).
  3. Payment launch and destructive cancellation can race while the Lightning outcome is unknown (inline).
  4. The newly presented bond countdown uses the public listing expiration rather than TradeInfo.timeoutAt (inline).

Please also replace the mirrored bond-flow test with coverage that exercises the real dispatcher/order-book path. As a non-blocking documentation follow-up, specs/004-mostro-p2p-client/contracts/orders.md still describes bonds as unsupported and cancel_order as maker-only.

Comment thread rust/src/api/orders.rs Outdated
Comment thread rust/src/api/orders.rs Outdated
Comment thread lib/features/order/screens/pay_bond_invoice_screen.dart
Comment thread lib/features/trades/screens/trade_detail_screen.dart
…n addition to trade row

Extract followed_trade_state helper to retrieve authoritative (status, amount_sats) from persisted trade row. Add merge_followed_state to overlay trade state onto incoming public order before upserting to book cache. Apply guard in both subscribe_single_order and ingest_order_event so book cache never contradicts trade row with coarse public status downgrades.
…r taker back-out

Add is_mine check to cancel_order so only taker pre-commit back-outs reset book entry to Pending; maker withdrawals and cooperative cancels drop the order. Mirror logic in dispatch_mostro_message Canceled handler to preserve non-owned orders and let public 38383 events govern them.
…creen

Track bond payment lifecycle through idle/launched/confirming phases to prevent cancellation races when payment outcome is unknown. Add PopScope handler that blocks navigation during in-flight payments and presents three-way dialog (keep paying/leave/release) when bond is unpaid. Mark payment as launched on wallet open, invoice copy, share, and NWC dispatch so settling bonds cannot be cancelled.
…ade status

Add _resolveDeadline helper that returns trade's timeoutAt while WaitingTakerBond, otherwise falls back to public listing expiresAt. Extract _epochSeconds to handle PlatformInt64 conversion. Add listener to tradeStatusProvider that reloads deadline when status changes so countdown reflects correct timeout window instead of stale listing expiry.
…cel_order contract docs

Rewrite tradeAmountProvider comment to explain that persisted trade row is authoritative for per-role sats and order book fallback only applies when no trade is followed, preventing premature polling termination with coarse public amount. Restructure ternary to explicit if-else for clarity. Update cancel_order contract to document ownership-based book handling: taker pre-commit back-outs reset to Pending, maker withdraw
…and unify public order sync

Extract classify_cancel and cancel_drops_listing helpers to encapsulate ownership-based cancel decision logic, replacing inline is_taker_backout and owned checks. Introduce CancelEffect enum to make cancel_order's book handling explicit. Extract sync_public_order_update to deduplicate d-tag event processing between subscribe_single_order and future ingest_order_event paths. Add comprehensive tests covering

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/l10n/app_en.arb (1)

179-180: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use natural status wording.

Waiting Bond is ungrammatical and unclear. Use Waiting for bond or, preferably, Bond payment required to make the required action explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/l10n/app_en.arb` around lines 179 - 180, Update the
tradeStatusWaitingBond localization value to use clear, natural status wording,
preferably “Bond payment required,” while preserving its existing description
metadata.
🧹 Nitpick comments (2)
test/features/order/screens/pay_bond_invoice_screen_test.dart (1)

198-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for tapping the NWC pay button.

This group verifies the idle-state render and back-dialog behavior, but nothing taps payWithWalletButton to exercise onPaymentStarted/onPaymentSuccess/onFallbackToManual. Adding this (once the underlying bug flagged in pay_bond_invoice_screen.dart is fixed) would have caught the widget being disposed before the payment outcome resolves. As per coding guidelines, add targeted tests when expanding complex logic, asynchronous workflows, or protocol handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/features/order/screens/pay_bond_invoice_screen_test.dart` around lines
198 - 215, Add a widget test in the “PayBondInvoiceScreen with an NWC wallet
connected” group that taps l10n.payWithWalletButton and awaits the payment
outcome, covering onPaymentStarted, onPaymentSuccess, and onFallbackToManual
without disposing the screen prematurely. Reuse _pumpBondScreen and _settle, and
assert the resulting UI for the resolved payment or manual fallback path.

Source: Coding guidelines

lib/features/order/providers/trade_state_provider.dart (1)

63-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

tradeStatusProvider's getOrder fallback bypasses the new bridge seam.

Line 68 still calls orders_api.getOrder(orderId: orderId) directly, while tradeAmountProvider above was refactored to go through ref.read(bridgeGetOrderProvider). This is the exact seam this PR introduced specifically so bridge calls are substitutable "since the bridge is uninitialised under flutter test" (per the doc comment on lines 6-8) — leaving this call untouched means the getOrder-fallback branch of tradeStatusProvider can't be unit-tested the same way, and it's an easy-to-miss inconsistency for future readers.

♻️ Proposed fix
-        final info = await orders_api.getOrder(orderId: orderId);
+        final info = await getOrder(orderId);

(assuming getOrder is bound from ref.read(bridgeGetOrderProvider) the same way as in tradeAmountProvider)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/order/providers/trade_state_provider.dart` around lines 63 - 72,
Update the getOrder fallback in tradeStatusProvider to obtain and invoke the
bridge-backed callable from ref.read(bridgeGetOrderProvider), matching
tradeAmountProvider. Remove the direct orders_api.getOrder call while preserving
the existing null check, status yield, and terminal-status return behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/order/screens/pay_bond_invoice_screen.dart`:
- Around line 188-204: The _PaymentPhase.confirming branch in _footer needs a
recovery action instead of rendering only _waitingIndicator. Provide the same
cancel or “not paid yet” escape available during _PaymentPhase.launched, and
update _handleBackIntent/PopScope handling as needed so confirming users can
exit or recover when daemon confirmation never arrives, while preserving the
waiting indicator.

In `@lib/l10n/app_localizations_en.dart`:
- Around line 245-255: Update the leave-bond-payment localization text so the
action clearly returns the order to the book rather than releasing the bond: in
lib/l10n/app_localizations_en.dart lines 245-255, replace “release it now” with
explicit order-return wording; apply the equivalent order/commande wording in
lib/l10n/app_localizations_fr.dart lines 251-261 and order/ordine wording in
lib/l10n/app_localizations_it.dart lines 249-259. Keep the button labels and
other text unchanged.

---

Outside diff comments:
In `@lib/l10n/app_en.arb`:
- Around line 179-180: Update the tradeStatusWaitingBond localization value to
use clear, natural status wording, preferably “Bond payment required,” while
preserving its existing description metadata.

---

Nitpick comments:
In `@lib/features/order/providers/trade_state_provider.dart`:
- Around line 63-72: Update the getOrder fallback in tradeStatusProvider to
obtain and invoke the bridge-backed callable from
ref.read(bridgeGetOrderProvider), matching tradeAmountProvider. Remove the
direct orders_api.getOrder call while preserving the existing null check, status
yield, and terminal-status return behavior.

In `@test/features/order/screens/pay_bond_invoice_screen_test.dart`:
- Around line 198-215: Add a widget test in the “PayBondInvoiceScreen with an
NWC wallet connected” group that taps l10n.payWithWalletButton and awaits the
payment outcome, covering onPaymentStarted, onPaymentSuccess, and
onFallbackToManual without disposing the screen prematurely. Reuse
_pumpBondScreen and _settle, and assert the resulting UI for the resolved
payment or manual fallback path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66024996-b136-4753-b4dc-fa3aa59902ac

📥 Commits

Reviewing files that changed from the base of the PR and between cdd737f and d38228a.

📒 Files selected for processing (21)
  • lib/features/order/providers/trade_state_provider.dart
  • lib/features/order/screens/pay_bond_invoice_screen.dart
  • lib/features/trades/screens/trade_detail_screen.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • lib/shared/widgets/nwc_payment_widget.dart
  • rust/src/api/orders.rs
  • specs/004-mostro-p2p-client/contracts/orders.md
  • test/features/order/providers/trade_amount_provider_test.dart
  • test/features/order/screens/pay_bond_invoice_screen_test.dart
  • test/features/trades/trade_detail_countdown_test.dart
  • test/support/fake_trades.dart
🚧 Files skipped from review as they are similar to previous changes (4)
  • lib/l10n/app_it.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_localizations_es.dart
  • specs/004-mostro-p2p-client/contracts/orders.md

Comment thread lib/features/order/screens/pay_bond_invoice_screen.dart
Comment thread lib/l10n/app_localizations_en.dart Outdated
… leave-bond dialog copy

Replace direct orders_api calls with injected bridgeListTradesProvider and bridgeGetOrderProvider to enable widget testing. Add tests verifying tradeStatusProvider prefers trade row status over coarse public bucket and falls back to book when no trade exists. Update leaveBondPaymentContent and releaseOrderButton strings across all locales to clarify that releasing "puts the order back on the book" / "returns
Add bond_invoice, bond_amount_sats fields to Rust TradeRow test fixture and rating, totalReviews, daysActive fields to Dart OrderInfo test fixture to match updated data structures.

@Catrya Catrya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an order is in the waiting-buyer-invoice or waiting-payment status and the taker doesn't respond, the order returns to the Pending status. However, the maker's app doesn't update to Pending, it stays on the previous status. This doesn't happen on main.

Steps to reproduce:

  1. User A creates a sell order.
  2. User B takes the order and pays the bond.
  3. Mostro asks User B to provide a Lightning invoice.
  4. User A sees the order in the waiting-buyer-invoice status.
  5. User B does not provide the invoice and lets the timeout expire.
  6. The order returns to the Pending status, but User A continues to see waiting-buyer-invoice instead of Pending.

The daemon returns an unanswered WaitingBuyerInvoice or WaitingPayment to
the book as Pending; the maker must follow instead of staying stranded on
the stale fine-grained status. public_status_supersedes now rejects only
the bucket that already corresponds to what we hold (no information), so
every other bucket — including a return to Pending — is a real transition.
@AndreaDiazCorreia
AndreaDiazCorreia requested a review from Catrya July 31, 2026 21:40

@Catrya Catrya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s weird because sometimes it works fine and other times it doesn't. In these cases, all the orders should appear as "pending" since the timeout is 3 minutes and the new "pending" message has already been sent, but not all of them are updating.
Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 1 (taker): recognize pay-bond-invoice and retire the BondRequired rejection

3 participants