Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions docs/cashu/03-track-b-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Cashu Escrow — Track B: Release (Happy Path)

**Status:** Draft for review · **Target:** `main` (**requires `mostro-core ≥ 0.14.0`**) ·
**Depends on:** Fundamentals **CF-1, CF-2, CF-5** + **Track A** (the escrow must be
locked before it can be released) · **Feature flag:** `[cashu].enabled`

Track B is the **happy-path settlement** of a Cashu trade: the buyer confirms
fiat sent, the seller releases, and the buyer redeems the locked 2-of-3 token
with **two signatures** (its own + the seller's). It is "box 3" of the sequence
diagram in [`../CASHU_ESCROW_ARCHITECTURE.md`](../CASHU_ESCROW_ARCHITECTURE.md).

This document assumes Fundamentals and Track A are merged. It only adds behaviour
*inside the Cashu branch*; the Lightning path is never changed.

---

## 1. Goal and scope

### Goal
Complete a locked Cashu trade without Mostro ever touching the funds:
1. The **buyer** signals `FiatSent` (gated by the remaining-locktime guard, §4B).
2. The **seller** signals `Release`, and delivers its **Cashu release signature**
directly to the buyer (P2P NIP-59 DM) so the buyer can build a valid 2-of-3
`SwapRequest` and redeem the ecash **itself**.
3. Mostro only **advances the order state** (`Active → FiatSent → Success`),
makes the trade **rateable**, and — because the fee was collected at lock
(Track A §4A) — takes **no** settlement action on the funds.

### In scope
- The Cashu branch of `fiat_sent_action` carrying the **§4B remaining-locktime
guard** (`escrow_settlement_margin_days`, default 3).
- The Cashu branch of `release_action`: the seller marks release, the daemon
advances state and relays/acknowledges the seller's release signature to the
buyer, and the order becomes rateable.
- Unblocking `FiatSent`, `Release`, and `RateUser` in `dispatch_cashu`.

### Out of scope (other tracks)
- **Cooperative cancel** → Track C. **Dispute resolution** → Track D.
- The **live fee redeem** (Track A TA-1f follow-up) — orthogonal to release.
- Any change to the Lightning release/settle path.

---

## 2. Where Track B sits — flow and state transitions

```mermaid
sequenceDiagram
participant B as Buyer
participant M as Mostro (cashu mode)
participant S as Seller
participant Mint as Cashu Mint

Note over B,S: Escrow already locked (Track A) — order is Active
B->>M: FiatSent
M->>M: guard: remaining locktime >= escrow_settlement_margin_days
M->>S: FiatSentOk (buyer paid — release when ready)
S->>B: Release signature (NIP-59 DM, seller signs SIG_INPUTS)
S->>M: Release
M->>M: advance Active/FiatSent -> Success; make rateable
B->>Mint: SwapRequest {2-of-3 proofs, buyer_sig + seller_sig, buyer outputs}
Mint-->>B: fresh ecash (buyer holds the funds)
```

**State transitions Track B performs:**
`Active → FiatSent` (buyer), then `FiatSent → Success` (seller release). Both are
Cashu analogues of the Lightning flow, but **no hold invoice is settled** — the
buyer redeems the token itself, off Mostro's servers.

> **Why the seller's signature goes P2P, not through Mostro.** On the happy path
> Mostro is neither a signer nor a required relay: the seller signs the release
> and hands it to the buyer over a NIP-59 DM using the trade keys, exactly as the
> `release`/`cancel` messages already travel. Mostro only needs the **state**
> update. (Mostro MAY still relay a copy for reliability, but the trust model
> does not depend on it.)

---

## 3. What Track B consumes from Fundamentals + Track A

| Needs | From | Exact item |
|-------|------|------------|
| Mode gate | CF-1 | `Settings::is_cashu_enabled()`, `escrow_mode()` |
| Locktime floor / margin | CF-1 | `get_cashu().escrow_locktime_days`; **new** `escrow_settlement_margin_days` (default 3, §4B) |
| Locked escrow row | Track A | `Order.{cashu_escrow_token, cashu_escrow_locked_at}` populated, status `Active` |
| Token locktime read | CF-2 | parse the stored token's `locktime` (the guard compares it to now) |
| Dispatch seam | CF-5 | `FiatSent`/`Release`/`RateUser` arms in `dispatch_cashu` |
| Notifications | existing | `enqueue_order_msg`, `update_order_event` |

Protocol (already on `main`, `mostro-core ≥ 0.14.0`): `Action::{FiatSent,
FiatSentOk, Release, Released, RateUser}` and the existing rating payloads —
**no new protocol variant is required**. The seller's Cashu release signature is
carried in the existing P2P release message shape (trade-key-signed NIP-59 DM);
Mostro validates the *state transition*, not the swap.

---

## 4. The `escrow_settlement_margin_days` guard (§4B — Track B executes it)

Track A §4B defines the attack: a seller stalls the fiat phase until little
locktime remains, lets the buyer send fiat on day 13 of 15, goes silent, and
reclaims via the refund path on day 15 — keeping both fiat and sats without
failing a single protocol check. **Track B closes it at the `FiatSent` gate.**

- New `#[serde(default)]` key on `CashuSettings`: **`escrow_settlement_margin_days`,
default 3**. Added by Track B (not needed during foundation).
- In the Cashu branch of `fiat_sent_action`: read the stored escrow token's
`locktime`; reject `FiatSent` with a clear `CantDo` (e.g.
`CashuEscrowNotLocked` if the token/locktime is missing, else a
settlement-window reason) when
`locktime - now < escrow_settlement_margin_days`. Fiat can never be sent inside
the danger window, so the seller cannot weaponise the locktime.

The margin must be comfortably below the locktime floor (`escrow_locktime_days`,
default 15) so a normal trade has ample settlement room; the difference (≈12
days) is the usable fiat-settlement window.
Comment on lines +104 to +115

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Specify margin validation and expired-locktime handling.

The supplied CashuSettings currently has no escrow_settlement_margin_days; the implementation plan should require startup validation that the margin is below escrow_locktime_days. Also define expired-locktime behavior explicitly and require checked/saturating subtraction, otherwise a configurable margin can disable every FiatSent and unsigned locktime - now arithmetic can underflow.

🤖 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 `@docs/cashu/03-track-b-release.md` around lines 104 - 115, Update the
CashuSettings implementation and startup validation to add
escrow_settlement_margin_days and require it to be strictly below
escrow_locktime_days. In the Cashu branch of fiat_sent_action, explicitly reject
expired or missing locktimes with the appropriate CantDo reason, and use checked
or saturating subtraction for locktime-versus-now calculations so underflow
cannot disable every FiatSent.


---

## 5. Handlers — the Cashu branches

### 5A · `fiat_sent_action` (Cashu branch)
Same identity/status checks as today (only the **buyer** may send fiat; order
must be `Active`). Then, in Cashu mode only: apply the §4B guard, advance
`Active → FiatSent`, publish the order event, and notify both parties
(`FiatSentOk`) — no LND, no hold-invoice interaction.

### 5B · `release_action` (Cashu branch)
The submitter must be the **seller** (same identity check as the Lightning
release). In Cashu mode: **do not** settle a hold invoice (there is none);
instead advance `FiatSent → Success`, publish the order event, acknowledge the
seller's release signature to the buyer (relay/ack the P2P DM), and make the
trade rateable (`RateUser` becomes valid). The fee was already collected at lock
Comment on lines +130 to +132

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require a verified release signature before success

In this flow the seller's Cashu signature is only described as a P2P DM, while the daemon advances FiatSent to Success on the seller's Release. If a seller sends Release after FiatSent but never sends a valid NUT-11 signature to the buyer, Mostro would make the trade terminal/rateable even though the buyer cannot redeem and may no longer have a usable dispute path. Please make this transition depend on a daemon-verifiable signature delivery or an explicit buyer/redeem acknowledgement, not solely on the seller's release message.

Useful? React with 👍 / 👎.

(Track A §4A), so release moves **no** funds.

### 5C · `dispatch_cashu` unblocks
Replace the `InvalidAction` arms for `FiatSent`, `Release`, and `RateUser`
(route through the existing `handle_message_action_no_ln`, whose branches now
carry the Cashu logic). No other action changes.

---

## 6. PR breakdown (atomic, backwards-compatible)

### TB-1 · `escrow_settlement_margin_days` + `FiatSent` guard
Add the `CashuSettings` key and the Cashu branch of `fiat_sent_action` with the
§4B remaining-locktime guard. Unblock `FiatSent` in `dispatch_cashu`.
*Depends on CF-1, Track A. Conflict surface: `config/*`, `fiat_sent.rs`,
`app.rs` (one dispatch arm).*

### TB-2 · `release_action` Cashu branch + rating
Add the Cashu branch of `release_action` (advance state, ack the seller
signature, make rateable) and unblock `Release` + `RateUser`. Completes the
happy path end-to-end with TB-1 and Track A.
*Depends on CF-5, Track A, TB-1 (for a full e2e test). Conflict surface:
`release.rs`, `rate_user.rs` (if touched), `app.rs` (two dispatch arms).*

---

## 7. Issues table — sequential vs parallel

| ID | Title | Depends on | Parallel with | Conflict surface | Risk |
|----|-------|-----------|---------------|------------------|------|
| **TB-1** | `escrow_settlement_margin_days` + `FiatSent` §4B guard | CF-1, Track A | Tracks C/D | `config/*`, `fiat_sent.rs` | Low-Medium |
| **TB-2** | `release_action` Cashu branch + unblock `Release`/`RateUser` | CF-5, Track A, TB-1 | Tracks C/D | `release.rs`, `app.rs` | Medium |

All of Track B is parallel with Tracks C/D (disjoint handler files); the shared
touch point is the `dispatch_cashu` allow-list, edited one arm at a time.
Comment on lines +163 to +167

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make the documented track ordering consistent.

  • docs/cashu/03-track-b-release.md#L163-L167: remove or qualify the claim that Track B is parallel with Tracks C/D.
  • docs/cashu/04-track-c-coop-cancel.md#L154-L161: reflect the stated dependency on Track B if it is a merge/runtime dependency.
  • docs/cashu/05-track-d-dispute.md#L176-L186: reflect the stated dependency on Track C if it is required for implementation.
📍 Affects 3 files
  • docs/cashu/03-track-b-release.md#L163-L167 (this comment)
  • docs/cashu/04-track-c-coop-cancel.md#L154-L161
  • docs/cashu/05-track-d-dispute.md#L176-L186
🤖 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 `@docs/cashu/03-track-b-release.md` around lines 163 - 167, The documented
track ordering is inconsistent: update docs/cashu/03-track-b-release.md lines
163-167 to remove or qualify Track B’s parallelism with Tracks C/D, update
docs/cashu/04-track-c-coop-cancel.md lines 154-161 to state Track B as a
dependency if it is required for merge or runtime behavior, and update
docs/cashu/05-track-d-dispute.md lines 176-186 to state Track C as a dependency
if required for implementation.


---

## 8. Definition of Done

1. A locked Cashu order can be driven `Active → FiatSent → Success` end-to-end
against the CF-3 mint, with the buyer redeeming the 2-of-3 token itself.
2. `FiatSent` is rejected inside the settlement-margin window (§4B), and accepted
outside it; the exact `CantDoReason` is asserted.
3. Every identity/status rejection path returns the correct reason and leaves the
order unchanged (wrong sender, wrong status, guard tripped).
4. The trade becomes rateable only after release; `RateUser` works.
5. With Cashu disabled, behaviour is identical to `main`; existing tests pass
unmodified. `fmt`/`clippy -D warnings`/`test` green.

---

## 9. Cross-track obligations satisfied / raised

| Obligation | Defined in | Track B does |
|------------|-----------|--------------|
| `FiatSent` rejected when remaining locktime < `escrow_settlement_margin_days` | Track A §4B | **Executed** (TB-1) |
| `RateUser` unblocked once terminal state reachable | CF-5 §6 | **Executed** (TB-2) |
| Buyer locktime warnings as expiry approaches | Track A §4B | Surfaced by TA-3 monitor; TB may add a nudge on `FiatSent` |
186 changes: 186 additions & 0 deletions docs/cashu/04-track-c-coop-cancel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Cashu Escrow — Track C: Cooperative Cancel

**Status:** Draft for review · **Target:** `main` (**requires `mostro-core ≥ 0.14.0`**) ·
**Depends on:** Fundamentals **CF-1, CF-2, CF-5** + **Track A** (the escrow must be
locked before it can be cancelled) · **Feature flag:** `[cashu].enabled`

Track C is the **cooperative unwind**: both parties agree to abandon a locked
trade, the buyer hands the seller the signature needed to reclaim the escrow, and
Mostro records the cancellation and **refunds the seller the fee** it collected at
lock. No dispute, no arbitrator signature.

This document assumes Fundamentals and Track A are merged. It only adds behaviour
*inside the Cashu branch*; the Lightning path is never changed.

---

## 1. Goal and scope

### Goal
Let a locked Cashu trade be cancelled by mutual consent without Mostro moving the
escrow:
1. Either party requests `Cancel`; when **both** have requested it (the existing
cooperative-cancel handshake), the trade is cancelled.
2. The **buyer** delivers its **Cashu signature** directly to the **seller** (P2P
NIP-59 DM) so the seller can build a 2-of-3 `SwapRequest`
(`buyer_sig + seller_sig`) and **reclaim** the locked ecash itself.
3. Mostro advances the order to `Canceled` and — because the fee was collected at
lock (Track A §4A) — **refunds the seller** the whole Mostro fee
(`2 * order.fee`).

### In scope
- The Cashu branch of `cancel_action` (cooperative handshake → `Canceled`).
- The **fee-refund obligation** (Track A §4A): mint/send a fresh token of
`2 * order.fee` to the seller's **trade** pubkey on a cancelled-after-lock
order. (This is the first track to execute the refund obligation.)
- Unblocking `Cancel` in `dispatch_cashu`.

### Out of scope (other tracks)
- **Unilateral / dispute-driven** cancellation → Track D (`admin_cancel`).
- **Release** happy path → Track B.
- The **live fee redeem** mechanics (Track A TA-1f follow-up); the refund shares
the same "Mostro sends ecash" capability and should land with it.

---

## 2. Where Track C sits — flow and state transitions

```mermaid
sequenceDiagram
participant B as Buyer
participant M as Mostro (cashu mode)
participant S as Seller
participant Mint as Cashu Mint

Note over B,S: Escrow already locked (Track A)
B->>M: Cancel
S->>M: Cancel
M->>M: both requested -> cooperative cancel
B->>S: Buyer signature (NIP-59 DM)
M->>M: advance -> Canceled
M->>S: Fee refund (fresh token 2*order.fee to P_S)
S->>Mint: SwapRequest {2-of-3 proofs, buyer_sig + seller_sig, seller outputs}
Mint-->>S: reclaimed ecash
```

**State transition Track C performs:** the existing cooperative-cancel handshake
drives the order to `Canceled`. No hold invoice is cancelled — the seller
reclaims the token itself with the buyer's signature.

> **Why the buyer signs for the seller here.** On the happy path the *seller*
> signs so the *buyer* redeems; on a cancel the roles invert — the funds return to
> the seller, so the *buyer* provides the second signature that lets the *seller*
> reclaim. Same 2-of-3 token, opposite redeemer.

---

## 3. What Track C consumes from Fundamentals + Track A

| Needs | From | Exact item |
|-------|------|------------|
| Mode gate | CF-1 | `Settings::is_cashu_enabled()`, `escrow_mode()` |
| Locked escrow row | Track A | `Order.{cashu_escrow_token, cashu_escrow_locked_at}` populated |
| Fee to refund | Track A §4A | `order.fee` → refund value `2 * order.fee`; `cashu_fee_token`/`cashu_fee_redeemed_at` state |
| Seller trade pubkey | existing | `order.get_seller_pubkey()` (refund recipient) |
| Mostro-sends-ecash | CF-2 (TA-1f follow-up) | mint/send a fresh token to `P_S` |
| Dispatch seam | CF-5 | `Cancel` arm in `dispatch_cashu` |

Protocol (already on `main`, `mostro-core ≥ 0.14.0`): the existing
cooperative-cancel messages (`Action::Cancel` and its
`CooperativeCancel*`/`Canceled` acknowledgements). The buyer's Cashu signature
travels in the existing P2P cancel message shape; **no new protocol variant is
required**.

---

## 4. The fee refund (Track A §4A obligation — Track C executes it)

Because the fee is realised at **lock**, not at success, the daemon **owes the
seller a refund** on every non-success path. A cooperative cancel is one such
path.

- Refund value = **`2 * order.fee`** (the whole Mostro fee), sent as a fresh
token to the seller's **trade** pubkey.
- Idempotency: gate the refund on the fee state so a replayed/duplicate cancel
never double-refunds — e.g. only refund when `cashu_fee_token IS NOT NULL` and
the order has not already been refunded (stamp a `cashu_fee_refunded_at`, or
reuse `cashu_fee_redeemed_at` semantics: a redeemed fee is refunded by minting
fresh ecash back, a not-yet-redeemed fee is simply not collected). The exact
Comment on lines +107 to +108

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refund unredeemed fee tokens instead of ignoring them

For the Track A fee model, the fee token is locked 1-of-1 to P_M, so the seller cannot spend it. If cooperative cancel happens before cashu_fee_redeemed_at is stamped, treating the fee as “simply not collected” does not refund the seller; it leaves their fee ecash locked to Mostro's key. The cancel path needs to redeem-and-return fresh ecash, provide a usable P_M signature/refund path, or require the self-service-refund refinement before this no-op is safe.

Useful? React with 👍 / 👎.

bookkeeping is a TC decision, but it **must** be single-shot.
Comment on lines +102 to +109

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define one crash-safe, idempotent seller-refund contract.

  • docs/cashu/04-track-c-coop-cancel.md#L102-L109: specify atomic claiming, durable retry/idempotency behavior, and fee-state semantics around minting.
  • docs/cashu/05-track-d-dispute.md#L137-L143: require seller-win disputes to use the same refund helper and idempotency contract, including cancel/dispute terminal-state races.
🧰 Tools
🪛 LanguageTool

[style] ~104-~104: The adverb ‘never’ is usually put before the verb ‘cancel’.
Context: ...n the fee state so a replayed/duplicate cancel never double-refunds — e.g. only refund when ...

(ADVERB_WORD_ORDER)

📍 Affects 2 files
  • docs/cashu/04-track-c-coop-cancel.md#L102-L109 (this comment)
  • docs/cashu/05-track-d-dispute.md#L137-L143
🤖 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 `@docs/cashu/04-track-c-coop-cancel.md` around lines 102 - 109, Define one
crash-safe, single-shot seller-refund contract in
docs/cashu/04-track-c-coop-cancel.md lines 102-109: specify atomic refund
claiming, durable retry/idempotency behavior, and explicit fee-state semantics
for minting. In docs/cashu/05-track-d-dispute.md lines 137-143, require
seller-win disputes to call the same refund helper and follow the same contract,
including races between cancel and dispute terminal states.

- If the fee was **never charged** (`mostro.fee == 0`), there is nothing to
refund — the branch is a no-op.

> **Interaction with the self-service-refund refinement (Track A §4A).** If a
> future revision locks the fee token with `locktime` + `refund = [P_S]`, the
> seller can reclaim the fee unilaterally after locktime and Track C's proactive
> refund becomes a fast-path optimisation rather than the sole recovery route.
> Track C ships the proactive refund against the simple 1-of-1 fee token TA-1f
> defines.

---

## 5. Handler — the Cashu branch of `cancel_action`

Keep the existing cooperative-cancel handshake (both parties must request
`Cancel`). In Cashu mode, at the point the Lightning path would cancel the hold
invoice:
- **Do not** touch LND (there is no hold invoice).
- Advance the order to `Canceled`, publish the order event.
Comment on lines +127 to +128

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the cooperative-cancel terminal status

The existing active-order cooperative cancel path persists Status::CooperativelyCanceled in cancel_cooperative_execution_step_2; Status::Canceled is used for other cancel flows. Specifying Canceled for the Cashu branch would make the new branch diverge from the current cooperative-cancel semantics and can break code that distinguishes cooperative completion from ordinary cancellation/admin cancellation. Please keep the internal terminal state aligned with the existing handler and only collapse to a public “canceled” status where the current NIP-33 mapping already does so.

Useful? React with 👍 / 👎.

- Acknowledge/relay the buyer's Cashu signature to the seller so the seller can
reclaim.
- Execute the **fee refund** (§4) once, if a fee was collected.

`dispatch_cashu` replaces the `InvalidAction` arm for `Cancel` (routing through
the cashu-aware `cancel` handler). A *unilateral* cancel of a locked escrow (no
peer consent) is **not** a Track C concern — that path is a dispute (Track D).

---

## 6. PR breakdown (atomic, backwards-compatible)

### TC-1 · `cancel_action` Cashu branch + fee refund
Add the Cashu branch to `cancel_action` (cooperative handshake → `Canceled`, no
LND), the single-shot fee refund to `P_S`, and unblock `Cancel` in
`dispatch_cashu`. Unit-tested against the CF-3 mint (both-parties cancel →
`Canceled` + seller can reclaim + fee refunded once; single-party cancel does not
finalise; replay does not double-refund).
*Depends on CF-5, Track A (and TA-1f for the fee state + the Mostro-sends-ecash
capability). Conflict surface: `cancel.rs`, possibly `db.rs` (refund bookkeeping,
additive) + `migrations/` if a `cashu_fee_refunded_at` column is chosen,
`app.rs` (one dispatch arm).*

---

## 7. Issues table — sequential vs parallel

| ID | Title | Depends on | Parallel with | Conflict surface | Risk |
|----|-------|-----------|---------------|------------------|------|
| **TC-1** | `cancel_action` Cashu branch + fee refund + unblock `Cancel` | CF-5, Track A, TA-1f | Tracks B/D | `cancel.rs`, `app.rs`, (opt.) `migrations/` | Medium (funds return + refund) |

Track C is parallel with Tracks B/D; the only shared touch point is the
`dispatch_cashu` `Cancel` arm.

---

## 8. Definition of Done

1. A locked Cashu order, cancelled cooperatively by both parties, reaches
`Canceled`, the seller can reclaim the escrow with the buyer's signature, and
the seller is refunded `2 * order.fee` — verified end-to-end against the CF-3
mint.
2. A single-party `Cancel` does **not** finalise the cancellation (the handshake
still requires both).
3. The fee refund is **single-shot**: a replayed/duplicate cancel never
double-refunds; a fee-free order refunds nothing.
4. With Cashu disabled, behaviour is identical to `main`; existing tests pass
unmodified. `fmt`/`clippy -D warnings`/`test` green.

---

## 9. Cross-track obligations satisfied / raised

| Obligation | Defined in | Track C does |
|------------|-----------|--------------|
| Fee refund on non-success (coop cancel after lock), `2 * order.fee` to `P_S` | Track A §4A / §10 | **Executed** (TC-1) |
| Single-shot refund bookkeeping | Track A §4A | **Executed** (idempotent refund) |
| Mostro-sends-ecash capability | Track A §4A (TA-1f) | **Shared** with the fee redeem; lands together |
Loading