fix(trades): sync maker trade status and hold invoice from daemon - #96
Conversation
The maker's TradeInfo in the DB was never updated after creation: - The local UUID was not replaced with the daemon-assigned UUID, so tradeStatusProvider polled with a stale ID and never found the order in the order book cache — status stayed "Pending" forever. - Action::PayInvoice gift wraps were unhandled, so the hold invoice was never extracted or saved — the seller never saw the payment screen when a buyer took their order. - Kind 38383 status changes were reflected in the order book cache but never persisted to the trades DB, causing stale status on app restart. Adds update_trade_order_id and update_trade_fields to the Storage trait and wires them into the order subscription loop, single-order subscription, and gift-wrap rumor dispatch for PayInvoice and other status actions.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughAdded two new persistent storage APIs to sync trades ( Changes
Sequence Diagram(s)sequenceDiagram
participant Daemon
participant OrdersAPI
participant DB
participant OrderBook
Daemon->>OrdersAPI: gift-wrap Action (e.g., PayInvoice / other Actions)
OrdersAPI->>OrdersAPI: map Action -> Option<OrderStatus>, extract bolt11/amount
OrdersAPI->>DB: update_trade_fields(order_id, status?, hold_invoice?, amount?)
DB-->>OrdersAPI: Ok/Err (best-effort)
OrdersAPI->>OrderBook: upsert/update in-memory order
Note right of OrdersAPI: For Kind 38383 global subscription\nalso: replace local UUID -> daemon UUID
OrdersAPI->>DB: update_trade_order_id(local_id, daemon_id)
DB-->>OrdersAPI: Ok/Err (best-effort)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@rust/src/api/orders.rs`:
- Around line 972-975: The match arm that builds (bolt11, amount) from
kind.payload for Payload::PaymentRequest currently uses a direct cast (a as u64)
which will wrap negative i64 values; change this to use a checked conversion
(e.g., u64::try_from(a)) and handle the Result by mapping Ok(v) to Some(v) and
treating Err(_) as None (or logging the invalid signed amount) so negative
amounts are not converted into huge unsigned values; update the matching branch
in the code that constructs (bolt11, amount) for Payload::PaymentRequest to
perform this checked conversion and add an appropriate log/error path.
In `@rust/src/db/indexeddb.rs`:
- Around line 111-127: The two no-op methods update_trade_order_id and
update_trade_fields currently return Ok(()) and silently hide missing IndexedDB
persistence; either implement persistence using indexed_db_futures for the web
target (open the DB, start a transaction, update the relevant trade record(s)
and commit) or change both methods to return an explicit error (e.g.,
Err(Error::new("IndexedDB persistence not implemented"))) so callers won’t
assume success; locate these functions in indexeddb.rs and replace the Ok(())
stubs with the proper indexed_db_futures-based persistence logic or a clear Err
until full support is added.
In `@rust/src/db/sqlite.rs`:
- Around line 355-389: The current update_trade_order_id and update_trade_fields
implementations perform a read-modify-write by calling get_trade_by_order_id and
then save_trade, which can cause lost updates under concurrency; change them to
perform atomic updates directly in SQL (use a single UPDATE with json_set to
modify the JSON blob) or run the read/modify/write inside a DB transaction with
row locking (e.g., SELECT ... FOR UPDATE) so concurrent tasks cannot clobber
each other; update the functions update_trade_order_id and update_trade_fields
to execute the atomic UPDATE statement(s) (targeting the JSON path(s) for
order.id, order.status, hold_invoice, order.amount_sats) instead of calling
get_trade_by_order_id/save_trade, or wrap that sequence in a transaction lock to
guarantee serialization.
🪄 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
Run ID: fa76e07e-6d47-4179-b1af-471a497127d1
📒 Files selected for processing (4)
rust/src/api/orders.rsrust/src/db/indexeddb.rsrust/src/db/mod.rsrust/src/db/sqlite.rs
- PayInvoice amount conversion uses u64::try_from instead of bare cast to avoid wrapping negative values into huge unsigned amounts. - SQLite update_trade_order_id and update_trade_fields now use single UPDATE statements with json_set instead of read-modify-write to avoid lost updates under concurrency. - IndexedDB stubs log a warning so missing web persistence is visible during development, matching the existing save_mostro_node pattern.
The maker's TradeInfo in the DB was never updated after creation:
The local UUID was not replaced with the daemon-assigned UUID, so tradeStatusProvider polled with a stale ID and never found the order in the order book cache — status stayed "Pending" forever.
Action::PayInvoice gift wraps were unhandled, so the hold invoice was never extracted or saved — the seller never saw the payment screen when a buyer took their order.
Kind 38383 status changes were reflected in the order book cache but never persisted to the trades DB, causing stale status on app restart.
Adds update_trade_order_id and update_trade_fields to the Storage trait and wires them into the order subscription loop, single-order subscription, and gift-wrap rumor dispatch for PayInvoice and other status actions.
Summary by CodeRabbit
New Features
Bug Fixes