@zkp2p/cash 0.4.7 → 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/dist/index.cjs CHANGED
@@ -498,7 +498,7 @@ function buildCapabilities(environment) {
498
498
  currencies: [...new Set(currencies2)].sort(),
499
499
  payeeHint: PAYEE_HINTS[platform] ?? "Your payment handle for this platform",
500
500
  requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform),
501
- requiresAtomicAccessPolicy: CASH_RESTRICTED_PLATFORMS.has(platform)
501
+ requiresAtomicAccessPolicy: false
502
502
  };
503
503
  }).filter((p) => p.currencies.length > 0).sort((a, b) => a.platform.localeCompare(b.platform));
504
504
  const currencies = [...new Set(platforms.flatMap((p) => p.currencies))].sort();
@@ -676,15 +676,16 @@ 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
  ),
683
+ /** @deprecated Cash-outs no longer require an atomic access-policy flow. */
683
684
  atomicAccessPolicyRequired: (platforms) => new CashError({
684
685
  code: "ATOMIC_ACCESS_POLICY_REQUIRED",
685
- message: `${platforms.join(", ")} cash-outs require atomic deposit creation and access-policy configuration.`,
686
+ message: `Atomic access-policy enforcement for ${platforms.join(", ")} is deprecated.`,
686
687
  retryable: false,
687
- remediation: `Use Peer web or a host that atomically batches guard-before, createDeposit, guard-after, and configureDeposit. Nothing was submitted by this call.`
688
+ remediation: `Upgrade @zkp2p/cash; current cash-out flows do not require atomic access-policy configuration.`
688
689
  }),
689
690
  sourceRouteUnsupportedInPrepare: () => new CashError({
690
691
  code: "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
@@ -887,20 +888,28 @@ var errors = {
887
888
  },
888
889
  { cause }
889
890
  ),
890
- accessPolicyConfigurationFailed: (depositId, groupIds, cause, transactionHash) => new CashError(
891
+ accessPolicyConfigurationFailed: (depositId, groupIds, context = {}) => new CashError(
891
892
  {
892
893
  code: "ACCESS_POLICY_CONFIGURATION_FAILED",
893
- message: `Cash-out deposit ${depositId} was created, but its required access policy was not confirmed.`,
894
+ message: `Cash-out deposit ${depositId} was created, but its access policy could not be confirmed.`,
894
895
  retryable: false,
895
- 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.`,
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.`,
896
897
  recovery: {
897
898
  kind: "configure-cashout-access-policy",
898
899
  depositId,
899
900
  groupIds: [...groupIds],
900
- ...transactionHash ? { transactionHash } : {}
901
+ ...context.transactionHash ? { transactionHash: context.transactionHash } : {},
902
+ ...context.source ? {
903
+ source: {
904
+ amount: context.source.amount.toString(),
905
+ ...context.source.requestId ? { requestId: context.source.requestId } : {},
906
+ txHashes: context.source.txHashes,
907
+ ...context.source.transactions ? { transactions: context.source.transactions } : {}
908
+ }
909
+ } : {}
901
910
  }
902
911
  },
903
- { cause }
912
+ { cause: context.cause }
904
913
  ),
905
914
  escrowPaused: () => new CashError({
906
915
  code: "ESCROW_PAUSED",
@@ -1786,6 +1795,11 @@ async function submitAndConfirm(client, verb, send) {
1786
1795
  function isKnownPreBroadcastFailure(mapped) {
1787
1796
  return mapped.code === "TRANSACTION_REJECTED" || mapped.code === "INSUFFICIENT_TOKEN_BALANCE" || mapped.code === "ALLOWANCE_NOT_VISIBLE" || mapped.code === "ESCROW_PAUSED";
1788
1797
  }
1798
+ function requiresCashoutAccessPolicy(depositInput) {
1799
+ return depositInput.payouts.some(
1800
+ (payout) => CASH_RESTRICTED_PLATFORMS.has(payout.processorName.toLowerCase())
1801
+ );
1802
+ }
1789
1803
  function depositOrderOptions(deposit) {
1790
1804
  const remaining = toBigIntOrUndefined(deposit.remainingDeposits);
1791
1805
  const outstanding = toBigIntOrUndefined(deposit.outstandingIntentAmount);
@@ -1917,12 +1931,6 @@ function createCashClient(options) {
1917
1931
  ...range ? { intentAmountRange: range } : {}
1918
1932
  };
1919
1933
  }
1920
- function assertGenericCashoutSupported(payoutInput) {
1921
- const restrictedPlatforms = payoutInput.payouts.map((payout) => payout.processorName.toLowerCase()).filter((platform) => CASH_RESTRICTED_PLATFORMS.has(platform));
1922
- if (restrictedPlatforms.length > 0) {
1923
- throw errors.atomicAccessPolicyRequired(restrictedPlatforms);
1924
- }
1925
- }
1926
1934
  function isCashPayoutSet(payouts) {
1927
1935
  return payouts.length > 0 && payouts.every((payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0);
1928
1936
  }
@@ -2162,7 +2170,7 @@ function createCashClient(options) {
2162
2170
  if (lastNonceError) throw lastNonceError;
2163
2171
  throw new Error(`Signer provider did not observe Relay nonce ${afterRelay - 1}`);
2164
2172
  }
2165
- function prepareCashoutAccess(depositId, client = readClient) {
2173
+ function prepareCashoutAccess(depositId, client = readClient, source) {
2166
2174
  const { compositeId, escrowAddress, onchainDepositId } = parseDepositId(depositId);
2167
2175
  const groupIds = CASH_ACCESS_GROUP_IDS[environment];
2168
2176
  try {
@@ -2181,9 +2189,50 @@ function createCashClient(options) {
2181
2189
  chainId: prepared.chainId
2182
2190
  };
2183
2191
  } catch (err) {
2184
- throw errors.accessPolicyConfigurationFailed(compositeId, groupIds, err);
2192
+ throw errors.accessPolicyConfigurationFailed(compositeId, groupIds, {
2193
+ cause: err,
2194
+ ...source ? { source } : {}
2195
+ });
2185
2196
  }
2186
2197
  }
2198
+ async function configureCashoutAccess(client, signer, depositInput, depositId, source) {
2199
+ if (!requiresCashoutAccessPolicy(depositInput)) return void 0;
2200
+ const groupIds = CASH_ACCESS_GROUP_IDS[environment];
2201
+ const prepared = prepareCashoutAccess(depositId, client, source);
2202
+ let hash;
2203
+ try {
2204
+ hash = await signer.sendTransaction({
2205
+ account: signer.account,
2206
+ chain: signer.chain,
2207
+ to: prepared.to,
2208
+ data: prepared.data,
2209
+ value: prepared.value
2210
+ });
2211
+ } catch (err) {
2212
+ throw errors.accessPolicyConfigurationFailed(depositId, groupIds, {
2213
+ cause: err,
2214
+ ...source ? { source } : {}
2215
+ });
2216
+ }
2217
+ let receipt;
2218
+ try {
2219
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash });
2220
+ } catch (err) {
2221
+ throw errors.accessPolicyConfigurationFailed(depositId, groupIds, {
2222
+ cause: err,
2223
+ transactionHash: hash,
2224
+ ...source ? { source } : {}
2225
+ });
2226
+ }
2227
+ if (receipt.status === "reverted") {
2228
+ throw errors.accessPolicyConfigurationFailed(depositId, groupIds, {
2229
+ cause: errors.transactionFailed(hash),
2230
+ transactionHash: hash,
2231
+ ...source ? { source } : {}
2232
+ });
2233
+ }
2234
+ return hash;
2235
+ }
2187
2236
  return {
2188
2237
  capabilities,
2189
2238
  async sourceCapabilities() {
@@ -2221,11 +2270,8 @@ function createCashClient(options) {
2221
2270
  },
2222
2271
  async cashout(input, opts) {
2223
2272
  const payoutInput = validatePayout(input);
2224
- assertGenericCashoutSupported(payoutInput);
2225
2273
  const client = await signingClient("cashout", opts);
2226
2274
  const owner = opts.signer.account.address;
2227
- let sourceResult;
2228
- let cashoutAmount = input.amount;
2229
2275
  if (input.source) {
2230
2276
  const sourceSigner = opts.sourceSigner ?? (input.source.chainId === BASE_CHAIN_ID ? opts.signer : void 0);
2231
2277
  if (!sourceSigner?.account) throw errors.signerRequired("source cashout");
@@ -2246,7 +2292,7 @@ function createCashClient(options) {
2246
2292
  if (relayQuote.outputAmount < MIN_CASHOUT_AMOUNT) {
2247
2293
  throw errors.amountBelowMinimum(relayQuote.outputAmount, MIN_CASHOUT_AMOUNT);
2248
2294
  }
2249
- cashoutAmount = relayQuote.outputAmount;
2295
+ const cashoutAmount = relayQuote.outputAmount;
2250
2296
  const depositInput2 = validateDepositInput(cashoutAmount, input, payoutInput);
2251
2297
  const params2 = await buildDepositParams(client, depositInput2);
2252
2298
  const escrow2 = client.escrowV2Address ?? client.escrowAddress;
@@ -2263,7 +2309,6 @@ function createCashClient(options) {
2263
2309
  txHashes: executed.txHashes,
2264
2310
  ...executed.transactions ? { transactions: executed.transactions } : {}
2265
2311
  };
2266
- sourceResult = routedSource;
2267
2312
  try {
2268
2313
  await waitForBaseSignerAfterRelay(
2269
2314
  client,
@@ -2319,6 +2364,13 @@ function createCashClient(options) {
2319
2364
  const abi2 = client.escrowV2Abi ?? client.escrowAbi;
2320
2365
  const resolved2 = resolveCashDepositId({ logs: receipt2.logs, abi: abi2 });
2321
2366
  if (!resolved2) throw errors.depositResolutionFailed(hash2);
2367
+ const accessPolicyTxHash2 = await configureCashoutAccess(
2368
+ client,
2369
+ opts.signer,
2370
+ depositInput2,
2371
+ resolved2.compositeId,
2372
+ routedSource
2373
+ );
2322
2374
  const order2 = deriveCashOrder(resolved2.compositeId, [], {
2323
2375
  remainingAmount: depositInput2.amount,
2324
2376
  status: "ACTIVE"
@@ -2329,6 +2381,7 @@ function createCashClient(options) {
2329
2381
  escrowAddress: resolved2.escrowAddress,
2330
2382
  onchainDepositId: resolved2.onchainDepositId,
2331
2383
  order: order2,
2384
+ ...accessPolicyTxHash2 ? { accessPolicyTxHash: accessPolicyTxHash2 } : {},
2332
2385
  source: routedSource
2333
2386
  };
2334
2387
  }
@@ -2373,6 +2426,12 @@ function createCashClient(options) {
2373
2426
  const abi = client.escrowV2Abi ?? client.escrowAbi;
2374
2427
  const resolved = resolveCashDepositId({ logs: receipt.logs, abi });
2375
2428
  if (!resolved) throw errors.depositResolutionFailed(hash);
2429
+ const accessPolicyTxHash = await configureCashoutAccess(
2430
+ client,
2431
+ opts.signer,
2432
+ depositInput,
2433
+ resolved.compositeId
2434
+ );
2376
2435
  const order = deriveCashOrder(resolved.compositeId, [], {
2377
2436
  remainingAmount: depositInput.amount,
2378
2437
  status: "ACTIVE"
@@ -2383,13 +2442,12 @@ function createCashClient(options) {
2383
2442
  escrowAddress: resolved.escrowAddress,
2384
2443
  onchainDepositId: resolved.onchainDepositId,
2385
2444
  order,
2386
- ...sourceResult ? { source: sourceResult } : {}
2445
+ ...accessPolicyTxHash ? { accessPolicyTxHash } : {}
2387
2446
  };
2388
2447
  },
2389
2448
  async prepare(input) {
2390
2449
  if (input.source) throw errors.sourceRouteUnsupportedInPrepare();
2391
2450
  const depositInput = validateDepositInput(input.amount, input);
2392
- assertGenericCashoutSupported(depositInput);
2393
2451
  const params = await buildDepositParams(readClient, depositInput);
2394
2452
  const { prepared } = await readClient.prepareCreateDeposit({
2395
2453
  ...params,
@@ -2422,7 +2480,7 @@ function createCashClient(options) {
2422
2480
  }
2423
2481
  ],
2424
2482
  register: { hashedOnchainIds },
2425
- accessPolicyRequired: false
2483
+ accessPolicyRequired: requiresCashoutAccessPolicy(depositInput)
2426
2484
  };
2427
2485
  },
2428
2486
  finalizePreparedCashout(receipt) {
@@ -3049,7 +3107,8 @@ var cashErrorRecoveryJsonSchema = zod.z.discriminatedUnion("kind", [
3049
3107
  kind: zod.z.literal("configure-cashout-access-policy"),
3050
3108
  depositId: zod.z.string(),
3051
3109
  groupIds: zod.z.array(zod.z.string()),
3052
- transactionHash: zod.z.string().optional()
3110
+ transactionHash: zod.z.string().optional(),
3111
+ source: zod.z.object(cashSourceRecoveryJsonShape).strict().optional()
3053
3112
  }).strict()
3054
3113
  ]);
3055
3114
  var cashErrorJsonSchema = zod.z.object({
@@ -3377,6 +3436,10 @@ function topUpResultFromJson(json) {
3377
3436
  function capabilitiesToJson(caps) {
3378
3437
  return {
3379
3438
  ...caps,
3439
+ platforms: caps.platforms.map((platform) => ({
3440
+ ...platform,
3441
+ requiresAtomicAccessPolicy: false
3442
+ })),
3380
3443
  amount: {
3381
3444
  min: caps.amount.min.toString(),
3382
3445
  recommendedMin: caps.amount.recommendedMin.toString(),
@@ -3395,7 +3458,7 @@ function capabilitiesFromJson(json) {
3395
3458
  platforms: parsed.platforms.map((p) => ({
3396
3459
  ...p,
3397
3460
  currencies: p.currencies,
3398
- requiresAtomicAccessPolicy: p.requiresAtomicAccessPolicy ?? CASH_RESTRICTED_PLATFORMS.has(p.platform)
3461
+ requiresAtomicAccessPolicy: false
3399
3462
  })),
3400
3463
  currencies: parsed.currencies,
3401
3464
  amount: {
@@ -3429,7 +3492,15 @@ function cashErrorFromJson(json) {
3429
3492
  kind: parsed.recovery.kind,
3430
3493
  depositId: parsed.recovery.depositId,
3431
3494
  groupIds: parsed.recovery.groupIds,
3432
- ...parsed.recovery.transactionHash !== void 0 ? { transactionHash: parsed.recovery.transactionHash } : {}
3495
+ ...parsed.recovery.transactionHash !== void 0 ? { transactionHash: parsed.recovery.transactionHash } : {},
3496
+ ...parsed.recovery.source !== void 0 ? {
3497
+ source: {
3498
+ amount: parsed.recovery.source.amount,
3499
+ txHashes: parsed.recovery.source.txHashes,
3500
+ ...parsed.recovery.source.requestId !== void 0 ? { requestId: parsed.recovery.source.requestId } : {},
3501
+ ...parsed.recovery.source.transactions !== void 0 ? { transactions: parsed.recovery.source.transactions } : {}
3502
+ }
3503
+ } : {}
3433
3504
  };
3434
3505
  } else if (parsed.recovery.kind === "inspect-base-operation-submission") {
3435
3506
  recovery = {