@zkp2p/cash 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -16,7 +16,9 @@ can withdraw an unmatched deposit.
16
16
  human approval step) → use `prepare()` / `prepareTopUp()` /
17
17
  `prepareWithdraw()`. Each returns unsigned `txs[]`
18
18
  (`{ to, data, value, chainId }`) plus same-index `steps[]` labels; inspect
19
- the plan, submit the transactions in order, and wait for each receipt.
19
+ the plan, submit the transactions in order, and wait for each receipt. After
20
+ `createDeposit` confirms, pass its receipt to `finalizePreparedCashout()` and
21
+ persist the returned `depositId`.
20
22
  3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
21
23
  `@zkp2p/cash/tools` and map the tool names to the verbs above. Base-USDC
22
24
  mutating tools return unsigned transactions. `cash_source_quote` is a quote,
@@ -64,7 +66,20 @@ const stats = await cash.fillStats();
64
66
  const { depositId } = await cash.cashout(
65
67
  {
66
68
  amount: usdc(500),
67
- receive: { platform: 'revolut', currency: 'EUR', payee: { offchainId: 'revtag' } },
69
+ receive: { platform: 'revolut', currency: 'EUR', payee: 'revtag' },
70
+ },
71
+ { signer },
72
+ );
73
+
74
+ // Faster matching: one Revolut method, three live-oracle currency options.
75
+ const multiCurrency = await cash.cashout(
76
+ {
77
+ amount: usdc(500),
78
+ receive: {
79
+ platform: 'revolut',
80
+ currencies: ['EUR', 'GBP', 'USD'],
81
+ payee: { offchainId: 'revtag' },
82
+ },
68
83
  },
69
84
  { signer },
70
85
  );
@@ -173,6 +188,7 @@ Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
173
188
  | `UNSUPPORTED_PLATFORM_CURRENCY` | no | Use a currency listed for that platform |
174
189
  | `AMOUNT_BELOW_MINIMUM` | no | Raise amount (hard floor $0.01, recommended at least 1 USDC) |
175
190
  | `INVALID_INTENT_AMOUNT_RANGE` | no | Use a positive min, max at least min, and max no greater than amount |
191
+ | `INVALID_PAYOUT_CURRENCIES` | no | Pass one or more unique currencies listed for the platform |
176
192
  | `PAYEE_VERIFICATION_REQUIRED` | no | Register a new Wise/PayPal payee through Peer; an existing registered handle can be reused |
177
193
  | `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
178
194
  | `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
@@ -187,6 +203,7 @@ Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
187
203
  | `SOURCE_CASHOUT_STATUS_UNKNOWN` | no | Inspect `recovery.depositTxHash`; do not resubmit while its receipt is unknown |
188
204
  | `INSUFFICIENT_TOKEN_BALANCE` | no | Fund the required token amount, then retry |
189
205
  | `ALLOWANCE_NOT_VISIBLE` | yes | Approval mined but a stale RPC hid it; retry after it becomes visible |
206
+ | `TRANSACTION_REJECTED` | yes | Retry when ready and approve the wallet request |
190
207
  | `TRANSACTION_FAILED` | no | Inspect the failed/reverted call before another action |
191
208
  | `TRANSACTION_SUBMISSION_UNKNOWN` | no | Inspect Base wallet/protocol state and the recovery action before any resubmission |
192
209
  | `TRANSACTION_STATUS_UNKNOWN` | no | Inspect `recovery.transactionHash` before resubmitting |
package/README.md CHANGED
@@ -44,11 +44,25 @@ const pairStats = (await cash.fillStats())['venmo:USD'];
44
44
  const { depositId } = await cash.cashout(
45
45
  {
46
46
  amount: usdc(1000),
47
- receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
47
+ receive: { platform: 'venmo', currency: 'USD', payee: '@you' },
48
48
  },
49
49
  { signer }, // any viem WalletClient on Base
50
50
  );
51
51
 
52
+ // One method can offer several currencies. The buyer chooses the fill
53
+ // currency, and each option resolves at its own live oracle rate.
54
+ const fastFill = await cash.cashout(
55
+ {
56
+ amount: usdc(1000),
57
+ receive: {
58
+ platform: 'revolut',
59
+ currencies: ['EUR', 'GBP', 'USD'],
60
+ payee: { offchainId: 'revtag' },
61
+ },
62
+ },
63
+ { signer },
64
+ );
65
+
52
66
  for await (const order of cash.watch(depositId)) {
53
67
  console.log(order.state, order.explain());
54
68
  if (order.state === 'delivered') break;
@@ -88,6 +102,7 @@ console.log(source?.transactions?.origin, source?.transactions?.destination);
88
102
  | `relayStatus(requestId)` | Relay request status from the Relay SDK request path |
89
103
  | `estimate({ amount, currency }, { includeEta? })` | Base USDC oracle estimate; optionally skip the historical ETA for progressive rendering |
90
104
  | `cashout(input, { signer })` | Registers your payee, creates the protocol-held order, returns the `depositId` |
105
+ | `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, then resolve the confirmed createDeposit receipt into a resumable result |
91
106
  | `order(depositId)` / `orders(owner)` | Resume any order from its id alone; list all orders for a wallet |
92
107
  | `watch(depositId)` | Async iterator: yields on every state change until terminal, abort, or timeout |
93
108
  | `withdraw(depositId, { signer, amount? })` | The ONE unwind verb - partial with an `amount` (live intents don't block it), full close without (prunes expired intents first) |
@@ -102,9 +117,36 @@ transaction does before signing. `prepare()` is Base-USDC-only and rejects a
102
117
  `source` with `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE`. A signer-backed app can
103
118
  use `cashout({ source }, { signer, sourceSigner })`; a custody-separated host
104
119
  must execute and confirm its Relay route before preparing the Base-USDC
105
- cashout. Every Peer Cash transaction, including approves, carries ERC-8021
120
+ cashout. After externally executing a prepared `createDeposit`, pass its
121
+ confirmed receipt to `finalizePreparedCashout()` to recover the same
122
+ `CashoutResult` shape as `cashout()` without importing protocol ABIs. Every
123
+ Peer Cash transaction, including approves, carries ERC-8021
106
124
  attribution: `peer-cash` first, your own `referrer` code(s) after it.
107
125
 
126
+ ```ts
127
+ const prepared = await cash.prepare({
128
+ amount: 5_000_000n,
129
+ receive: {
130
+ platform: 'venmo',
131
+ currency: 'USD',
132
+ payee: { offchainId: '@maker' },
133
+ },
134
+ });
135
+
136
+ let createDepositReceipt;
137
+ for (const [index, transaction] of prepared.txs.entries()) {
138
+ const receipt = await externalRuntime.sendAndWait(transaction);
139
+ if (prepared.steps[index]?.kind === 'createDeposit') {
140
+ createDepositReceipt = receipt;
141
+ }
142
+ }
143
+ if (!createDepositReceipt) throw new Error('createDeposit receipt missing');
144
+
145
+ const result = cash.finalizePreparedCashout(createDepositReceipt);
146
+ await persistDepositId(result.depositId);
147
+ const liveOrder = await cash.order(result.depositId);
148
+ ```
149
+
108
150
  `capabilities()` presents Zelle as one platform. A cashout with
109
151
  `receive.platform: 'zelle'` attaches only the generic Zelle payment method to
110
152
  the deposit. Bank-specific capture routing is outside this maker-side SDK and
@@ -81,6 +81,12 @@ var errors = {
81
81
  retryable: false,
82
82
  remediation: `Use a positive minimum no greater than the maximum, and a maximum no greater than the cash-out amount.`
83
83
  }),
84
+ invalidPayoutCurrencies: (platform, reason) => new CashError({
85
+ code: "INVALID_PAYOUT_CURRENCIES",
86
+ message: `The ${platform} payout currency set is invalid: ${reason}.`,
87
+ retryable: false,
88
+ remediation: `Pass one or more unique currencies listed for ${platform} by capabilities().`
89
+ }),
84
90
  activeIntentBlocksWithdrawal: (depositId) => new CashError({
85
91
  code: "ACTIVE_INTENT_BLOCKS_WITHDRAWAL",
86
92
  message: `Order ${depositId} has a live buyer intent; escrow blocks withdrawal while a buyer may still deliver.`,
@@ -284,7 +290,12 @@ var errors = {
284
290
  code: "DEPOSIT_RESOLUTION_FAILED",
285
291
  message: `Deposit transaction ${txHash} succeeded but no DepositReceived event was found in the receipt.`,
286
292
  retryable: false,
287
- remediation: `Inspect the transaction on Basescan; recover the depositId from the DepositReceived log manually, then resume with order(depositId).`
293
+ remediation: `Inspect the transaction on Basescan; recover the depositId from the DepositReceived log manually, then resume with order(depositId).`,
294
+ recovery: {
295
+ kind: "inspect-base-transaction",
296
+ transactionHash: txHash,
297
+ operation: "cashout"
298
+ }
288
299
  }),
289
300
  signerRequired: (verb) => new CashError({
290
301
  code: "SIGNER_REQUIRED",
@@ -322,6 +333,15 @@ var errors = {
322
333
  },
323
334
  { cause }
324
335
  ),
336
+ transactionRejected: (verb, cause) => new CashError(
337
+ {
338
+ code: "TRANSACTION_REJECTED",
339
+ message: `The ${verb} wallet request was cancelled.`,
340
+ retryable: true,
341
+ remediation: `Retry the original Peer Cash action and approve the wallet request when you are ready.`
342
+ },
343
+ { cause }
344
+ ),
325
345
  transactionSubmissionUnknown: (operation, cause, recovery) => new CashError(
326
346
  {
327
347
  code: "TRANSACTION_SUBMISSION_UNKNOWN",
@@ -365,6 +385,7 @@ var errors = {
365
385
  };
366
386
  function mapChainError(verb, err, context = {}) {
367
387
  if (isCashError(err)) return err;
388
+ if (isUserRejectedError(err)) return errors.transactionRejected(verb, err);
368
389
  const message = err instanceof Error ? err.message : String(err);
369
390
  if (/\bpaused\b/i.test(message)) return errors.escrowPaused();
370
391
  if (/exceeds balance|insufficient token balance/i.test(message)) {
@@ -375,5 +396,41 @@ function mapChainError(verb, err, context = {}) {
375
396
  }
376
397
  return errors.chainCallFailed(verb, err);
377
398
  }
399
+ function hasUserRejectionText(value) {
400
+ const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "");
401
+ return normalized.includes("userrejected") || normalized.includes("userdenied") || normalized.includes("requestrejected") || normalized.includes("rejectedrequest") || /(^|[^a-z0-9])action[_ -]?rejected(?:error)?($|[^a-z0-9])/i.test(value) || normalized === "actionrejected" || normalized === "actionrejectederror";
402
+ }
403
+ function isUserRejectedError(value) {
404
+ const seen = /* @__PURE__ */ new Set();
405
+ const text = [];
406
+ let current = value;
407
+ while (current !== null && !seen.has(current)) {
408
+ seen.add(current);
409
+ if (current === -32003 || current === "-32003") return false;
410
+ if (current === 4001 || current === "4001" || current === 5e3 || current === "5000") {
411
+ return true;
412
+ }
413
+ if (typeof current === "string") {
414
+ text.push(current);
415
+ break;
416
+ }
417
+ if (typeof current !== "object" && typeof current !== "function") break;
418
+ const detail = current;
419
+ if (detail.code === -32003 || detail.code === "-32003" || detail.name === "TransactionRejectedRpcError") {
420
+ return false;
421
+ }
422
+ if (detail.code === 4001 || detail.code === "4001" || detail.code === 5e3 || detail.code === "5000" || detail.code === "ACTION_REJECTED" || detail.name === "UserRejectedRequestError") {
423
+ return true;
424
+ }
425
+ text.push(
426
+ ...[detail.name, detail.message, detail.code].filter(
427
+ (part) => typeof part === "string"
428
+ )
429
+ );
430
+ if (detail.cause === void 0) break;
431
+ current = detail.cause;
432
+ }
433
+ return text.some(hasUserRejectionText);
434
+ }
378
435
 
379
- export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, CashError, MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, USDC_DECIMALS, errors, isCashError, mapChainError };
436
+ export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, CashError, MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, USDC_DECIMALS, errors, isCashError, isUserRejectedError, mapChainError };
@@ -1,4 +1,4 @@
1
- import { Address, WalletClient, Hash, Transport } from 'viem';
1
+ import { Address, WalletClient, Hash, Log, Transport } from 'viem';
2
2
  import { IndexerIntentStatus, Zkp2pClient, IndexerIntent, CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
3
3
  import { Execute, RelayClient, RelayChain, ProgressData } from '@relayprotocol/relay-sdk';
4
4
 
@@ -172,15 +172,20 @@ interface CashBuyerProfile {
172
172
  firstSeenAt?: number;
173
173
  lastSeenAt?: number;
174
174
  }
175
- /** A single payout leg of a cash-out (one platform + currency + payee). */
176
- interface CashPayout {
175
+ interface CashPayoutBase {
177
176
  /** Payment platform / processor name, e.g. `'venmo'`, `'revolut'`, `'wise'`. */
178
177
  processorName: string;
179
- /** Fiat currency the user wants to receive. */
180
- currency: CurrencyType;
181
178
  /** The user's payee handle for that platform (e.g. a Venmo username, Wisetag). */
182
179
  payeeData: CuratorPayeeDataInput;
183
180
  }
181
+ /** One payment method offering either one currency or a non-empty currency set. */
182
+ type CashPayout = CashPayoutBase & ({
183
+ currency: CurrencyType;
184
+ currencies?: never;
185
+ } | {
186
+ currency?: never;
187
+ currencies: readonly [CurrencyType, ...CurrencyType[]];
188
+ });
184
189
  /**
185
190
  * Input to create a market-rate (0% spread) cash-out deposit.
186
191
  *
@@ -193,7 +198,7 @@ interface CashDepositInput {
193
198
  token?: Address;
194
199
  /** Total amount to cash out, in USDC base units (6 decimals). */
195
200
  amount: bigint;
196
- /** One or more payout legs (platform + currency + payee). */
201
+ /** One or more payout legs (platform + currency choice + payee). */
197
202
  payouts: CashPayout[];
198
203
  /** Per-order min/max in USDC base units. Defaults derive from {@link buildIntentAmountRange}. */
199
204
  intentAmountRange?: {
@@ -435,8 +440,12 @@ interface CashEstimate {
435
440
  eta?: CashFillEta;
436
441
  }
437
442
 
443
+ type CashPayeeInput = string | CuratorPayeeDataInput;
444
+ /** Convert user-entered handles into the curator form for a payment platform. */
445
+ declare function normalizeCashPayee(platform: string, payee: CashPayeeInput): CuratorPayeeDataInput;
446
+
438
447
  /**
439
- * `createCashClient` - the eight-verb facade over a read-only `Zkp2pClient`.
448
+ * `createCashClient` - the cash lifecycle facade over a read-only `Zkp2pClient`.
440
449
  *
441
450
  * The facade keeps the outward surface tiny (capabilities / estimate / cashout
442
451
  * / order / orders / watch / withdraw / topUp) while reusing the published
@@ -481,8 +490,18 @@ interface CashLeg {
481
490
  platform: string;
482
491
  /** Fiat currency to receive. */
483
492
  currency: CurrencyType;
484
- /** Payee details, e.g. `{ offchainId: '@andrew' }`. */
485
- payee: CuratorPayeeDataInput;
493
+ /** Raw handle or prepared curator data (needed for identity attestations). */
494
+ payee: CashPayeeInput;
495
+ currencies?: never;
496
+ }
497
+ interface CashMultiCurrencyLeg {
498
+ /** Platform id from `capabilities()`, e.g. `'revolut'`. */
499
+ platform: string;
500
+ /** Fiat currencies a buyer may use to fill this cash-out. */
501
+ currencies: readonly [CurrencyType, ...CurrencyType[]];
502
+ /** Raw handle or prepared curator data shared by every offered currency. */
503
+ payee: CashPayeeInput;
504
+ currency?: never;
486
505
  }
487
506
  interface CashoutInput {
488
507
  /**
@@ -498,8 +517,8 @@ interface CashoutInput {
498
517
  /** Relay amount mode. Omit for the recommended exact source-input flow. */
499
518
  tradeType?: 'EXACT_INPUT' | 'EXACT_OUTPUT' | 'EXPECTED_OUTPUT';
500
519
  };
501
- /** Where the fiat should arrive. Multi-payout is a deliberate v1 cut. */
502
- receive: CashLeg;
520
+ /** Where the fiat should arrive. One method may offer multiple currencies. */
521
+ receive: CashLeg | CashMultiCurrencyLeg;
503
522
  /** Per-order min/max override (USDC base units). */
504
523
  intentAmountRange?: {
505
524
  min: bigint;
@@ -572,6 +591,12 @@ interface PrepareResult {
572
591
  hashedOnchainIds: string[];
573
592
  };
574
593
  }
594
+ /** Confirmed createDeposit receipt from an externally executed prepare() plan. */
595
+ interface PreparedCashoutReceipt {
596
+ transactionHash: Hash;
597
+ status: 'success' | 'reverted';
598
+ logs: readonly Log[];
599
+ }
575
600
  interface WithdrawResult {
576
601
  depositId: string;
577
602
  /** Present when expired intents had to be pruned before withdrawal. */
@@ -624,6 +649,8 @@ interface CashClient {
624
649
  cashout(input: CashoutInput, opts: CashoutOptions): Promise<CashoutResult>;
625
650
  /** 2b - Unsigned path: `txs[]` for agent wallets, AA, server keys, policy layers. */
626
651
  prepare(input: CashoutInput): Promise<PrepareResult>;
652
+ /** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
653
+ finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
627
654
  /** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
628
655
  order(depositId: string): Promise<CashOrder>;
629
656
  /**
@@ -662,4 +689,4 @@ interface CashClient {
662
689
  }
663
690
  declare function createCashClient(options: CashClientOptions): CashClient;
664
691
 
665
- export { type CashPlatformCapability as A, type CashPreparedStepKind as B, type CashPayoutInfo as C, type CashoutInput as D, type CashoutOptions as E, type CuratorPayeeDataInput as F, type EstimateInput as G, type EstimateOptions as H, type IntentStatus as I, RECOMMENDED_MIN_CASHOUT_AMOUNT as J, type RelayOptions as K, type RelayQuoteInput as L, MIN_CASHOUT_AMOUNT as M, type RelaySourceInput as N, type OrdersOptions as O, type PrepareResult as P, type RelayTransaction as Q, type RelayExecutionResult as R, type SignerOptions as S, type TopUpResult as T, type WatchOptions as U, type WithdrawOptions as V, type WithdrawResult as W, buildCapabilities as X, createCashClient as Y, type IntentEntity as a, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashFillStats as j, type CashPreparedStep as k, type RelayQuote as l, type RelayStatus as m, type CashSourceCapabilities as n, CASH_ATTRIBUTION_CODE as o, type CashAsset as p, type CashChain as q, type CashClient as r, type CashClientOptions as s, type CashFillEta as t, type CashLeg as u, type CashNextAction as v, type CashOrderState as w, type CashPairFillStats as x, type CashPayout as y, type CashPayoutPricing as z };
692
+ export { createCashClient as $, type CashPayout as A, type CashPayoutPricing as B, type CashPayoutInfo as C, type CashPlatformCapability as D, type CashPreparedStepKind as E, type CashoutInput as F, type CashoutOptions as G, type CuratorPayeeDataInput as H, type IntentStatus as I, type EstimateInput as J, type EstimateOptions as K, type PreparedCashoutReceipt as L, MIN_CASHOUT_AMOUNT as M, RECOMMENDED_MIN_CASHOUT_AMOUNT as N, type OrdersOptions as O, type PrepareResult as P, type RelayOptions as Q, type RelayExecutionResult as R, type RelayQuoteInput as S, type TopUpResult as T, type RelaySourceInput as U, type RelayTransaction as V, type WithdrawResult as W, type SignerOptions as X, type WatchOptions as Y, type WithdrawOptions as Z, buildCapabilities as _, type IntentEntity as a, normalizeCashPayee as a0, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashFillStats as j, type CashPreparedStep as k, type RelayQuote as l, type RelayStatus as m, type CashSourceCapabilities as n, CASH_ATTRIBUTION_CODE as o, type CashAsset as p, type CashChain as q, type CashClient as r, type CashClientOptions as s, type CashFillEta as t, type CashLeg as u, type CashMultiCurrencyLeg as v, type CashNextAction as w, type CashOrderState as x, type CashPairFillStats as y, type CashPayeeInput as z };
@@ -1,4 +1,4 @@
1
- import { Address, WalletClient, Hash, Transport } from 'viem';
1
+ import { Address, WalletClient, Hash, Log, Transport } from 'viem';
2
2
  import { IndexerIntentStatus, Zkp2pClient, IndexerIntent, CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
3
3
  import { Execute, RelayClient, RelayChain, ProgressData } from '@relayprotocol/relay-sdk';
4
4
 
@@ -172,15 +172,20 @@ interface CashBuyerProfile {
172
172
  firstSeenAt?: number;
173
173
  lastSeenAt?: number;
174
174
  }
175
- /** A single payout leg of a cash-out (one platform + currency + payee). */
176
- interface CashPayout {
175
+ interface CashPayoutBase {
177
176
  /** Payment platform / processor name, e.g. `'venmo'`, `'revolut'`, `'wise'`. */
178
177
  processorName: string;
179
- /** Fiat currency the user wants to receive. */
180
- currency: CurrencyType;
181
178
  /** The user's payee handle for that platform (e.g. a Venmo username, Wisetag). */
182
179
  payeeData: CuratorPayeeDataInput;
183
180
  }
181
+ /** One payment method offering either one currency or a non-empty currency set. */
182
+ type CashPayout = CashPayoutBase & ({
183
+ currency: CurrencyType;
184
+ currencies?: never;
185
+ } | {
186
+ currency?: never;
187
+ currencies: readonly [CurrencyType, ...CurrencyType[]];
188
+ });
184
189
  /**
185
190
  * Input to create a market-rate (0% spread) cash-out deposit.
186
191
  *
@@ -193,7 +198,7 @@ interface CashDepositInput {
193
198
  token?: Address;
194
199
  /** Total amount to cash out, in USDC base units (6 decimals). */
195
200
  amount: bigint;
196
- /** One or more payout legs (platform + currency + payee). */
201
+ /** One or more payout legs (platform + currency choice + payee). */
197
202
  payouts: CashPayout[];
198
203
  /** Per-order min/max in USDC base units. Defaults derive from {@link buildIntentAmountRange}. */
199
204
  intentAmountRange?: {
@@ -435,8 +440,12 @@ interface CashEstimate {
435
440
  eta?: CashFillEta;
436
441
  }
437
442
 
443
+ type CashPayeeInput = string | CuratorPayeeDataInput;
444
+ /** Convert user-entered handles into the curator form for a payment platform. */
445
+ declare function normalizeCashPayee(platform: string, payee: CashPayeeInput): CuratorPayeeDataInput;
446
+
438
447
  /**
439
- * `createCashClient` - the eight-verb facade over a read-only `Zkp2pClient`.
448
+ * `createCashClient` - the cash lifecycle facade over a read-only `Zkp2pClient`.
440
449
  *
441
450
  * The facade keeps the outward surface tiny (capabilities / estimate / cashout
442
451
  * / order / orders / watch / withdraw / topUp) while reusing the published
@@ -481,8 +490,18 @@ interface CashLeg {
481
490
  platform: string;
482
491
  /** Fiat currency to receive. */
483
492
  currency: CurrencyType;
484
- /** Payee details, e.g. `{ offchainId: '@andrew' }`. */
485
- payee: CuratorPayeeDataInput;
493
+ /** Raw handle or prepared curator data (needed for identity attestations). */
494
+ payee: CashPayeeInput;
495
+ currencies?: never;
496
+ }
497
+ interface CashMultiCurrencyLeg {
498
+ /** Platform id from `capabilities()`, e.g. `'revolut'`. */
499
+ platform: string;
500
+ /** Fiat currencies a buyer may use to fill this cash-out. */
501
+ currencies: readonly [CurrencyType, ...CurrencyType[]];
502
+ /** Raw handle or prepared curator data shared by every offered currency. */
503
+ payee: CashPayeeInput;
504
+ currency?: never;
486
505
  }
487
506
  interface CashoutInput {
488
507
  /**
@@ -498,8 +517,8 @@ interface CashoutInput {
498
517
  /** Relay amount mode. Omit for the recommended exact source-input flow. */
499
518
  tradeType?: 'EXACT_INPUT' | 'EXACT_OUTPUT' | 'EXPECTED_OUTPUT';
500
519
  };
501
- /** Where the fiat should arrive. Multi-payout is a deliberate v1 cut. */
502
- receive: CashLeg;
520
+ /** Where the fiat should arrive. One method may offer multiple currencies. */
521
+ receive: CashLeg | CashMultiCurrencyLeg;
503
522
  /** Per-order min/max override (USDC base units). */
504
523
  intentAmountRange?: {
505
524
  min: bigint;
@@ -572,6 +591,12 @@ interface PrepareResult {
572
591
  hashedOnchainIds: string[];
573
592
  };
574
593
  }
594
+ /** Confirmed createDeposit receipt from an externally executed prepare() plan. */
595
+ interface PreparedCashoutReceipt {
596
+ transactionHash: Hash;
597
+ status: 'success' | 'reverted';
598
+ logs: readonly Log[];
599
+ }
575
600
  interface WithdrawResult {
576
601
  depositId: string;
577
602
  /** Present when expired intents had to be pruned before withdrawal. */
@@ -624,6 +649,8 @@ interface CashClient {
624
649
  cashout(input: CashoutInput, opts: CashoutOptions): Promise<CashoutResult>;
625
650
  /** 2b - Unsigned path: `txs[]` for agent wallets, AA, server keys, policy layers. */
626
651
  prepare(input: CashoutInput): Promise<PrepareResult>;
652
+ /** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
653
+ finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
627
654
  /** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
628
655
  order(depositId: string): Promise<CashOrder>;
629
656
  /**
@@ -662,4 +689,4 @@ interface CashClient {
662
689
  }
663
690
  declare function createCashClient(options: CashClientOptions): CashClient;
664
691
 
665
- export { type CashPlatformCapability as A, type CashPreparedStepKind as B, type CashPayoutInfo as C, type CashoutInput as D, type CashoutOptions as E, type CuratorPayeeDataInput as F, type EstimateInput as G, type EstimateOptions as H, type IntentStatus as I, RECOMMENDED_MIN_CASHOUT_AMOUNT as J, type RelayOptions as K, type RelayQuoteInput as L, MIN_CASHOUT_AMOUNT as M, type RelaySourceInput as N, type OrdersOptions as O, type PrepareResult as P, type RelayTransaction as Q, type RelayExecutionResult as R, type SignerOptions as S, type TopUpResult as T, type WatchOptions as U, type WithdrawOptions as V, type WithdrawResult as W, buildCapabilities as X, createCashClient as Y, type IntentEntity as a, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashFillStats as j, type CashPreparedStep as k, type RelayQuote as l, type RelayStatus as m, type CashSourceCapabilities as n, CASH_ATTRIBUTION_CODE as o, type CashAsset as p, type CashChain as q, type CashClient as r, type CashClientOptions as s, type CashFillEta as t, type CashLeg as u, type CashNextAction as v, type CashOrderState as w, type CashPairFillStats as x, type CashPayout as y, type CashPayoutPricing as z };
692
+ export { createCashClient as $, type CashPayout as A, type CashPayoutPricing as B, type CashPayoutInfo as C, type CashPlatformCapability as D, type CashPreparedStepKind as E, type CashoutInput as F, type CashoutOptions as G, type CuratorPayeeDataInput as H, type IntentStatus as I, type EstimateInput as J, type EstimateOptions as K, type PreparedCashoutReceipt as L, MIN_CASHOUT_AMOUNT as M, RECOMMENDED_MIN_CASHOUT_AMOUNT as N, type OrdersOptions as O, type PrepareResult as P, type RelayOptions as Q, type RelayExecutionResult as R, type RelayQuoteInput as S, type TopUpResult as T, type RelaySourceInput as U, type RelayTransaction as V, type WithdrawResult as W, type SignerOptions as X, type WatchOptions as Y, type WithdrawOptions as Z, buildCapabilities as _, type IntentEntity as a, normalizeCashPayee as a0, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashFillStats as j, type CashPreparedStep as k, type RelayQuote as l, type RelayStatus as m, type CashSourceCapabilities as n, CASH_ATTRIBUTION_CODE as o, type CashAsset as p, type CashChain as q, type CashClient as r, type CashClientOptions as s, type CashFillEta as t, type CashLeg as u, type CashMultiCurrencyLeg as v, type CashNextAction as w, type CashOrderState as x, type CashPairFillStats as y, type CashPayeeInput as z };