@zkp2p/cash 0.4.8 → 0.4.9

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
@@ -1,5 +1,9 @@
1
1
  # @zkp2p/cash - agent integration manual
2
2
 
3
+ > This file is the shipped manual for agents **using** the package. To
4
+ > contribute to the SDK itself (layout, ground rules, CI, releasing), start at
5
+ > [CLAUDE.md](https://github.com/zkp2p/peer-cash/blob/main/CLAUDE.md).
6
+
3
7
  You are integrating Peer Cash: an offramp that routes any Relay-supported EVM
4
8
  source asset into Base USDC, then converts Base USDC to fiat (Venmo, Revolut,
5
9
  Wise, Zelle, ...) at the live Chainlink market rate. The user whose USDC you
@@ -18,7 +22,9 @@ can withdraw an unmatched deposit.
18
22
  (`{ to, data, value, chainId }`) plus same-index `steps[]` labels; inspect
19
23
  the plan, submit the transactions in order, and wait for each receipt. After
20
24
  `createDeposit` confirms, pass its receipt to `finalizePreparedCashout()` and
21
- persist the returned `depositId`.
25
+ persist the returned `depositId`. If the original plan set
26
+ `accessPolicyRequired: true`, submit and confirm
27
+ `prepareAccessPolicy(depositId)` with the depositor next.
22
28
  3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
23
29
  `@zkp2p/cash/tools` and map the tool names to the verbs above. Base-USDC
24
30
  mutating tools return unsigned transactions. `cash_source_quote` is a quote,
@@ -32,9 +38,10 @@ Every transaction (including approves) carries ERC-8021 attribution:
32
38
  code. The namespaced referral marker gives its Peer Privy wallet the direct
33
39
  deposit-level integration share instead of applying maker L1/L2.
34
40
 
35
- **Platform caveats, all surfaced in `capabilities()`:**
41
+ **Platform caveats:**
36
42
 
37
- - **Venmo, Cash App, and PayPal attach access groups by default.** Signed
43
+ - **Venmo, Cash App, and PayPal restrict who can signal intents by default.**
44
+ If any payout leg uses one of these platforms, signed
38
45
  `cashout()` confirms `createDeposit`, then submits and confirms the Plus, Pro,
39
46
  Peer Makers, and Peer Pay policy using the same viem wallet. This is a
40
47
  deliberate non-atomic follow-up with a brief unprotected interval. For
@@ -44,9 +51,10 @@ deposit-level integration share instead of applying maker L1/L2.
44
51
 
45
52
  - **Wise and PayPal** carry `requiresIdentityAttestation: true`. A new curator
46
53
  registration needs a signed maker identity attestation this SDK cannot mint
47
- (it comes from the Peer app/extension). An already-registered handle can be
48
- reused with bare payee data. A new handle without its attestation fails with
49
- `PAYEE_VERIFICATION_REQUIRED` before funds move on-chain.
54
+ (first-party Peer web obtains it through the Peer TEE browser extension).
55
+ An already-registered handle can be reused with bare payee data. A new handle
56
+ without its attestation fails with `PAYEE_VERIFICATION_REQUIRED` before funds
57
+ move on-chain.
50
58
  - **Venmo, Revolut, Cash App, Monzo** validate the handle against the live
51
59
  platform at registration - the account must exist. The rest (Zelle, Chime,
52
60
  etc.) are format-checked only. Match handles to the `payeeHint`.
@@ -180,6 +188,10 @@ console.log(routed.source?.transactions?.origin, routed.source?.transactions?.de
180
188
  broadcast; follow its recovery action and inspect wallet/protocol state.
181
189
  `TRANSACTION_STATUS_UNKNOWN` means the returned hash may already have
182
190
  succeeded. Inspect `err.recovery.transactionHash` first.
191
+ - **Never repeat a cash-out after an access-policy failure.** The deposit
192
+ already exists. If `recovery.transactionHash` is present, inspect that policy
193
+ transaction first; prepare another policy transaction only when the previous
194
+ one is absent or confirmed reverted.
183
195
  - **`ORDER_NOT_FOUND` seconds after `cashout()` is indexer lag**, not a lost
184
196
  deposit. The tx receipt you hold is the truth. Retry; `watch()` absorbs
185
197
  this automatically.
@@ -214,10 +226,10 @@ Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
214
226
  | `INVALID_INTENT_AMOUNT_RANGE` | no | Use a positive min, max at least min, and max no greater than amount |
215
227
  | `INVALID_PAYOUT_CURRENCIES` | no | Pass one or more unique currencies listed for the platform |
216
228
  | `INVALID_PAYOUT_PLATFORMS` | no | Pass one leg or an array of legs, using each platform at most once |
217
- | `PAYEE_VERIFICATION_REQUIRED` | no | Register a new Wise/PayPal payee through Peer; an existing registered handle can be reused |
229
+ | `PAYEE_VERIFICATION_REQUIRED` | no | Use Peer web + TEE extension for new Wise/PayPal; reuse registered handles |
218
230
  | `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
219
231
  | `ATOMIC_ACCESS_POLICY_REQUIRED` | no | Deprecated compatibility code; current SDK flows never emit it |
220
- | `ACCESS_POLICY_CONFIGURATION_FAILED` | no | Deposit exists; retry its policy with `recovery.depositId`, never create another cash-out |
232
+ | `ACCESS_POLICY_CONFIGURATION_FAILED` | no | Deposit exists; inspect policy tx first, attach only if needed; never repeat the cash-out. |
221
233
  | `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
222
234
  | `SOURCE_RECIPIENT_MISMATCH` | no | Route Base USDC to the cashout depositor |
223
235
  | `SOURCE_CAPABILITIES_FAILED` | yes | Retry discovery or fall back to Base USDC |
@@ -257,7 +269,8 @@ and tool results.
257
269
  Prove your integration against `environment: 'staging'` with a funded test
258
270
  wallet. Never wait on a buyer - buyer-side is out of your scope:
259
271
 
260
- 1. `cashout()` a small amount (1–2 USDC) → capture `depositId`.
272
+ 1. `cashout()` a small amount (1–2 USDC) → capture `depositId` and, for a
273
+ restricted payout, `accessPolicyTxHash`.
261
274
  2. `order(depositId)` shows `awaiting-buyer` (retry through indexer lag).
262
275
  3. `orders(owner)` includes the deposit.
263
276
  4. `withdraw(depositId)` → transaction succeeds.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @zkp2p/cash
2
2
 
3
- Route any Relay-supported EVM source asset into Base USDC, then cash out to fiat
4
- on Venmo, Revolut, Wise, Zelle, and more at the live Chainlink market rate,
5
- with zero spread and no centralized off-ramp provider.
3
+ Route any Relay-supported EVM source asset into Base USDC, then cash out to
4
+ fiat on Venmo, Revolut, Wise, Zelle, and more at the live Chainlink market
5
+ rate, with zero spread and no centralized off-ramp provider.
6
6
 
7
7
  Peer Cash is an **offramp-only** SDK for the [ZKP2P](https://peer.xyz)
8
8
  protocol. The cashing-out user is the maker: their USDC becomes a deposit in
@@ -12,18 +12,13 @@ No hosted widget, no provider custody, no quote engine to maintain.
12
12
 
13
13
  **[npm](https://www.npmjs.com/package/@zkp2p/cash)** · **[Lifecycle and recovery](docs/lifecycle-and-recovery.md)** · **[Agent integration manual](AGENTS.md)**
14
14
 
15
- ## Pick the right SDK
16
-
17
- Peer Cash and the general ZKP2P SDK serve different integration depths:
15
+ ## Install
18
16
 
19
- | Package | Use it when | Boundary |
20
- | ------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
21
- | `@zkp2p/cash` | Cash-out is the product | Offramp only. The user is always the maker, the destination is Base USDC, pricing is the live Chainlink rate at fill with zero spread, and the SDK owns the resumable order lifecycle. |
22
- | `@zkp2p/sdk` | You are composing directly with the Peer protocol | General maker and taker operations, deposits, intents, proofs, quotes, vaults, rate managers, referrals, hooks, and API helpers. Your application owns the workflow and protocol choices. |
17
+ ```sh
18
+ npm install @zkp2p/cash viem
19
+ ```
23
20
 
24
- Peer Cash is a narrow facade over `@zkp2p/sdk`, not a replacement for it. It
25
- cannot express custom spreads, buyer-side proof flows, vaults, disputes, or
26
- arbitrary protocol operations.
21
+ ## Quickstart
27
22
 
28
23
  ```ts
29
24
  import { createCashClient, usdc } from '@zkp2p/cash';
@@ -43,13 +38,15 @@ const fillStats = await cash.fillStats();
43
38
  const pairStats = fillStats['venmo:USD'];
44
39
  const multiCurrencyStats = fillStats['revolut:EUR+GBP+USD'];
45
40
 
46
- const { depositId } = await cash.cashout(
41
+ const { depositId, accessPolicyTxHash } = await cash.cashout(
47
42
  {
48
43
  amount: usdc(1000),
49
44
  receive: { platform: 'venmo', currency: 'USD', payee: '@you' },
50
45
  },
51
46
  { signer }, // any viem WalletClient on Base, including an EOA
52
47
  );
48
+ // Venmo, Cash App, and PayPal return only after their access policy confirms.
49
+ console.log(depositId, accessPolicyTxHash);
53
50
 
54
51
  // One method can offer several currencies. The buyer chooses the fill
55
52
  // currency, and each option resolves at its own live oracle rate.
@@ -84,52 +81,18 @@ for await (const order of cash.watch(depositId)) {
84
81
  }
85
82
  ```
86
83
 
87
- ## Earn the integration share
88
-
89
- Use the same six-character referral code shown in your Peer mobile or web app.
90
- No API key, registration transaction, or separate receiving address is needed:
91
- the referral code already belongs to your Peer Privy wallet.
92
-
93
- ```ts
94
- const cash = createCashClient({
95
- environment: 'production',
96
- referralCode: 'ABC123',
97
- });
98
- ```
99
-
100
- The SDK normalizes the value and stamps `peer-ref-ABC123` into ERC-8021
101
- attribution on the deposit transaction. When that liquidity is filled, Curator
102
- pays the code owner 50 bps, capped by the configured Peer service fee. This is
103
- the deposit-level integration path: it replaces the maker L1/L2 referral split
104
- for that deposit instead of enrolling the cashing-out user as your referee.
105
-
106
- The mapping is permanent. If you later customize your displayed Peer referral
107
- code, open deposits carrying the old code still pay the same wallet. Include at
108
- most one `peer-ref-XXXXXX` marker; an unknown or conflicting marker receives no
109
- integration share. The existing `referrer` option remains available for
110
- analytics-only ERC-8021 codes such as `acme-app`.
84
+ ## Pick the right SDK
111
85
 
112
- Source asset path:
86
+ Peer Cash and the general ZKP2P SDK serve different integration depths:
113
87
 
114
- ```ts
115
- const { depositId, source } = await cash.cashout(
116
- {
117
- amount: 10_000_000n, // exact input: 10 USDC in source-token base units
118
- source: {
119
- chainId: 1,
120
- currency: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
121
- tradeType: 'EXACT_INPUT',
122
- },
123
- receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
124
- },
125
- { signer, sourceSigner },
126
- );
88
+ | Package | Use it when | Boundary |
89
+ | ------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90
+ | `@zkp2p/cash` | Cash-out is the product | Offramp only. The user is always the maker, the destination is Base USDC, pricing is the live Chainlink rate at fill with zero spread, and the SDK owns the resumable order lifecycle. |
91
+ | `@zkp2p/sdk` | You are composing directly with the Peer protocol | General maker and taker operations, deposits, intents, proofs, quotes, vaults, rate managers, referrals, hooks, and API helpers. Your application owns the workflow and protocol choices. |
127
92
 
128
- // source.amount is Relay's guaranteed minimum Base USDC output and the exact
129
- // amount deposited into the cash-out order. It is not the route's actual output.
130
- console.log(source?.amount, source?.requestId);
131
- console.log(source?.transactions?.origin, source?.transactions?.destination);
132
- ```
93
+ Peer Cash is a narrow facade over `@zkp2p/sdk`, not a replacement for it. It
94
+ cannot express custom spreads, buyer-side proof flows, vaults, disputes, or
95
+ arbitrary protocol operations.
133
96
 
134
97
  ## The core verbs
135
98
 
@@ -165,32 +128,55 @@ Peer Cash transaction, including approves, carries ERC-8021 attribution:
165
128
  `peer-cash` first, optional `peer-ref-XXXXXX` from `referralCode` next, and your
166
129
  analytics-only `referrer` code(s) after it.
167
130
 
131
+ Order reads fail closed against the same active catalog. If any method on an
132
+ indexed deposit is unsupported, `orders()` excludes the whole deposit and
133
+ `order()` returns `ORDER_NOT_FOUND`; Peer Cash never partially reclassifies a
134
+ mixed historical deposit.
135
+
136
+ ## Payout rails and access policies
137
+
138
+ | Payout rail | Access-policy behavior | New payee registration |
139
+ | --------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
140
+ | Venmo / Cash App | Four groups attach after deposit confirmation | Curator validates the live handle |
141
+ | PayPal | Same four-group follow-up | Requires a Peer TEE browser-extension identity attestation |
142
+ | Wise | No access-policy follow-up | Requires a Peer TEE browser-extension identity attestation |
143
+ | Other supported rails | No access-policy follow-up; use `capabilities()` for currencies and format | Follow the `payeeHint`; live-validation behavior is described in the integration guide |
144
+
168
145
  No platform requires an atomic access-policy flow. `cashout()` and `prepare()`
169
146
  work with any viem `WalletClient`, including a local or externally connected
170
147
  EOA; no Privy wallet or signer API is required. The deprecated
171
148
  `requiresAtomicAccessPolicy` capability remains for wire compatibility and is
172
149
  always `false`.
173
150
 
174
- Venmo, Cash App, and PayPal cash-outs attach Plus, Pro, Peer Makers, and Peer
175
- Pay groups by default. Signed `cashout()` creates the deposit first, then uses
176
- the same wallet to submit and confirm the policy transaction; this intentionally
151
+ Venmo, Cash App, and PayPal cash-outs restrict intent signaling to Plus, Pro,
152
+ Peer Makers, and Peer Pay groups by default. If any payout leg uses one of
153
+ those platforms, signed `cashout()` creates the deposit first, then uses the
154
+ same wallet to submit and confirm the policy transaction; this intentionally
177
155
  leaves a brief non-atomic interval. Prepared integrations receive
178
- `accessPolicyRequired: true` for those platforms and, after confirming
179
- `createDeposit`, must call `finalizePreparedCashout(receipt)` followed by
156
+ `accessPolicyRequired: true` and, after confirming `createDeposit`, must call
157
+ `finalizePreparedCashout(receipt)` followed by
180
158
  `prepareAccessPolicy(depositId)`. Other platforms do not need the follow-up.
159
+
181
160
  If policy attachment fails, `ACCESS_POLICY_CONFIGURATION_FAILED.recovery`
182
- identifies the existing deposit and any submitted policy transaction. Do not
183
- create another cash-out; resume the policy step with the same depositor wallet.
161
+ identifies the existing deposit and any submitted policy transaction. Never
162
+ create another cash-out. When `recovery.transactionHash` is present, inspect
163
+ that transaction before resubmitting; otherwise prepare the policy again with
164
+ the same depositor wallet.
184
165
 
185
166
  `capabilities()` presents Zelle as one platform. A cashout with
186
167
  `receive.platform: 'zelle'` attaches only the generic Zelle payment method to
187
168
  the deposit. Bank-specific capture routing is outside this maker-side SDK and
188
169
  never changes the on-chain payment method.
189
170
 
190
- Order reads fail closed against the same active catalog. If any method on an
191
- indexed deposit is unsupported, `orders()` excludes the whole deposit and
192
- `order()` returns `ORDER_NOT_FOUND`; Peer Cash never partially reclassifies a
193
- mixed historical deposit.
171
+ `capabilities()` tells you which platforms need a verified identity for a new
172
+ payee registration (`requiresIdentityAttestation` - Wise and PayPal today).
173
+ The SDK accepts an `identityAttestation` in structured payee data but does not
174
+ mint one. First-party Peer web obtains it through the Peer TEE browser
175
+ extension. An already-registered Wise or PayPal handle can be reused with bare
176
+ payee data. A new handle without its signed attestation fails during curator
177
+ registration with `PAYEE_VERIFICATION_REQUIRED`, before funds move on-chain.
178
+
179
+ ## Source routing (any EVM asset in)
194
180
 
195
181
  The default/minimal flow is unchanged: pass Base USDC base units to
196
182
  `estimate()` and `cashout()`. For any other source asset, pass `source` to
@@ -202,6 +188,27 @@ The destination is always canonical Base USDC
202
188
  discovered and quoted by `@relayprotocol/relay-sdk`, not a static token
203
189
  allowlist.
204
190
 
191
+ ```ts
192
+ const { depositId, accessPolicyTxHash, source } = await cash.cashout(
193
+ {
194
+ amount: 10_000_000n, // exact input: 10 USDC in source-token base units
195
+ source: {
196
+ chainId: 1,
197
+ currency: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
198
+ tradeType: 'EXACT_INPUT',
199
+ },
200
+ receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
201
+ },
202
+ { signer, sourceSigner },
203
+ );
204
+
205
+ // source.amount is Relay's guaranteed minimum Base USDC output and the exact
206
+ // amount deposited into the cash-out order. It is not the route's actual output.
207
+ console.log(source?.amount, source?.requestId);
208
+ console.log(source?.transactions?.origin, source?.transactions?.destination);
209
+ console.log(accessPolicyTxHash); // present because this example uses Venmo
210
+ ```
211
+
205
212
  Routes that submit more than one source-chain transaction (approve, then
206
213
  route) require a nonce-managed source signer -
207
214
  `privateKeyToAccount(pk, { nonceManager })` from viem. Without one the SDK
@@ -209,12 +216,6 @@ refuses the route preflight with `SOURCE_NONCE_MANAGER_REQUIRED` instead of
209
216
  letting the route transaction reuse the approval's nonce and revert
210
217
  mid-route. Browser wallets are unaffected.
211
218
 
212
- `capabilities()` tells you which platforms need a verified identity for a new
213
- payee registration (`requiresIdentityAttestation` - Wise and PayPal today).
214
- An already-registered Wise or PayPal handle can be reused with bare payee data.
215
- A new handle without its signed attestation fails during curator registration
216
- with `PAYEE_VERIFICATION_REQUIRED`, before funds move on-chain.
217
-
218
219
  ## Source-route recovery
219
220
 
220
221
  Persist `depositId`, transaction hashes, and the Relay `requestId` as soon as
@@ -242,7 +243,9 @@ they are available. A source-routed result includes both a flat
242
243
  wallet activity and the supplied recovery action before any retry.
243
244
  - `ACCESS_POLICY_CONFIGURATION_FAILED`: the deposit exists, but its required
244
245
  Venmo, Cash App, or PayPal policy was not confirmed. Do not cash out again;
245
- retry `prepareAccessPolicy(error.recovery.depositId)` with the depositor.
246
+ inspect `recovery.transactionHash` when present, then retry
247
+ `prepareAccessPolicy(error.recovery.depositId)` only if the prior policy
248
+ transaction did not succeed.
246
249
 
247
250
  Wallet clients pinned to the wrong chain fail with `SIGNER_CHAIN_MISMATCH`
248
251
  before a quote or transaction is submitted. Chainless wallets are checked
@@ -289,6 +292,31 @@ awaiting-buyer ──────────► matched ───────
289
292
 
290
293
  Deep dive: [docs/lifecycle-and-recovery.md](docs/lifecycle-and-recovery.md).
291
294
 
295
+ ## Earn the integration share
296
+
297
+ Use the same six-character referral code shown in your Peer mobile or web app.
298
+ No API key, registration transaction, or separate receiving address is needed:
299
+ the referral code already belongs to your Peer Privy wallet.
300
+
301
+ ```ts
302
+ const cash = createCashClient({
303
+ environment: 'production',
304
+ referralCode: 'ABC123',
305
+ });
306
+ ```
307
+
308
+ The SDK normalizes the value and stamps `peer-ref-ABC123` into ERC-8021
309
+ attribution on the deposit transaction. When that liquidity is filled, Curator
310
+ pays the code owner 50 bps, capped by the configured Peer service fee. This is
311
+ the deposit-level integration path: it replaces the maker L1/L2 referral split
312
+ for that deposit instead of enrolling the cashing-out user as your referee.
313
+
314
+ The mapping is permanent. If you later customize your displayed Peer referral
315
+ code, open deposits carrying the old code still pay the same wallet. Include at
316
+ most one `peer-ref-XXXXXX` marker; an unknown or conflicting marker receives no
317
+ integration share. The existing `referrer` option remains available for
318
+ analytics-only ERC-8021 codes such as `acme-app`.
319
+
292
320
  ## For agents
293
321
 
294
322
  - `cashout`/`withdraw`/`topUp` have unsigned counterparts (`prepare`,
@@ -296,6 +324,9 @@ Deep dive: [docs/lifecycle-and-recovery.md](docs/lifecycle-and-recovery.md).
296
324
  before signing, then submit the matching `txs[]` in order.
297
325
  - Mutating tool calls return unsigned transactions by default; signing stays
298
326
  with the host that owns custody, policy, and user approval.
327
+ - After a prepared restricted cash-out confirms, the host adapter must call
328
+ `finalizePreparedCashout(receipt)` and `prepareAccessPolicy(depositId)`; these
329
+ receipt/signing operations are `CashClient` methods, not built-in tool calls.
299
330
  - Every error carries `code`, `retryable`, and a `remediation` sentence.
300
331
  - Every order carries `nextActions: ('wait' | 'withdraw')[]` - no heuristics.
301
332
  - Every wire type has a zod schema + JSON codec - state crosses process
@@ -326,12 +357,6 @@ defaults to `https://api-staging.zkp2p.xyz`. Indexer, curator, and Relay
326
357
  options remain overridable via `createCashClient` options. Base USDC on Base
327
358
  is the default source and the only destination asset for cashout orders.
328
359
 
329
- ## Install
330
-
331
- ```sh
332
- npm install @zkp2p/cash viem
333
- ```
334
-
335
360
  ## Examples
336
361
 
337
362
  Runnable first-party examples in [`examples/`](examples):
@@ -346,6 +371,13 @@ dependency currently ships from private source. Onchain custody is enforced by
346
371
  the protocol: only the contract holds funds, and only the maker can withdraw
347
372
  an unmatched deposit.
348
373
 
374
+ ## Contributing
375
+
376
+ [CLAUDE.md](https://github.com/zkp2p/peer-cash/blob/main/CLAUDE.md) is the
377
+ contributor guide: ground rules, repo layout, the `bun run ci` gate, and the
378
+ release/publish process. `AGENTS.md` is the shipped manual for agents using
379
+ the package, not the contributor entry point.
380
+
349
381
  ## License
350
382
 
351
383
  MIT
@@ -194,7 +194,7 @@ var errors = {
194
194
  code: "PAYEE_VERIFICATION_REQUIRED",
195
195
  message: `${platform} requires a verified maker identity attestation to register a payee; a bare handle is not accepted.`,
196
196
  retryable: false,
197
- remediation: `Register this ${platform} payee through the ZKP2P app / extension (which produces the signed identity attestation) before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`
197
+ remediation: `Register this ${platform} payee through Peer web and its TEE browser extension, which produces the signed identity attestation, before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`
198
198
  },
199
199
  { cause }
200
200
  ),
@@ -411,7 +411,7 @@ var errors = {
411
411
  code: "ACCESS_POLICY_CONFIGURATION_FAILED",
412
412
  message: `Cash-out deposit ${depositId} was created, but its access policy could not be confirmed.`,
413
413
  retryable: false,
414
- remediation: `Do not create another cash-out. Submit prepareAccessPolicy(recovery.depositId) with the same depositor wallet, then confirm that transaction.`,
414
+ remediation: `Do not create another cash-out. If recovery.transactionHash is present, inspect that policy transaction first. Otherwise, or if it is confirmed reverted, submit and confirm prepareAccessPolicy(recovery.depositId) with the same depositor wallet.`,
415
415
  recovery: {
416
416
  kind: "configure-cashout-access-policy",
417
417
  depositId,
@@ -306,12 +306,16 @@ interface CashPlatformCapability {
306
306
  payeeHint: string;
307
307
  /**
308
308
  * When true, registering a payee for this platform requires a signed maker
309
- * identity attestation the SDK cannot produce - register the payee via the
310
- * ZKP2P app/extension first. A bare-handle `cashout()` throws
309
+ * identity attestation the SDK cannot produce. First-party Peer web obtains
310
+ * it through the Peer TEE browser extension. Existing registrations can be
311
+ * reused with bare payee data; a new bare handle throws
311
312
  * `PAYEE_VERIFICATION_REQUIRED`.
312
313
  */
313
314
  requiresIdentityAttestation: boolean;
314
- /** @deprecated Always false. Restricted-platform policies are attached sequentially. */
315
+ /**
316
+ * @deprecated Always false. This does not report the sequential restricted-
317
+ * platform policy; prepared hosts must inspect `PrepareResult.accessPolicyRequired`.
318
+ */
315
319
  requiresAtomicAccessPolicy: boolean;
316
320
  }
317
321
  interface CashCapabilities {
@@ -584,7 +588,7 @@ interface CashoutResult {
584
588
  onchainDepositId: bigint;
585
589
  /** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
586
590
  order: CashOrder;
587
- /** Confirmed access-policy transaction for Venmo, Cash App, or PayPal cash-outs. */
591
+ /** Confirmed access-policy transaction when any payout leg is Venmo, Cash App, or PayPal. */
588
592
  accessPolicyTxHash?: Hash;
589
593
  /** Present when `cashout()` first routed a source asset through Relay. */
590
594
  source?: {
@@ -612,7 +616,7 @@ interface PrepareResult {
612
616
  register: {
613
617
  hashedOnchainIds: string[];
614
618
  };
615
- /** Whether the host must submit `prepareAccessPolicy(depositId)` after `createDeposit`. */
619
+ /** Whether the host must submit and confirm the policy after `createDeposit` confirms. */
616
620
  accessPolicyRequired: boolean;
617
621
  }
618
622
  /** Confirmed createDeposit receipt from an externally executed prepare() plan. */
@@ -306,12 +306,16 @@ interface CashPlatformCapability {
306
306
  payeeHint: string;
307
307
  /**
308
308
  * When true, registering a payee for this platform requires a signed maker
309
- * identity attestation the SDK cannot produce - register the payee via the
310
- * ZKP2P app/extension first. A bare-handle `cashout()` throws
309
+ * identity attestation the SDK cannot produce. First-party Peer web obtains
310
+ * it through the Peer TEE browser extension. Existing registrations can be
311
+ * reused with bare payee data; a new bare handle throws
311
312
  * `PAYEE_VERIFICATION_REQUIRED`.
312
313
  */
313
314
  requiresIdentityAttestation: boolean;
314
- /** @deprecated Always false. Restricted-platform policies are attached sequentially. */
315
+ /**
316
+ * @deprecated Always false. This does not report the sequential restricted-
317
+ * platform policy; prepared hosts must inspect `PrepareResult.accessPolicyRequired`.
318
+ */
315
319
  requiresAtomicAccessPolicy: boolean;
316
320
  }
317
321
  interface CashCapabilities {
@@ -584,7 +588,7 @@ interface CashoutResult {
584
588
  onchainDepositId: bigint;
585
589
  /** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
586
590
  order: CashOrder;
587
- /** Confirmed access-policy transaction for Venmo, Cash App, or PayPal cash-outs. */
591
+ /** Confirmed access-policy transaction when any payout leg is Venmo, Cash App, or PayPal. */
588
592
  accessPolicyTxHash?: Hash;
589
593
  /** Present when `cashout()` first routed a source asset through Relay. */
590
594
  source?: {
@@ -612,7 +616,7 @@ interface PrepareResult {
612
616
  register: {
613
617
  hashedOnchainIds: string[];
614
618
  };
615
- /** Whether the host must submit `prepareAccessPolicy(depositId)` after `createDeposit`. */
619
+ /** Whether the host must submit and confirm the policy after `createDeposit` confirms. */
616
620
  accessPolicyRequired: boolean;
617
621
  }
618
622
  /** Confirmed createDeposit receipt from an externally executed prepare() plan. */
package/dist/index.cjs CHANGED
@@ -676,7 +676,7 @@ var errors = {
676
676
  code: "PAYEE_VERIFICATION_REQUIRED",
677
677
  message: `${platform} requires a verified maker identity attestation to register a payee; a bare handle is not accepted.`,
678
678
  retryable: false,
679
- remediation: `Register this ${platform} payee through the ZKP2P app / extension (which produces the signed identity attestation) before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`
679
+ remediation: `Register this ${platform} payee through Peer web and its TEE browser extension, which produces the signed identity attestation, before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`
680
680
  },
681
681
  { cause }
682
682
  ),
@@ -893,7 +893,7 @@ var errors = {
893
893
  code: "ACCESS_POLICY_CONFIGURATION_FAILED",
894
894
  message: `Cash-out deposit ${depositId} was created, but its access policy could not be confirmed.`,
895
895
  retryable: false,
896
- remediation: `Do not create another cash-out. Submit prepareAccessPolicy(recovery.depositId) with the same depositor wallet, then confirm that transaction.`,
896
+ remediation: `Do not create another cash-out. If recovery.transactionHash is present, inspect that policy transaction first. Otherwise, or if it is confirmed reverted, submit and confirm prepareAccessPolicy(recovery.depositId) with the same depositor wallet.`,
897
897
  recovery: {
898
898
  kind: "configure-cashout-access-policy",
899
899
  depositId,
@@ -2272,8 +2272,6 @@ function createCashClient(options) {
2272
2272
  const payoutInput = validatePayout(input);
2273
2273
  const client = await signingClient("cashout", opts);
2274
2274
  const owner = opts.signer.account.address;
2275
- let sourceResult;
2276
- let cashoutAmount = input.amount;
2277
2275
  if (input.source) {
2278
2276
  const sourceSigner = opts.sourceSigner ?? (input.source.chainId === BASE_CHAIN_ID ? opts.signer : void 0);
2279
2277
  if (!sourceSigner?.account) throw errors.signerRequired("source cashout");
@@ -2294,7 +2292,7 @@ function createCashClient(options) {
2294
2292
  if (relayQuote.outputAmount < MIN_CASHOUT_AMOUNT) {
2295
2293
  throw errors.amountBelowMinimum(relayQuote.outputAmount, MIN_CASHOUT_AMOUNT);
2296
2294
  }
2297
- cashoutAmount = relayQuote.outputAmount;
2295
+ const cashoutAmount = relayQuote.outputAmount;
2298
2296
  const depositInput2 = validateDepositInput(cashoutAmount, input, payoutInput);
2299
2297
  const params2 = await buildDepositParams(client, depositInput2);
2300
2298
  const escrow2 = client.escrowV2Address ?? client.escrowAddress;
@@ -2311,7 +2309,6 @@ function createCashClient(options) {
2311
2309
  txHashes: executed.txHashes,
2312
2310
  ...executed.transactions ? { transactions: executed.transactions } : {}
2313
2311
  };
2314
- sourceResult = routedSource;
2315
2312
  try {
2316
2313
  await waitForBaseSignerAfterRelay(
2317
2314
  client,
@@ -2445,8 +2442,7 @@ function createCashClient(options) {
2445
2442
  escrowAddress: resolved.escrowAddress,
2446
2443
  onchainDepositId: resolved.onchainDepositId,
2447
2444
  order,
2448
- ...accessPolicyTxHash ? { accessPolicyTxHash } : {},
2449
- ...sourceResult ? { source: sourceResult } : {}
2445
+ ...accessPolicyTxHash ? { accessPolicyTxHash } : {}
2450
2446
  };
2451
2447
  },
2452
2448
  async prepare(input) {