Skip to content

feat(add-bond-invoice): implement AddBondInvoice action#169

Merged
grunch merged 4 commits into
mainfrom
feat/add-bond-invoice
May 21, 2026
Merged

feat(add-bond-invoice): implement AddBondInvoice action#169
grunch merged 4 commits into
mainfrom
feat/add-bond-invoice

Conversation

@grunch
Copy link
Copy Markdown
Member

@grunch grunch commented May 21, 2026

Summary

Bumps mostro-core 0.11.10.11.3 and implements the AddBondInvoice action (protocol: Bond payout invoice), the counterparty-direction dual of pay-bond-invoice.

mostro-core 0.11.1 → 0.11.3 changes adopted

  • 0.11.2: Action::AddBondInvoice.
  • 0.11.3: Payload::BondPayoutRequest(BondPayoutRequest { order, slashed_at }); get_payment_request() extended to AddBondInvoice; BondPayoutRequest exported from the prelude.

CLI changes — both directions of the flow

  • Outbound (counterparty → Mostro): new addbondinvoice -o <id> -i <invoice> command that replies to a slash with a bolt11 for the counterparty's share, signed with the order's trade key (Payload::PaymentRequest). A wait timeout is treated as the happy path (Mostro pays from its wallet without acking); only a cant-do response is surfaced as an error.
  • Inbound (Mostro → counterparty): renders the BondPayoutRequest in both print_commands_results and getdm, computing the forfeit deadline locally from the on-wire slashed_at (never the local clock) plus the node's bond_payout_claim_window_days, per the protocol.
  • New fetch_bond_claim_window_days helper reads the bond_payout_claim_window_days tag from the kind-38385 info event, queried lazily from getdm only when a bond payout request is actually present.
  • README command reference updated; parser test added for the request rendering across known/unknown claim windows.

Testing

  • cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, and cargo test all pass.
  • Verified read-only against a live mainnet Mostro node (daemon 0.17.3, bonds enabled): fetch_bond_claim_window_days returned Some(1) matching the node's bond_payout_claim_window_days tag, and the deadline math is correct.
  • Not exercised: the full take → bond → slash → reply cycle (moves real funds / requires solver role on mainnet).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added addbondinvoice CLI command to submit invoices for bond payout requests.
    • DM display now shows bond payout requests with a calculated forfeit/deadline and clearer submission/timeout feedback.
  • Chores

    • Bumped core dependency to the latest patch release.

Review Change Stack

Bump mostro-core 0.11.1 -> 0.11.3, which adds Action::AddBondInvoice and
the Payload::BondPayoutRequest variant, and implement both directions of
the bond payout invoice flow in the CLI:

- Outbound (counterparty -> Mostro): new `addbondinvoice -o <id> -i <inv>`
  command that replies to a slash with a bolt11 for the counterparty's
  share, signed with the order's trade key (Payload::PaymentRequest).
  A timeout while waiting is treated as success since Mostro pays without
  acknowledging; only `cant-do` is surfaced on failure.

- Inbound (Mostro -> counterparty): render the BondPayoutRequest in both
  print_commands_results and getdm, computing the forfeit deadline locally
  from the on-wire `slashed_at` (never local clock) plus the node's
  `bond_payout_claim_window_days` info-event tag.

- Add fetch_bond_claim_window_days helper to read the kind-38385 info
  event tag, queried lazily from getdm only when a request is present.

Update README command reference and add a parser test for the request
rendering across known/unknown claim windows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f193eb54-bb94-4bfd-9c38-c9373a5d7d77

📥 Commits

Reviewing files that changed from the base of the PR and between a29e90f and 0a3d077.

📒 Files selected for processing (5)
  • src/cli/add_bond_invoice.rs
  • src/parser/dms.rs
  • src/util/events.rs
  • src/util/messaging.rs
  • src/util/mod.rs

Walkthrough

Adds an addbondinvoice CLI command to submit a bond invoice via Nostr DM, introduces a distinct DM-timeout error, fetches Mostro's bond-claim-window config, and updates DM rendering and tests to show computed bond forfeit deadlines.

Changes

Bond Invoice Submission and Display

Layer / File(s) Summary
Dependency update
Cargo.toml
mostro-core bumped from 0.11.1 to 0.11.3.
CLI command registration and documentation
src/cli.rs, README.md
Adds Commands::AddBondInvoice { order_id, invoice }, module/import/dispatcher wiring, and README addbondinvoice entry.
Bond invoice submission handler
src/cli/add_bond_invoice.rs
execute_add_bond_invoice loads order/trade keys, validates invoice, constructs AddBondInvoice message, sends DM to Mostro, and awaits optional reply (timeout handled).
DM retrieval integration with bond request handling
src/cli/get_dm.rs
Detect inbound AddBondInvoice with BondPayoutRequest, conditionally fetch claim-window days, and pass it to DM printing.
Bond claim window information fetching
src/util/events.rs, src/util/mod.rs
Adds fetch_bond_claim_window_days to read Mostro info events and re-exports it; returns Option<i64>.
DM display for bond payout requests with deadlines
src/parser/dms.rs
Extend print_direct_messages signature with claim_window_days, compute/display bond forfeit deadline for Payload::BondPayoutRequest, add Action::AddBondInvoice handling and set icon to 💰.
Distinct DM wait timeout error
src/util/messaging.rs
Introduce WaitForDmTimeout and preserve timeout as distinct error path in wait_for_dm.
Test suite updates for DM display changes
tests/parser_dms.rs
Update DM-printing tests to new parameter shape and add print_dms_with_bond_payout_request test.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Catrya

Poem

🐰 I hopped a DM through Mostro's night,
Sent an invoice neat and bright,
Claim windows fetched and deadlines shown,
A tiny rabbit's work well-grown,
Hop, send, and balance set to right.

🚥 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 pull request title 'feat(add-bond-invoice): implement AddBondInvoice action' directly and accurately describes the main change—implementing the AddBondInvoice action feature, which aligns with the PR's primary objective across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-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 and usage tips.

Copy link
Copy Markdown

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

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: a29e90f1a5

ℹ️ 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 src/cli/add_bond_invoice.rs Outdated
Comment thread src/parser/dms.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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)
src/cli/add_bond_invoice.rs (1)

53-75: 💤 Low value

Unwrap the redundant Option around payload.

payload is typed as Option<Payload> but is always Some(...) once the else branch returns Err. Constructing the value directly is clearer and matches the actual contract.

♻️ Proposed simplification
-    // Parse invoice (Lightning address or BOLT11) and build payload
-    let ln_addr = LightningAddress::from_str(invoice);
-    let payload = if ln_addr.is_ok() {
-        Some(Payload::PaymentRequest(None, invoice.to_string(), None))
-    } else {
-        match is_valid_invoice(invoice) {
-            Ok(i) => Some(Payload::PaymentRequest(None, i.to_string(), None)),
-            Err(e) => {
-                return Err(anyhow::anyhow!("Invalid invoice: {}", e));
-            }
-        }
-    };
+    // Parse invoice (Lightning address or BOLT11) and build payload
+    let payment_request = if LightningAddress::from_str(invoice).is_ok() {
+        invoice.to_string()
+    } else {
+        is_valid_invoice(invoice)
+            .map(|i| i.to_string())
+            .map_err(|e| anyhow::anyhow!("Invalid invoice: {}", e))?
+    };
+    let payload = Some(Payload::PaymentRequest(None, payment_request, None));
🤖 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 `@src/cli/add_bond_invoice.rs` around lines 53 - 75, The payload variable is
wrapped in an unnecessary Option even though errors return early; change payload
to be a Payload (not Option<Payload>) by assigning either
Payload::PaymentRequest(...) from the LightningAddress::from_str branch or from
the is_valid_invoice match branch, and then pass Some(payload) into
Message::new_order; update references to payload, Payload::PaymentRequest,
LightningAddress::from_str, is_valid_invoice, Message::new_order, and
Action::AddBondInvoice accordingly.
🤖 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 `@src/cli/add_bond_invoice.rs`:
- Around line 93-106: The match in add_bond_invoice treats any Err from
wait_for_dm(...) as a successful submission; change it to detect the
timeout-specific error from wait_for_dm and only print the “✅ Bond payout
invoice submitted…” messages in that timeout/happy-path case, while propagating
or logging any other Err values (originating from ctx.client.subscribe,
sent_message.await, etc.) back to the caller or as an actual failure. Update
wait_for_dm to return a typed error or ensure it maps timeout to a
distinguishable variant, then match on that variant in add_bond_invoice (keeping
print_dm_events(...) on Ok) and handle non-timeout errors by returning Err or
printing an error message so send/sign/transport failures aren’t misreported as
success.

---

Nitpick comments:
In `@src/cli/add_bond_invoice.rs`:
- Around line 53-75: The payload variable is wrapped in an unnecessary Option
even though errors return early; change payload to be a Payload (not
Option<Payload>) by assigning either Payload::PaymentRequest(...) from the
LightningAddress::from_str branch or from the is_valid_invoice match branch, and
then pass Some(payload) into Message::new_order; update references to payload,
Payload::PaymentRequest, LightningAddress::from_str, is_valid_invoice,
Message::new_order, and Action::AddBondInvoice accordingly.
🪄 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: 1573ea97-d3c1-460c-a67f-49c07040c916

📥 Commits

Reviewing files that changed from the base of the PR and between c08b8a5 and a29e90f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • Cargo.toml
  • README.md
  • src/cli.rs
  • src/cli/add_bond_invoice.rs
  • src/cli/get_dm.rs
  • src/parser/dms.rs
  • src/util/events.rs
  • src/util/mod.rs
  • tests/parser_dms.rs

Comment thread src/cli/add_bond_invoice.rs
…sion

Previously any error from wait_for_dm was reported as "invoice submitted",
so a subscribe/sign/transport failure where the reply never went out was
misreported as success. Introduce a distinguishable WaitForDmTimeout error
and only show the success message on that timeout (the genuine no-reply
happy path), propagating every other error.

Also drop the unnecessary Option wrapper around the payload, which is
always Some by the time it is used (invalid invoices return early).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@mostronatorcoder mostronatorcoder Bot left a comment

Choose a reason for hiding this comment

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

Strict review on current head 5c047126fb7f6afd164fb2d237486742f85cf031.

One issue left before this is mergeable:

  1. src/parser/dms.rs:148 prints mostro-cli addbondinvoice --orderid ..., but the actual clap long flag generated from order_id is --order-id (or -o). Following the printed command literally will fail, so the user guidance for the new inbound AddBondInvoice flow is wrong right now.

I ran:

  • cargo test
  • cargo clippy --all-targets --all-features -- -D warnings

Both pass on this head, but the bad printed command is still a real UX bug in the new flow, so I'm requesting changes.

The inbound bond-payout hint printed the long flag `--orderid` (correct,
since the Commands enum's `rename_all = "lower"` renames `order_id` to
`--orderid`), but the unusual form is easy to misread. Switch the hint to
the short `-o`/`-i` flags, which are unambiguous and match the README's
convention for these commands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@ermeme ermeme Bot left a comment

Choose a reason for hiding this comment

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

Hermes Agent Review

I found two correctness issues that should be fixed before merge. See inline comments.

Comment thread src/util/events.rs
Comment thread src/cli/add_bond_invoice.rs Outdated
@grunch
Copy link
Copy Markdown
Member Author

grunch commented May 21, 2026

Re: the src/parser/dms.rs finding that the hint should print --order-id instead of --orderid — this was a false positive.

The Commands enum carries #[clap(rename_all = "lower")], which renames the order_id field's long flag to --orderid (lowercase, no separator), not the clap default --order-id. Verified on the reviewed commit:

$ mostro-cli addbondinvoice --help
Usage: mostro-cli addbondinvoice --orderid <ORDER_ID> --invoice <INVOICE>
Options:
  -o, --orderid <ORDER_ID>  Order id
  -i, --invoice <INVOICE>   Invoice string

$ mostro-cli addbondinvoice --order-id abc --invoice def
error: unexpected argument '--order-id' found
  tip: a similar argument exists: '--orderid'

So the originally printed --orderid was correct and copy-pasteable; --order-id would actually have failed.

That said, since --orderid is an unusual form that's easy to misread, I switched the hint to the short flags (addbondinvoice -o <id> -i <bolt11>) in 1a7c3e7 — unambiguous and consistent with how the README documents these commands.

- fetch_bond_claim_window_days: select the kind-38385 info event by max
  created_at instead of relying on limit(1) + .first(), so a lagging or
  multi-relay setup can't surface a stale bond_payout_claim_window_days and
  render the wrong, user-facing forfeit deadline.

- execute_add_bond_invoice: the protocol's bond-payout reply is a bolt11, so
  stop forwarding Lightning Addresses verbatim (they'd bounce back as
  cant-do/invalid-invoice from Mostro). Validate the bolt11 locally and fail
  fast with a clear error; drop the now-unused LightningAddress/FromStr imports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@grunch grunch merged commit 15ffb45 into main May 21, 2026
6 checks passed
@grunch grunch deleted the feat/add-bond-invoice branch May 21, 2026 15:32
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.

1 participant