Bond payout invoice

The add-bond-invoice action is the counterparty-direction dual of pay-bond-invoice: 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 Cancel order or a timeout-driven slash.

Direction and trigger

  • Mostro → counterparty (request). Mostro emits this message after a bond_resolution 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.

  • 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 below).

Mostro → counterparty (request)

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:

[
  {
    "order": {
      "version": 1,
      "id": "<Order Id>",
      "action": "add-bond-invoice",
      "payload": {
        "bond_payout_request": {
          "order": {
            "id": "<Order Id>",
            "kind": "sell",
            "amount": 500,
            "fiat_code": "VES",
            "fiat_amount": 100,
            "payment_method": "face to face",
            "premium": 1
          },
          "slashed_at": 1734000000
        }
      }
    }
  },
  null
]
  • bond_payout_request.slashed_at is the Unix timestamp (seconds, UTC) at which the slash was recorded — see Forfeit deadline.

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.

Forfeit deadline

The client computes the forfeit deadline as:

deadline = slashed_at + bond_payout_claim_window_days * 86_400
  • slashed_at is read from the bond_payout_request payload above.
  • bond_payout_claim_window_days is a tag published on the Mostro instance's kind-38385 info event — see Other events published by Mostro.

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.

Counterparty → Mostro (reply)

The counterparty replies with a Gift wrap Nostr event whose rumor content carries the bolt11 inside the standard payment_request array. The invoice carries its own amount, so the array has two elements (per Payment Request Array Structure):

[
  {
    "order": {
      "version": 1,
      "id": "<Order Id>",
      "action": "add-bond-invoice",
      "payload": {
        "payment_request": [
          null,
          "lnbcrt5u1pj59wmepp5..."
        ]
      }
    }
  },
  "<index N signature of the sha256 hash of the serialized first element of content>"
]

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.

Recipient resolution

The recipient of the request message 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:

Order kindSolver flagBond on …Recipient
sellslash_buyer = truetakermaker (seller)
sellslash_seller = truemakertaker (buyer)
buyslash_buyer = truemakertaker (seller)
buyslash_seller = truetakermaker (buyer)

Expected client behaviour

  • 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.
  • 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). Do not derive the deadline from receive time.
  • 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.
  • Sign the reply with the non-slashed side's trade key. See Keys management.

Payout confirmations (Phase 3.5)

After the counterparty submits their payout invoice, Mostro acknowledges receipt and later confirms payment with two dedicated actions.

bond-invoice-accepted

Sent by Mostro to the counterparty immediately after successfully receiving and validating their payout bolt11. This confirms Mostro has the invoice on file and is attempting the Lightning payment.

[
  {
    "order": {
      "version": 1,
      "id": "<Order Id>",
      "action": "bond-invoice-accepted",
      "payload": {
        "order": {
          "id": "<Order Id>",
          "kind": "sell",
          "amount": 500,
          "fiat_code": "VES",
          "fiat_amount": 100,
          "payment_method": "face to face",
          "premium": 1
        }
      }
    }
  },
  null
]

Clients should surface this as a reassurance: "Your bond payout invoice has been received and the payment is being processed."

bond-payout-completed

Sent by Mostro to the counterparty when the Lightning payment to their invoice has been confirmed as settled. The amount in the payload reflects the amount paid.

[
  {
    "order": {
      "version": 1,
      "id": "<Order Id>",
      "action": "bond-payout-completed",
      "payload": {
        "order": {
          "id": "<Order Id>",
          "kind": "sell",
          "amount": 500,
          "fiat_code": "VES",
          "fiat_amount": 100,
          "payment_method": "face to face",
          "premium": 1
        }
      }
    }
  },
  null
]

Clients should surface this as a terminal success: "Your bond payout of amount Sats has been completed!"

Both actions carry Payload::Order (the SmallOrder context) and are serde-additive: a client that does not recognise them ignores the message and falls back to the add-bond-invoice reply it already handles — no funds at risk.

Failure modes

  • The counterparty never replies before the deadline → no Lightning payment arrives and the share is forfeited; the slashed funds remain with the node.
  • 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.
  • 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.
  • 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.
  • If send_payment retries are exhausted (see payout_max_retries in the info event) the counterparty is re-prompted with a fresh add-bond-invoice request, provided the claim window has not yet elapsed. If the window has passed, the share is forfeited and no further messages are sent.

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.