Skip to content

Commit 6ae34c4

Browse files
authored
Merge pull request #43 from MostroP2P/docs/pay-bond-invoice-action
docs: document pay-bond-invoice action and waiting-taker-bond status
2 parents 09454ff + 7a52b3d commit 6ae34c4

8 files changed

Lines changed: 112 additions & 0 deletions

File tree

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [Take range sell order](./take_sell_range_order.md)
1717
- [Take buy order](./take_buy.md)
1818
- [Take range buy order](./take_buy_range_order.md)
19+
- [Pay bond invoice](./pay_bond_invoice.md)
1920
- [Seller pays hold invoice](./seller_pay_hold_invoice.md)
2021
- [Fiat sent](./fiatsent.md)
2122
- [Release](./release.md)

src/cancel.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ Mostro updates the addressable event with `d` tag `<Order Id>` to change the sta
6666
]
6767
```
6868

69+
## Bond race during take
70+
71+
A client that sent `take-buy` / `take-sell` and is waiting for [`pay-bond-invoice`](./pay_bond_invoice.md) may receive `Action::Canceled` instead — meaning another user paid their bond first on the same order (whichever bond locks first wins; see [pay bond invoice](./pay_bond_invoice.md)). Surface this clearly to the user, e.g. *"Order was taken by another user before you locked the bond."* Do not silently retry the take — the order may not be available anymore, and the supersede mechanism on the daemon side has already discarded the prior bond request.
72+
6973
## Cancel cooperatively
7074

7175
A user can cancel an `active` order, but will need the counterparty to agree, let's look at an example where the seller initiates a cooperative cancellation:

src/message_suggestions_for_actions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Below are suggestions for messages that clients can show to users when receiving
1313
- **pay-invoice:**
1414
Please pay this hold invoice of `amount` Sats for `fiat_code` `fiat_amount` to start the operation. If you do not pay it within `expiration_seconds`, the trade will be canceled.
1515

16+
- **pay-bond-invoice:**
17+
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.
18+
1619
- **add-invoice:**
1720
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.
1821

src/order_event.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ Events are [addressable events](https://github.com/nostr-protocol/nips/blob/mast
6464
- `k` < Order type >: `sell` or `buy`. This specifies the type of transaction in terms of bitcoin. "sell" means selling bitcoin, while "buy" indicates buying bitcoin.
6565
- `f` < Currency >: The fiat asset being traded, using the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) standard.
6666
- `s` < Status >: `pending`, `canceled`, `in-progress`, `success`, `expired`.
67+
68+
An order with `s = pending` may already be matched to a taker who is in the middle of paying their anti-abuse bond. It remains takeable in this window — another user may attempt the take, and whichever bond locks first wins (the prior taker is notified with `Action::Canceled`, see [Cancel](./cancel.md)). The internal daemon state that tracks this (`waiting-taker-bond`, visible only in DM payload echoes) is not part of NIP-69's four-bucket wire model. Clients **must not** gray out or hide a `pending` order from the local order-book view just because their user has initiated a take.
6769
- `amt` < Amount >: The amount of Bitcoin to be traded, the amount is defined in satoshis, if `0` means that the amount of satoshis will be obtained from a public API after the taker accepts the order.
6870
- `fa` < Fiat amount >: The fiat amount being traded, for range orders two values are expected, the minimum and maximum amount.
6971
- `pm` < Payment method >: The payment method used for the trade, if the order has multiple payment methods, they should be separated by a comma.

src/other_events.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ Below is an explanation of the meaning of some of the labels in this event, all
199199
- `y`: Platform identifier tag values. Mostro publishes `"mostro"` and MAY include a second value with the Mostro instance name from settings.
200200
- `z`: The type of event.
201201

202+
### Upcoming: anti-abuse bond tags
203+
204+
A future Mostro release will publish bond-related tags in this event so clients can detect bond-enabled nodes ahead of a take. The tag set is still being finalized; at minimum it will include a `bond` tag with the value `enabled` or `disabled`. Additional tags describing the bond size policy (percentage, floor) may be added.
205+
206+
Clients that take orders on a node advertising `bond` `enabled` should be prepared to handle the [`pay-bond-invoice`](./pay_bond_invoice.md) action and the `waiting-taker-bond` order status. Until this tag is finalized and shipped, clients should treat the absence of bond tags as "behaviour unknown" and surface a clear error if the node sends `pay-bond-invoice` to a take they cannot handle.
207+
202208
## Information about the Relays Where Events Are Published
203209

204210
The operator of a Mostro instance decides which relays the events from that instance are published to. This information can be accessed in events [kind 10002](https://github.com/nostr-protocol/nips/blob/master/65.md), which are published by the Mostro instances.

src/pay_bond_invoice.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Pay bond invoice
2+
3+
When the receiving Mostro node has the anti-abuse bond feature enabled, takers must lock a small Lightning hold invoice as a security deposit before the trade flow proceeds. Mostro asks for this payment with the `pay-bond-invoice` action.
4+
5+
The bond is **independent from the trade escrow**: it is a second hold invoice with its own payment hash, and it is **released on every normal exit path** — completion, cancel before timeout, or dispute resolution where the solver does not direct otherwise. Bonds are typically around **1% of the trade amount** (with an operator-configured floor), much smaller than the trade hold invoice that may follow.
6+
7+
## Direction and trigger
8+
9+
- **Direction:** Mostro → user (the taker).
10+
- **Trigger:** Sent immediately after a successful `take-buy` / `take-sell` when the operator has bonds enabled.
11+
- **Order status:** The published NIP-33 order event keeps the `s` tag at `pending` while the bond is outstanding (per NIP-69's four-bucket model — see [Peer-to-peer Order events. NIP-69](./order_event.md)). The DM payload's embedded `SmallOrder` echo carries the daemon-internal status `waiting-taker-bond` so the recipient client can render the bond-payment phase distinctly, but external observers (other potential takers, order-book aggregators) continue to see the order as `pending` and may still attempt to take it. This is deliberate: a taker who never pays the bond cannot park the order off the book.
12+
13+
## Mostro message to the taker
14+
15+
The rumor's content has the same shape as `pay-invoice`; only the action discriminator differs:
16+
17+
```json
18+
[
19+
{
20+
"order": {
21+
"version": 1,
22+
"id": "<Order Id>",
23+
"action": "pay-bond-invoice",
24+
"payload": {
25+
"payment_request": [
26+
{
27+
"id": "<Order Id>",
28+
"kind": "sell",
29+
"status": "waiting-taker-bond",
30+
"amount": 7851,
31+
"fiat_code": "VES",
32+
"fiat_amount": 100,
33+
"payment_method": "face to face",
34+
"premium": 1,
35+
"created_at": 1698937797
36+
},
37+
"lnbcrt780n1pj59wmepp5..."
38+
]
39+
}
40+
}
41+
},
42+
null
43+
]
44+
```
45+
46+
> Note: the `status` value `"waiting-taker-bond"` here is the daemon-internal state echoed in the DM payload. The corresponding NIP-33 addressable order event's `s` tag is still `pending`.
47+
48+
## Expected client behaviour
49+
50+
- Decode the bolt11 and surface it to the user **explicitly as a bond**, not as the trade escrow. Do not reuse the same wording or UI step that you use for `pay-invoice`.
51+
- Pay it. The HTLC enters `Accepted` state — the sats are locked in the taker's wallet, not captured.
52+
- Do not collapse this into the same UI step as any subsequent `pay-invoice` on the same order. They are independent HTLCs (different payment hashes), and the user must explicitly approve each.
53+
54+
## Follow-up flow
55+
56+
Once the bond HTLC is `Accepted`, Mostro proceeds with the normal trade flow:
57+
58+
- **Sell order taken (taker = buyer):** order status moves to `waiting-buyer-invoice`; the taker next receives `add-invoice` to provide a payout invoice.
59+
- **Buy order taken (taker = seller):** order status moves to `waiting-payment`; the taker next receives `pay-invoice` for the **trade hold invoice**.
60+
61+
> **Important — buy order taken (seller-as-taker):** this is the only flow on which a single user pays **two hold invoices in sequence on the same order** — first the bond (`pay-bond-invoice`), then the trade escrow (`pay-invoice`). They arrive as distinct actions and must be presented to the user as separate steps. Do not auto-pay either, do not coalesce them, and make the distinction obvious in the UI; this is the most error-prone path for client developers.
62+
63+
## Daemon status `waiting-taker-bond` (DM payloads only)
64+
65+
In addition to the NIP-69 wire status (`pending` while the bond is outstanding), Mostro tags the order's internal state as `waiting-taker-bond` so it can route subsequent messages correctly. Clients see this value in the `SmallOrder` echo embedded in `pay-bond-invoice` payloads and may use it to drive UI ("Waiting for bond payment"). It does **not** appear on the addressable NIP-33 order event — that one continues to advertise the order as `pending`.
66+
67+
Internal transitions (visible only in DM payload echoes):
68+
69+
- From `pending``waiting-taker-bond`, after a successful `take-buy` / `take-sell` when bonds are enabled.
70+
- From `waiting-taker-bond``waiting-payment` (buy order taken) or → `waiting-buyer-invoice` (sell order taken), once the bond HTLC is `Accepted`.
71+
- From `waiting-taker-bond``pending`, if the bond bolt11 is never paid and expires, or the taker cancels before locking. The published `s` tag was `pending` throughout — observers see only that the take attempt left no trace.
72+
73+
## Failure modes
74+
75+
- 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.
76+
- 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.
78+
79+
## Backwards compatibility
80+
81+
Clients running an older `mostro-core` version that does not yet know `pay-bond-invoice` will fail to deserialize the message and silently drop it; from the user's perspective the take stalls and eventually times out without surfacing a useful error. Operators are responsible for not enabling bonds in production until clients in the wild have adopted the `mostro-core` release that ships `Action::PayBondInvoice`. Clients should:
82+
83+
- Recognise the action explicitly and surface it to the user.
84+
- If unable to handle it (e.g. an older build talking to a bond-enabled node), present a clear error rather than silently retrying the take.
85+
86+
The Mostro info event will gain bond-related tags so clients can detect bond-enabled nodes ahead of a take — see [Other events published by Mostro](./other_events.md#mostro-instance-status).

src/take_buy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ The event to send to Mostro would look like this:
3131
}
3232
```
3333

34+
## Optional: anti-abuse bond step
35+
36+
When the receiving Mostro node has bonds enabled, the seller (taker) first receives a [`pay-bond-invoice`](./pay_bond_invoice.md) message; the published NIP-33 order event remains `pending` (re-takeability is preserved — see [order event](./order_event.md)) and the DM payload echoes the internal status `waiting-taker-bond`. Only after the bond HTLC is `Accepted` does Mostro send the `pay-invoice` message described below.
37+
38+
This is the **only flow on which a single user pays two hold invoices in sequence on the same order**: the bond first, then the trade hold invoice. They arrive as distinct actions (`pay-bond-invoice` then `pay-invoice`) and clients **must** present them as separate steps. Clients that do not recognise `pay-bond-invoice` should expect the take to time out and surface a clear error to the user — do not silently retry the take.
39+
3440
## Mostro response
3541

3642
Mostro respond to the seller with a message with the following content:

src/take_sell.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ If the order amount is `0` the buyer doesn't know the exact amount to create the
1717
]
1818
```
1919

20+
## Optional: anti-abuse bond step
21+
22+
When the receiving Mostro node has bonds enabled, the buyer (taker) first receives a [`pay-bond-invoice`](./pay_bond_invoice.md) message; the published NIP-33 order event remains `pending` (re-takeability is preserved — see [order event](./order_event.md)) and the DM payload echoes the internal status `waiting-taker-bond`. Only after the bond HTLC is `Accepted` does Mostro send the `add-invoice` message described below. Clients that do not recognise `pay-bond-invoice` should expect the take to time out and surface a clear error to the user — do not silently retry the take.
23+
2024
## Mostro response
2125

2226
In order to continue the buyer needs to send a lightning network invoice to Mostro, in this case the amount of the order is `0`, so Mostro will need to calculate the amount of sats for this order, then Mostro will send back a message asking for a LN invoice indicating the correct amount of sats that the invoice should have, here the rumor's content of the message:

0 commit comments

Comments
 (0)