@zkp2p/cash 0.4.3 → 0.4.5

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
@@ -32,7 +32,13 @@ Every transaction (including approves) carries ERC-8021 attribution:
32
32
  code. The namespaced referral marker gives its Peer Privy wallet the direct
33
33
  deposit-level integration share instead of applying maker L1/L2.
34
34
 
35
- **Two platform caveats, both surfaced in `capabilities()`:**
35
+ **Platform caveats, all surfaced in `capabilities()`:**
36
+
37
+ - **Venmo, Cash App, and PayPal** carry
38
+ `requiresAtomicAccessPolicy: true`. Generic `cashout()` and `prepare()` reject
39
+ them with `ATOMIC_ACCESS_POLICY_REQUIRED` before any side effect. Use Peer
40
+ web's Curator-backed atomic flow or a host with the same atomic guarantee;
41
+ never create the deposit and attach the group policy sequentially.
36
42
 
37
43
  - **Wise and PayPal** carry `requiresIdentityAttestation: true`. A new curator
38
44
  registration needs a signed maker identity attestation this SDK cannot mint
@@ -93,7 +99,7 @@ const multiPlatform = await cash.cashout(
93
99
  {
94
100
  amount: usdc(500),
95
101
  receive: [
96
- { platform: 'venmo', currency: 'USD', payee: '@handle' },
102
+ { platform: 'chime', currency: 'USD', payee: '$handle' },
97
103
  { platform: 'revolut', currencies: ['EUR', 'GBP'], payee: { offchainId: 'revtag' } },
98
104
  ],
99
105
  },
@@ -208,6 +214,8 @@ Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
208
214
  | `INVALID_PAYOUT_PLATFORMS` | no | Pass one leg or an array of legs, using each platform at most once |
209
215
  | `PAYEE_VERIFICATION_REQUIRED` | no | Register a new Wise/PayPal payee through Peer; an existing registered handle can be reused |
210
216
  | `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
217
+ | `ATOMIC_ACCESS_POLICY_REQUIRED` | no | Use Peer web or an equivalent atomic host for Venmo, Cash App, or PayPal; nothing moved |
218
+ | `ACCESS_POLICY_CONFIGURATION_FAILED` | no | Recover an existing `0.4.4` deposit from its policy recovery data; do not create it again |
211
219
  | `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
212
220
  | `SOURCE_RECIPIENT_MISMATCH` | no | Route Base USDC to the cashout depositor |
213
221
  | `SOURCE_CAPABILITIES_FAILED` | yes | Retry discovery or fall back to Base USDC |
package/README.md CHANGED
@@ -46,7 +46,7 @@ const multiCurrencyStats = fillStats['revolut:EUR+GBP+USD'];
46
46
  const { depositId } = await cash.cashout(
47
47
  {
48
48
  amount: usdc(1000),
49
- receive: { platform: 'venmo', currency: 'USD', payee: '@you' },
49
+ receive: { platform: 'chime', currency: 'USD', payee: '$you' },
50
50
  },
51
51
  { signer }, // any viem WalletClient on Base
52
52
  );
@@ -71,7 +71,7 @@ const widestReach = await cash.cashout(
71
71
  {
72
72
  amount: usdc(1000),
73
73
  receive: [
74
- { platform: 'venmo', currency: 'USD', payee: '@you' },
74
+ { platform: 'chime', currency: 'USD', payee: '$you' },
75
75
  { platform: 'revolut', currencies: ['EUR', 'GBP'], payee: { offchainId: 'revtag' } },
76
76
  ],
77
77
  },
@@ -120,7 +120,7 @@ const { depositId, source } = await cash.cashout(
120
120
  currency: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
121
121
  tradeType: 'EXACT_INPUT',
122
122
  },
123
- receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
123
+ receive: { platform: 'chime', currency: 'USD', payee: { offchainId: '$you' } },
124
124
  },
125
125
  { signer, sourceSigner },
126
126
  );
@@ -141,8 +141,9 @@ console.log(source?.transactions?.origin, source?.transactions?.destination);
141
141
  | `quoteSource(input)` / `executeSourceQuote(quote, { signer })` | Relay SDK EVM source routing into Base USDC before cashout |
142
142
  | `relayStatus(requestId)` | Relay request status from the Relay SDK request path |
143
143
  | `estimate({ amount, currency }, { includeEta? })` | Base USDC oracle estimate; optionally skip the historical ETA for progressive rendering |
144
- | `cashout(input, { signer })` | Registers your payee, creates the protocol-held order, returns the `depositId` |
145
- | `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, then resolve the confirmed createDeposit receipt into a resumable result |
144
+ | `cashout(input, { signer })` | Registers the payee and creates an unrestricted-rail order; protected rails fail before any side effect |
145
+ | `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare unrestricted-rail external signing, then resolve the confirmed createDeposit receipt into resumable state |
146
+ | `prepareAccessPolicy(depositId)` | Recover a restricted-rail deposit created by the unsafe `0.4.4` sequential flow; never use it to create a new order |
146
147
  | `order(depositId)` / `orders(owner)` | Resume any order from its id alone; list all orders for a wallet |
147
148
  | `watch(depositId)` | Async iterator: yields on every state change until terminal, abort, or timeout |
148
149
  | `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) |
@@ -164,29 +165,21 @@ Peer Cash transaction, including approves, carries ERC-8021 attribution:
164
165
  `peer-cash` first, optional `peer-ref-XXXXXX` from `referralCode` next, and your
165
166
  analytics-only `referrer` code(s) after it.
166
167
 
167
- ```ts
168
- const prepared = await cash.prepare({
169
- amount: 5_000_000n,
170
- receive: {
171
- platform: 'venmo',
172
- currency: 'USD',
173
- payee: { offchainId: '@maker' },
174
- },
175
- });
176
-
177
- let createDepositReceipt;
178
- for (const [index, transaction] of prepared.txs.entries()) {
179
- const receipt = await externalRuntime.sendAndWait(transaction);
180
- if (prepared.steps[index]?.kind === 'createDeposit') {
181
- createDepositReceipt = receipt;
182
- }
183
- }
184
- if (!createDepositReceipt) throw new Error('createDeposit receipt missing');
185
-
186
- const result = cash.finalizePreparedCashout(createDepositReceipt);
187
- await persistDepositId(result.depositId);
188
- const liveOrder = await cash.order(result.depositId);
189
- ```
168
+ Venmo, Cash App, and PayPal must attach Plus, Pro, Peer Makers, and Peer Pay in
169
+ the same atomic operation that creates the deposit. A create-then-configure
170
+ sequence leaves a public interval in which an unapproved taker can signal an
171
+ intent. The generic `cashout()` and `prepare()` paths therefore throw
172
+ `ATOMIC_ACCESS_POLICY_REQUIRED` before wallet access, Relay quoting, payee
173
+ registration, allowance, or deposit submission. Peer web supports these rails
174
+ through its atomic Curator-backed flow. Other hosts must provide an equivalent
175
+ atomic batch rather than composing the two public SDK calls sequentially.
176
+ `capabilities()` marks these platforms with
177
+ `requiresAtomicAccessPolicy: true`.
178
+
179
+ `prepareAccessPolicy(depositId)` remains available only to recover a restricted
180
+ deposit already created by `0.4.4`. Configure that existing deposit immediately;
181
+ do not call `cashout()` again and do not use this recovery helper as a new-order
182
+ flow.
190
183
 
191
184
  `capabilities()` presents Zelle as one platform. A cashout with
192
185
  `receive.platform: 'zelle'` attaches only the generic Zelle payment method to
@@ -2,6 +2,27 @@
2
2
  var BASE_CHAIN_ID = 8453;
3
3
  var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
4
4
  var CASH_INTENT_GUARDIAN_ADDRESS = "0x83671606454fA72ba1e2831E18C5090D25629414";
5
+ var CASH_RESTRICTED_PLATFORMS = /* @__PURE__ */ new Set(["venmo", "cashapp", "paypal"]);
6
+ var CASH_ACCESS_GROUP_IDS = {
7
+ production: [
8
+ "0xb8747401b308d4891385620071b5916e9c61284f25c4611541c529703de5babf",
9
+ "0xf030f72e772f954059ca28f94974088aaf6ba37bb1f264df48843a3d0c221dc3",
10
+ "0xdf1c64c54745aa1ce00642a5874f97e3183bf5e993c1f559d0a37a4df0b803c7",
11
+ "0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"
12
+ ],
13
+ preproduction: [
14
+ "0xb8747401b308d4891385620071b5916e9c61284f25c4611541c529703de5babf",
15
+ "0xf030f72e772f954059ca28f94974088aaf6ba37bb1f264df48843a3d0c221dc3",
16
+ "0xdf1c64c54745aa1ce00642a5874f97e3183bf5e993c1f559d0a37a4df0b803c7",
17
+ "0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"
18
+ ],
19
+ staging: [
20
+ "0xf6133c227eab8ae7da1ee143945bf7f31204394f3ba801dc9691f8af6ca8efa5",
21
+ "0xa6beb459bc621e7b050e431736c1c3298da26356d7095719185e859d68f70d9e",
22
+ "0x9cded1332f25c3ee0a9a822a4c827d3fbd081a8d7b2ba39c49917ec1983b8d6c",
23
+ "0xc82c20c00033046a2f017b65532d7148a337282f17c73296663a530e49ba00f7"
24
+ ]
25
+ };
5
26
  var USDC_DECIMALS = 6;
6
27
  var MARKET_SPREAD_BPS = 0;
7
28
  var ORACLE_MIN_CONVERSION_RATE_SENTINEL = 1n;
@@ -178,6 +199,12 @@ var errors = {
178
199
  },
179
200
  { cause }
180
201
  ),
202
+ atomicAccessPolicyRequired: (platforms) => new CashError({
203
+ code: "ATOMIC_ACCESS_POLICY_REQUIRED",
204
+ message: `${platforms.join(", ")} cash-outs require atomic deposit creation and access-policy configuration.`,
205
+ retryable: false,
206
+ remediation: `Use Peer web or a host that atomically batches guard-before, createDeposit, guard-after, and configureDeposit. Nothing was submitted by this call.`
207
+ }),
181
208
  sourceRouteUnsupportedInPrepare: () => new CashError({
182
209
  code: "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
183
210
  message: `prepare() cannot execute a Relay source route before creating the Base USDC cash-out.`,
@@ -379,6 +406,21 @@ var errors = {
379
406
  },
380
407
  { cause }
381
408
  ),
409
+ accessPolicyConfigurationFailed: (depositId, groupIds, cause, transactionHash) => new CashError(
410
+ {
411
+ code: "ACCESS_POLICY_CONFIGURATION_FAILED",
412
+ message: `Cash-out deposit ${depositId} was created, but its required access policy was not confirmed.`,
413
+ retryable: false,
414
+ remediation: `Do not call cashout() again. Inspect the existing deposit and any access-policy transaction, then configure that deposit with recovery.groupIds through @zkp2p/sdk accessPolicy.`,
415
+ recovery: {
416
+ kind: "configure-cashout-access-policy",
417
+ depositId,
418
+ groupIds: [...groupIds],
419
+ ...transactionHash ? { transactionHash } : {}
420
+ }
421
+ },
422
+ { cause }
423
+ ),
382
424
  escrowPaused: () => new CashError({
383
425
  code: "ESCROW_PAUSED",
384
426
  message: `The escrow contract is paused; deposits are temporarily disabled.`,
@@ -446,4 +488,4 @@ function isUserRejectedError(value) {
446
488
  return text.some(hasUserRejectionText);
447
489
  }
448
490
 
449
- export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_INTENT_GUARDIAN_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 };
491
+ export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ACCESS_GROUP_IDS, CASH_INTENT_GUARDIAN_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RESTRICTED_PLATFORMS, CASH_RETAIN_ON_EMPTY, CashError, MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, USDC_DECIMALS, errors, isCashError, isUserRejectedError, mapChainError };
@@ -311,6 +311,11 @@ interface CashPlatformCapability {
311
311
  * `PAYEE_VERIFICATION_REQUIRED`.
312
312
  */
313
313
  requiresIdentityAttestation: boolean;
314
+ /**
315
+ * When true, the platform needs deposit creation and access-policy setup in
316
+ * one atomic host operation. Generic `cashout()` and `prepare()` fail closed.
317
+ */
318
+ requiresAtomicAccessPolicy: boolean;
314
319
  }
315
320
  interface CashCapabilities {
316
321
  chainId: number;
@@ -582,6 +587,8 @@ interface CashoutResult {
582
587
  onchainDepositId: bigint;
583
588
  /** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
584
589
  order: CashOrder;
590
+ /** @deprecated Only present in recovery data produced by the unsafe 0.4.4 sequential flow. */
591
+ accessPolicyTxHash?: Hash;
585
592
  /** Present when `cashout()` first routed a source asset through Relay. */
586
593
  source?: {
587
594
  /** Conservative Base USDC amount deposited (Relay's guaranteed minimum output). */
@@ -608,6 +615,8 @@ interface PrepareResult {
608
615
  register: {
609
616
  hashedOnchainIds: string[];
610
617
  };
618
+ /** @deprecated Always false for new plans; restricted rails require an atomic host flow. */
619
+ accessPolicyRequired: boolean;
611
620
  }
612
621
  /** Confirmed createDeposit receipt from an externally executed prepare() plan. */
613
622
  interface PreparedCashoutReceipt {
@@ -670,6 +679,8 @@ interface CashClient {
670
679
  prepare(input: CashoutInput): Promise<PrepareResult>;
671
680
  /** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
672
681
  finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
682
+ /** Recover an existing 0.4.4 restricted-rail deposit by preparing its four-group policy. */
683
+ prepareAccessPolicy(depositId: string): PreparedTransaction;
673
684
  /** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
674
685
  order(depositId: string): Promise<CashOrder>;
675
686
  /**
@@ -311,6 +311,11 @@ interface CashPlatformCapability {
311
311
  * `PAYEE_VERIFICATION_REQUIRED`.
312
312
  */
313
313
  requiresIdentityAttestation: boolean;
314
+ /**
315
+ * When true, the platform needs deposit creation and access-policy setup in
316
+ * one atomic host operation. Generic `cashout()` and `prepare()` fail closed.
317
+ */
318
+ requiresAtomicAccessPolicy: boolean;
314
319
  }
315
320
  interface CashCapabilities {
316
321
  chainId: number;
@@ -582,6 +587,8 @@ interface CashoutResult {
582
587
  onchainDepositId: bigint;
583
588
  /** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
584
589
  order: CashOrder;
590
+ /** @deprecated Only present in recovery data produced by the unsafe 0.4.4 sequential flow. */
591
+ accessPolicyTxHash?: Hash;
585
592
  /** Present when `cashout()` first routed a source asset through Relay. */
586
593
  source?: {
587
594
  /** Conservative Base USDC amount deposited (Relay's guaranteed minimum output). */
@@ -608,6 +615,8 @@ interface PrepareResult {
608
615
  register: {
609
616
  hashedOnchainIds: string[];
610
617
  };
618
+ /** @deprecated Always false for new plans; restricted rails require an atomic host flow. */
619
+ accessPolicyRequired: boolean;
611
620
  }
612
621
  /** Confirmed createDeposit receipt from an externally executed prepare() plan. */
613
622
  interface PreparedCashoutReceipt {
@@ -670,6 +679,8 @@ interface CashClient {
670
679
  prepare(input: CashoutInput): Promise<PrepareResult>;
671
680
  /** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
672
681
  finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
682
+ /** Recover an existing 0.4.4 restricted-rail deposit by preparing its four-group policy. */
683
+ prepareAccessPolicy(depositId: string): PreparedTransaction;
673
684
  /** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
674
685
  order(depositId: string): Promise<CashOrder>;
675
686
  /**
package/dist/index.cjs CHANGED
@@ -13,6 +13,27 @@ var zod = require('zod');
13
13
  var BASE_CHAIN_ID = 8453;
14
14
  var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
15
15
  var CASH_INTENT_GUARDIAN_ADDRESS = "0x83671606454fA72ba1e2831E18C5090D25629414";
16
+ var CASH_RESTRICTED_PLATFORMS = /* @__PURE__ */ new Set(["venmo", "cashapp", "paypal"]);
17
+ var CASH_ACCESS_GROUP_IDS = {
18
+ production: [
19
+ "0xb8747401b308d4891385620071b5916e9c61284f25c4611541c529703de5babf",
20
+ "0xf030f72e772f954059ca28f94974088aaf6ba37bb1f264df48843a3d0c221dc3",
21
+ "0xdf1c64c54745aa1ce00642a5874f97e3183bf5e993c1f559d0a37a4df0b803c7",
22
+ "0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"
23
+ ],
24
+ preproduction: [
25
+ "0xb8747401b308d4891385620071b5916e9c61284f25c4611541c529703de5babf",
26
+ "0xf030f72e772f954059ca28f94974088aaf6ba37bb1f264df48843a3d0c221dc3",
27
+ "0xdf1c64c54745aa1ce00642a5874f97e3183bf5e993c1f559d0a37a4df0b803c7",
28
+ "0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"
29
+ ],
30
+ staging: [
31
+ "0xf6133c227eab8ae7da1ee143945bf7f31204394f3ba801dc9691f8af6ca8efa5",
32
+ "0xa6beb459bc621e7b050e431736c1c3298da26356d7095719185e859d68f70d9e",
33
+ "0x9cded1332f25c3ee0a9a822a4c827d3fbd081a8d7b2ba39c49917ec1983b8d6c",
34
+ "0xc82c20c00033046a2f017b65532d7148a337282f17c73296663a530e49ba00f7"
35
+ ]
36
+ };
16
37
  var USDC_DECIMALS = 6;
17
38
  var MARKET_SPREAD_BPS = 0;
18
39
  var ORACLE_MIN_CONVERSION_RATE_SENTINEL = 1n;
@@ -478,7 +499,8 @@ function buildCapabilities(environment) {
478
499
  platform,
479
500
  currencies: [...new Set(currencies2)].sort(),
480
501
  payeeHint: PAYEE_HINTS[platform] ?? "Your payment handle for this platform",
481
- requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform)
502
+ requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform),
503
+ requiresAtomicAccessPolicy: CASH_RESTRICTED_PLATFORMS.has(platform)
482
504
  };
483
505
  }).filter((p) => p.currencies.length > 0).sort((a, b) => a.platform.localeCompare(b.platform));
484
506
  const currencies = [...new Set(platforms.flatMap((p) => p.currencies))].sort();
@@ -660,6 +682,12 @@ var errors = {
660
682
  },
661
683
  { cause }
662
684
  ),
685
+ atomicAccessPolicyRequired: (platforms) => new CashError({
686
+ code: "ATOMIC_ACCESS_POLICY_REQUIRED",
687
+ message: `${platforms.join(", ")} cash-outs require atomic deposit creation and access-policy configuration.`,
688
+ retryable: false,
689
+ remediation: `Use Peer web or a host that atomically batches guard-before, createDeposit, guard-after, and configureDeposit. Nothing was submitted by this call.`
690
+ }),
663
691
  sourceRouteUnsupportedInPrepare: () => new CashError({
664
692
  code: "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
665
693
  message: `prepare() cannot execute a Relay source route before creating the Base USDC cash-out.`,
@@ -861,6 +889,21 @@ var errors = {
861
889
  },
862
890
  { cause }
863
891
  ),
892
+ accessPolicyConfigurationFailed: (depositId, groupIds, cause, transactionHash) => new CashError(
893
+ {
894
+ code: "ACCESS_POLICY_CONFIGURATION_FAILED",
895
+ message: `Cash-out deposit ${depositId} was created, but its required access policy was not confirmed.`,
896
+ retryable: false,
897
+ remediation: `Do not call cashout() again. Inspect the existing deposit and any access-policy transaction, then configure that deposit with recovery.groupIds through @zkp2p/sdk accessPolicy.`,
898
+ recovery: {
899
+ kind: "configure-cashout-access-policy",
900
+ depositId,
901
+ groupIds: [...groupIds],
902
+ ...transactionHash ? { transactionHash } : {}
903
+ }
904
+ },
905
+ { cause }
906
+ ),
864
907
  escrowPaused: () => new CashError({
865
908
  code: "ESCROW_PAUSED",
866
909
  message: `The escrow contract is paused; deposits are temporarily disabled.`,
@@ -1876,6 +1919,12 @@ function createCashClient(options) {
1876
1919
  ...range ? { intentAmountRange: range } : {}
1877
1920
  };
1878
1921
  }
1922
+ function assertGenericCashoutSupported(payoutInput) {
1923
+ const restrictedPlatforms = payoutInput.payouts.map((payout) => payout.processorName.toLowerCase()).filter((platform) => CASH_RESTRICTED_PLATFORMS.has(platform));
1924
+ if (restrictedPlatforms.length > 0) {
1925
+ throw errors.atomicAccessPolicyRequired(restrictedPlatforms);
1926
+ }
1927
+ }
1879
1928
  function isCashPayoutSet(payouts) {
1880
1929
  return payouts.length > 0 && payouts.every((payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0);
1881
1930
  }
@@ -2115,6 +2164,28 @@ function createCashClient(options) {
2115
2164
  if (lastNonceError) throw lastNonceError;
2116
2165
  throw new Error(`Signer provider did not observe Relay nonce ${afterRelay - 1}`);
2117
2166
  }
2167
+ function prepareCashoutAccess(depositId, client = readClient) {
2168
+ const { compositeId, escrowAddress, onchainDepositId } = parseDepositId(depositId);
2169
+ const groupIds = CASH_ACCESS_GROUP_IDS[environment];
2170
+ try {
2171
+ const prepared = client.accessPolicy.prepareConfigureDeposit({
2172
+ escrow: escrowAddress,
2173
+ depositId: onchainDepositId,
2174
+ enabled: true,
2175
+ groupIds,
2176
+ takers: [],
2177
+ txOverrides: attribution
2178
+ });
2179
+ return {
2180
+ to: prepared.to,
2181
+ data: prepared.data,
2182
+ value: prepared.value,
2183
+ chainId: prepared.chainId
2184
+ };
2185
+ } catch (err) {
2186
+ throw errors.accessPolicyConfigurationFailed(compositeId, groupIds, err);
2187
+ }
2188
+ }
2118
2189
  return {
2119
2190
  capabilities,
2120
2191
  async sourceCapabilities() {
@@ -2152,6 +2223,7 @@ function createCashClient(options) {
2152
2223
  },
2153
2224
  async cashout(input, opts) {
2154
2225
  const payoutInput = validatePayout(input);
2226
+ assertGenericCashoutSupported(payoutInput);
2155
2227
  const client = await signingClient("cashout", opts);
2156
2228
  const owner = opts.signer.account.address;
2157
2229
  let sourceResult;
@@ -2319,6 +2391,7 @@ function createCashClient(options) {
2319
2391
  async prepare(input) {
2320
2392
  if (input.source) throw errors.sourceRouteUnsupportedInPrepare();
2321
2393
  const depositInput = validateDepositInput(input.amount, input);
2394
+ assertGenericCashoutSupported(depositInput);
2322
2395
  const params = await buildDepositParams(readClient, depositInput);
2323
2396
  const { prepared } = await readClient.prepareCreateDeposit({
2324
2397
  ...params,
@@ -2350,7 +2423,8 @@ function createCashClient(options) {
2350
2423
  description: "Create the protocol-held cash-out order."
2351
2424
  }
2352
2425
  ],
2353
- register: { hashedOnchainIds }
2426
+ register: { hashedOnchainIds },
2427
+ accessPolicyRequired: false
2354
2428
  };
2355
2429
  },
2356
2430
  finalizePreparedCashout(receipt) {
@@ -2379,6 +2453,7 @@ function createCashClient(options) {
2379
2453
  })
2380
2454
  };
2381
2455
  },
2456
+ prepareAccessPolicy: prepareCashoutAccess,
2382
2457
  async order(depositId) {
2383
2458
  return fetchOrder(depositId);
2384
2459
  },
@@ -2833,6 +2908,7 @@ var cashoutResultJsonSchema = zod.z.object({
2833
2908
  escrowAddress: zod.z.string(),
2834
2909
  onchainDepositId: nonNegativeBigintString,
2835
2910
  order: cashOrderJsonSchema,
2911
+ accessPolicyTxHash: zod.z.string().optional(),
2836
2912
  source: zod.z.object({
2837
2913
  amount: nonNegativeBigintString,
2838
2914
  requestId: zod.z.string().optional(),
@@ -2843,7 +2919,8 @@ var cashoutResultJsonSchema = zod.z.object({
2843
2919
  var prepareResultJsonSchema = zod.z.object({
2844
2920
  txs: zod.z.array(preparedTransactionJsonSchema),
2845
2921
  steps: zod.z.array(cashPreparedStepJsonSchema),
2846
- register: zod.z.object({ hashedOnchainIds: zod.z.array(zod.z.string()) })
2922
+ register: zod.z.object({ hashedOnchainIds: zod.z.array(zod.z.string()) }),
2923
+ accessPolicyRequired: zod.z.boolean()
2847
2924
  });
2848
2925
  var withdrawResultJsonSchema = zod.z.object({
2849
2926
  depositId: zod.z.string(),
@@ -2874,7 +2951,8 @@ var cashCapabilitiesJsonSchema = zod.z.object({
2874
2951
  platform: zod.z.string(),
2875
2952
  currencies: zod.z.array(zod.z.string()),
2876
2953
  payeeHint: zod.z.string(),
2877
- requiresIdentityAttestation: zod.z.boolean()
2954
+ requiresIdentityAttestation: zod.z.boolean(),
2955
+ requiresAtomicAccessPolicy: zod.z.boolean().optional()
2878
2956
  })
2879
2957
  ),
2880
2958
  currencies: zod.z.array(zod.z.string()),
@@ -2910,6 +2988,7 @@ var CASH_ERROR_CODES = defineCashErrorCodes([
2910
2988
  "ORDER_NOT_FOUND",
2911
2989
  "PAYEE_REGISTRATION_FAILED",
2912
2990
  "PAYEE_VERIFICATION_REQUIRED",
2991
+ "ATOMIC_ACCESS_POLICY_REQUIRED",
2913
2992
  "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
2914
2993
  "SOURCE_RECIPIENT_MISMATCH",
2915
2994
  "SOURCE_CAPABILITIES_FAILED",
@@ -2921,6 +3000,7 @@ var CASH_ERROR_CODES = defineCashErrorCodes([
2921
3000
  "SOURCE_CASHOUT_SUBMISSION_UNKNOWN",
2922
3001
  "SOURCE_CASHOUT_STATUS_UNKNOWN",
2923
3002
  "DEPOSIT_RESOLUTION_FAILED",
3003
+ "ACCESS_POLICY_CONFIGURATION_FAILED",
2924
3004
  "ALLOWANCE_NOT_VISIBLE",
2925
3005
  "SIGNER_REQUIRED",
2926
3006
  "SIGNER_CHAIN_MISMATCH",
@@ -2966,6 +3046,12 @@ var cashErrorRecoveryJsonSchema = zod.z.discriminatedUnion("kind", [
2966
3046
  kind: zod.z.literal("inspect-base-transaction"),
2967
3047
  transactionHash: zod.z.string(),
2968
3048
  operation: zod.z.string()
3049
+ }).strict(),
3050
+ zod.z.object({
3051
+ kind: zod.z.literal("configure-cashout-access-policy"),
3052
+ depositId: zod.z.string(),
3053
+ groupIds: zod.z.array(zod.z.string()),
3054
+ transactionHash: zod.z.string().optional()
2969
3055
  }).strict()
2970
3056
  ]);
2971
3057
  var cashErrorJsonSchema = zod.z.object({
@@ -3223,6 +3309,7 @@ function cashoutResultToJson(result) {
3223
3309
  escrowAddress: result.escrowAddress,
3224
3310
  onchainDepositId: result.onchainDepositId.toString(),
3225
3311
  order: orderToJson(result.order),
3312
+ accessPolicyTxHash: result.accessPolicyTxHash,
3226
3313
  source: result.source ? {
3227
3314
  ...result.source,
3228
3315
  amount: result.source.amount.toString()
@@ -3237,6 +3324,7 @@ function cashoutResultFromJson(json) {
3237
3324
  escrowAddress: parsed.escrowAddress,
3238
3325
  onchainDepositId: BigInt(parsed.onchainDepositId),
3239
3326
  order: orderFromJson(parsed.order),
3327
+ accessPolicyTxHash: parsed.accessPolicyTxHash,
3240
3328
  source: parsed.source ? {
3241
3329
  ...parsed.source,
3242
3330
  amount: BigInt(parsed.source.amount)
@@ -3247,7 +3335,8 @@ function prepareResultToJson(result) {
3247
3335
  return {
3248
3336
  txs: result.txs.map(preparedTxToJson),
3249
3337
  steps: result.steps.map(preparedStepToJson),
3250
- register: result.register
3338
+ register: result.register,
3339
+ accessPolicyRequired: result.accessPolicyRequired
3251
3340
  };
3252
3341
  }
3253
3342
  function prepareResultFromJson(json) {
@@ -3255,7 +3344,8 @@ function prepareResultFromJson(json) {
3255
3344
  return {
3256
3345
  txs: parsed.txs.map(preparedTxFromJson),
3257
3346
  steps: parsed.steps.map(preparedStepFromJson),
3258
- register: parsed.register
3347
+ register: parsed.register,
3348
+ accessPolicyRequired: parsed.accessPolicyRequired
3259
3349
  };
3260
3350
  }
3261
3351
  function withdrawResultToJson(result) {
@@ -3306,7 +3396,8 @@ function capabilitiesFromJson(json) {
3306
3396
  },
3307
3397
  platforms: parsed.platforms.map((p) => ({
3308
3398
  ...p,
3309
- currencies: p.currencies
3399
+ currencies: p.currencies,
3400
+ requiresAtomicAccessPolicy: p.requiresAtomicAccessPolicy ?? CASH_RESTRICTED_PLATFORMS.has(p.platform)
3310
3401
  })),
3311
3402
  currencies: parsed.currencies,
3312
3403
  amount: {
@@ -3335,6 +3426,13 @@ function cashErrorFromJson(json) {
3335
3426
  transactionHash: parsed.recovery.transactionHash,
3336
3427
  operation: parsed.recovery.operation
3337
3428
  };
3429
+ } else if (parsed.recovery.kind === "configure-cashout-access-policy") {
3430
+ recovery = {
3431
+ kind: parsed.recovery.kind,
3432
+ depositId: parsed.recovery.depositId,
3433
+ groupIds: parsed.recovery.groupIds,
3434
+ ...parsed.recovery.transactionHash !== void 0 ? { transactionHash: parsed.recovery.transactionHash } : {}
3435
+ };
3338
3436
  } else if (parsed.recovery.kind === "inspect-base-operation-submission") {
3339
3437
  recovery = {
3340
3438
  kind: parsed.recovery.kind,