Skip to content

Commit 6cfd7e6

Browse files
authored
Merge pull request #44 from MostroP2P/docs/add-bond-invoice-action
docs: document add-bond-invoice action for bond payout flow
2 parents 6ae34c4 + c9b7526 commit 6cfd7e6

6 files changed

Lines changed: 120 additions & 1 deletion

File tree

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- [Take buy order](./take_buy.md)
1818
- [Take range buy order](./take_buy_range_order.md)
1919
- [Pay bond invoice](./pay_bond_invoice.md)
20+
- [Bond payout invoice](./add_bond_invoice.md)
2021
- [Seller pays hold invoice](./seller_pay_hold_invoice.md)
2122
- [Fiat sent](./fiatsent.md)
2223
- [Release](./release.md)

src/add_bond_invoice.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Bond payout invoice
2+
3+
The `add-bond-invoice` action is the counterparty-direction dual of [`pay-bond-invoice`](./pay_bond_invoice.md): where `pay-bond-invoice` asks a taker to lock a bond at the start of a trade, `add-bond-invoice` asks the non-slashed counterparty to provide a Lightning invoice for their share of a bond that has just been slashed. The flow is bi-directional — Mostro first sends a request message, the counterparty replies with a bolt11 — and it only fires after a solver-directed slash via [Admin Settle order](./admin_settle_order.md) / [Admin Cancel order](./admin_cancel_order.md) or a timeout-driven slash.
4+
5+
## Direction and trigger
6+
7+
- **Mostro → counterparty (request).** Mostro emits this message after a [`bond_resolution`](./admin_settle_order.md#bond-resolution-payload) settles the *other* party's bond. The request is re-emitted periodically until the counterparty replies or the claim window expires; clients must treat the repeats as idempotent reminders, not as new requests for a fresh invoice.
8+
9+
- **Counterparty → Mostro (reply).** The counterparty replies with a bolt11 sized at the counterparty share (`order.amount` in the request payload). The reply must arrive before the forfeit deadline (see [Forfeit deadline](#forfeit-deadline) below).
10+
11+
## Mostro → counterparty (request)
12+
13+
Mostro sends a single `add-bond-invoice` message to the non-slashed counterparty. The payload is a `bond_payout_request` variant that bundles the order context with the slash anchor:
14+
15+
```json
16+
[
17+
{
18+
"order": {
19+
"version": 1,
20+
"id": "<Order Id>",
21+
"action": "add-bond-invoice",
22+
"payload": {
23+
"bond_payout_request": {
24+
"order": {
25+
"id": "<Order Id>",
26+
"kind": "sell",
27+
"amount": 500,
28+
"fiat_code": "VES",
29+
"fiat_amount": 100,
30+
"payment_method": "face to face",
31+
"premium": 1
32+
},
33+
"slashed_at": 1734000000
34+
}
35+
}
36+
}
37+
},
38+
null
39+
]
40+
```
41+
42+
- `bond_payout_request.order.amount` is the counterparty's share of the slashed bond in sats. The reply's bolt11 principal must match this value exactly.
43+
- `bond_payout_request.slashed_at` is the Unix timestamp (seconds, UTC) at which the slash was recorded — see [Forfeit deadline](#forfeit-deadline).
44+
45+
The message carries no hardcoded human-readable deadline text; the client renders that warning locally in the user's own locale from `slashed_at` and the info-event window tag.
46+
47+
## Forfeit deadline
48+
49+
The client computes the forfeit deadline as:
50+
51+
```text
52+
deadline = slashed_at + bond_payout_claim_window_days * 86_400
53+
```
54+
55+
- `slashed_at` is read from the `bond_payout_request` payload above.
56+
- `bond_payout_claim_window_days` is a tag published on the Mostro instance's kind-38385 info event — see [Other events published by Mostro](./other_events.md).
57+
58+
`slashed_at` is shipped on the wire (rather than derived from the time the message lands) so that the deadline is stable across periodic re-deliveries and accurate even if the recipient — or their relay — was offline for several days. A deadline computed from "now + window" at receipt time would silently drift into the future on every retry; reading `slashed_at` from the payload pins it to the moment Mostro actually slashed. Clients **must** read `slashed_at` from the payload and **must not** substitute their local clock.
59+
60+
## Counterparty → Mostro (reply)
61+
62+
The counterparty replies with a Gift wrap Nostr event whose rumor content carries the bolt11 inside the standard `payment_request` 3-tuple. The third element is `null` because the invoice carries its own amount:
63+
64+
```json
65+
[
66+
{
67+
"order": {
68+
"version": 1,
69+
"id": "<Order Id>",
70+
"action": "add-bond-invoice",
71+
"payload": {
72+
"payment_request": [
73+
null,
74+
"lnbcrt5u1pj59wmepp5...",
75+
null
76+
]
77+
}
78+
}
79+
},
80+
"<index N signature of the sha256 hash of the serialized first element of content>"
81+
]
82+
```
83+
84+
The reply is signed with the **trade key** of the counterparty side — the side that was *not* slashed — exactly as in any other order-scoped action; see [Keys management](./key_management.md).
85+
86+
## Recipient resolution
87+
88+
The recipient of the request DM is the non-slashed counterparty of the trade, derived from the order's `buyer_pubkey` / `seller_pubkey` and the side the solver flagged in [`bond_resolution`](./admin_settle_order.md#bond-resolution-payload):
89+
90+
| Order kind | Solver flag | Bond on … | Recipient |
91+
|------------|----------------------------|-----------|------------------|
92+
| `sell` | `slash_buyer = true` | taker | maker (seller) |
93+
| `sell` | `slash_seller = true` | maker | taker (buyer) |
94+
| `buy` | `slash_buyer = true` | maker | taker (seller) |
95+
| `buy` | `slash_seller = true` | taker | maker (buyer) |
96+
97+
## Expected client behaviour
98+
99+
- **Dispatch on the action discriminator.** Route `add-bond-invoice` through its own handler. Collapsing it with `add-invoice` will misclassify the bolt11 as a trade-payout invoice and lead to user-visible accounting errors.
100+
- **Render the deadline locally.** Combine `slashed_at` from the `bond_payout_request` payload with `bond_payout_claim_window_days` from the kind-38385 info event ([Other events published by Mostro](./other_events.md)). Do not derive the deadline from receive time.
101+
- **Treat re-deliveries as idempotent reminders.** The same outstanding request is being repeated; do not re-prompt the user for a fresh invoice on every retry. `slashed_at` is re-emitted unchanged, so the deadline the client shows must not shift across retries.
102+
- **Sign the reply with the non-slashed side's trade key.** See [Keys management](./key_management.md).
103+
104+
## Failure modes
105+
106+
- The counterparty never replies before the deadline → no Lightning payment arrives and the share is forfeited; the slashed funds remain with the node.
107+
- A reply arrives after the deadline, or from a sender other than the resolved recipient, or after another reply already won the race → Mostro responds with a `cant-do` action carrying reason `not-allowed-by-status`.
108+
- The bolt11 principal does not match the requested counterparty share, or the invoice is otherwise undecodable / expired → Mostro responds with `cant-do` reason `invalid-invoice`.
109+
- On a node where the operator retains 100% of slashed bonds, **no `add-bond-invoice` message is emitted at all**. Clients should not surface a phantom payout request.
110+
111+
On success, the counterparty receives their share as a Lightning payment from the node's wallet. The routing fee is paid separately from that wallet and is not deducted from the principal.

src/admin_cancel_order.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Accepted combinations on `admin-cancel`:
4949

5050
`bond_resolution` is only valid on `admin-cancel` and `admin-settle`; it is rejected on every other action.
5151

52+
Whenever a side is slashed and the operator has configured `slash_node_share_pct < 1.0`, the non-slashed counterparty is subsequently asked to provide a Lightning invoice for their share of the bond via [Bond payout invoice](./add_bond_invoice.md).
53+
5254
## Mostro response
5355

5456
Mostro will send this message to the both parties buyer/seller and to the admin:

src/admin_settle_order.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Accepted combinations on `admin-settle`:
4949

5050
`bond_resolution` is only valid on `admin-settle` and `admin-cancel`; it is rejected on every other action.
5151

52+
Whenever a side is slashed and the operator has configured `slash_node_share_pct < 1.0`, the non-slashed counterparty is subsequently asked to provide a Lightning invoice for their share of the bond via [Bond payout invoice](./add_bond_invoice.md).
53+
5254
## Mostro response
5355

5456
Mostro will send this message to the both parties buyer/seller and to the admin:

src/message_suggestions_for_actions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Below are suggestions for messages that clients can show to users when receiving
1616
- **pay-bond-invoice:**
1717
Please pay this **bond** hold invoice of `amount` Sats as a security deposit before the trade begins. The bond is separate from the trade escrow and is released when the trade completes normally. If you do not pay it within `expiration_seconds`, the take will be canceled — the order remains visible on the order book as `pending` and may be taken again by anyone.
1818

19+
- **add-bond-invoice:**
20+
Please send me a Lightning invoice for `amount` Sats — this is your share of a slashed bond on order `id`. You have until `deadline` to submit it, or your share will be forfeited and the entire bond will be retained by the node.
21+
1922
- **add-invoice:**
2023
Please send me an invoice for `amount` satoshis equivalent to `fiat_code` `fiat_amount`. This is where I will send the funds upon trade completion. If you don’t provide the invoice within `expiration_seconds`, the trade will be canceled.
2124

src/pay_bond_invoice.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Internal transitions (visible only in DM payload echoes):
7474

7575
- The user never pays the bond bolt11 → the invoice expires; the order's NIP-33 status was `pending` throughout, so the rollback only undoes the daemon-internal take state. The order remains takeable.
7676
- The user pays the bond and then cancels before trade completion → the bond HTLC is cancelled and the funds return to the taker.
77-
- Slashing conditions (solver-directed dispute resolution, or timeout while in a waiting state) can settle the bond rather than release it. These paths are documented under [Admin Settle order](./admin_settle_order.md) and [Admin Cancel order](./admin_cancel_order.md), and in the Mostro daemon's anti-abuse bond specification.
77+
- Slashing conditions (solver-directed dispute resolution, or timeout while in a waiting state) can settle the bond rather than release it. The solver directs slashing via the `bond_resolution` payload documented under [Admin Settle order](./admin_settle_order.md) and [Admin Cancel order](./admin_cancel_order.md), and the non-slashed counterparty is then asked for their share of the bond via [Bond payout invoice](./add_bond_invoice.md).
7878

7979
## Backwards compatibility
8080

0 commit comments

Comments
 (0)