@zkp2p/sdk 0.15.0-rc.0 → 0.15.0-rc.2

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/README.md CHANGED
@@ -35,7 +35,7 @@ and chargeback-risk surface. The workspace version lives in `package.json`.
35
35
 
36
36
  ## Optional Venmo Gmail connection
37
37
 
38
- For partner cash-out integrations, prefer stable `@zkp2p/cash@0.7.2` and its
38
+ For partner cash-out integrations, prefer stable `@zkp2p/cash@0.7.3` and its
39
39
  [prepare/open/status flow](https://docs.peer.xyz/developer/peer-cash/guide#optional-venmo-receipt-linking).
40
40
  It selects the matching environment and keeps linking separate from cash-out.
41
41
  The direct SDK helpers below require `@zkp2p/sdk@0.14.2` or later.
@@ -79,6 +79,23 @@ hosts and custom window handling with account-status polling at least ten second
79
79
  apart. Deploy the hosted page before using the SDK helper. See the
80
80
  [flow specification](https://github.com/zkp2p/zkp2p-clients/blob/main/docs/venmo-gmail-connect.md).
81
81
 
82
+ ### X Money receipt linking
83
+
84
+ SDK `0.14.10` adds `getXMoneyConnectUrl` and `openXMoneyConnect`, using the same mailbox flow at
85
+ `/connect/xmoney`. Pass the registered `payeeDetails` hash and `payeeUsername`.
86
+ The normalized X username must hash to that identity. Both stay in the URL
87
+ fragment. X Money email notifications must be enabled and the inbox must contain
88
+ a signed received-payment receipt for that account. Gmail, personal Outlook and
89
+ iCloud Mail are supported. Deploy the hosted page before enabling this helper.
90
+
91
+ ```ts
92
+ import { openXMoneyConnect } from '@zkp2p/sdk';
93
+ await openXMoneyConnect({ payeeDetails, payeeUsername: 'seller' });
94
+ ```
95
+
96
+ The URL builder also accepts `returnUrl` for native hosts. Re-check the mailbox
97
+ credential status after return or dismissal, as with Venmo.
98
+
82
99
  ### Native app return
83
100
 
84
101
  SDK `0.14.3` adds optional `returnUrl` to the URL builder. Partners
@@ -292,9 +309,10 @@ bundle `payeeIdHash`, and store the encrypted bundle with curator. Plaintext
292
309
  seller session material is never returned to the page.
293
310
 
294
311
  `Zkp2pClient.uploadSellerCredential()` supports Cash App, Wise, and Amazon Pay UPI
295
- plaintext input from trusted runtimes. Venmo and PayPal use the Google OAuth helper. Internally it
296
- creates the encrypted bundle, then uses the same
297
- `apiUploadSellerCredentialBundle()` storage helper.
312
+ plaintext input from trusted runtimes. It creates the encrypted bundle, then uses
313
+ `apiUploadSellerCredentialBundle()` for storage. Venmo, PayPal, and X Money use
314
+ the [mailbox upload helpers](#seller-credential-status) for Gmail, personal
315
+ Outlook, or iCloud Mail.
298
316
 
299
317
  ## Create a Deposit
300
318
 
@@ -314,14 +332,17 @@ console.log(result.hash);
314
332
  console.log(result.depositDetails);
315
333
  ```
316
334
 
317
- By default, `createDeposit()` enables the trusted group bypass on Venmo and
318
- PayPal methods. After the deposit mines, it sends one
319
- `WhitelistPolicy.configureDeposit` per Venmo/PayPal method with the Peer Pay
335
+ The trusted-group whitelist is opt-in via `enableTrustedGroupBypass: true`
336
+ (defaults to false). By default, `createDeposit()` sends only the deposit and
337
+ returns empty `result.accessPolicyTxHashes` without waiting for a receipt.
338
+ When opted in, after the deposit mines, it sends one `configureDeposit` on the
339
+ current deposit whitelist (see [Deposit Access Policy](#deposit-access-policy))
340
+ per Venmo/PayPal method with the Peer Pay
320
341
  merchant group and returns the confirmed hashes as
321
342
  `result.accessPolicyTxHashes` (empty for other methods). Members of that group
322
- skip dispute-protection staking; other takers still stake, and until the policy
323
- mines, merchants stake too. Pass `enableTrustedGroupBypass: false` to skip it.
324
- If the deposit was sent but the policy was not confirmed, it throws a
343
+ skip dispute-protection staking. On OrchestratorV4 an enabled whitelist makes
344
+ the deposit private: only Peer Pay merchant-group members can signal.
345
+ When opted in, if the deposit was sent but the policy was not confirmed, it throws a
325
346
  `ContractError` whose `details.hash` is the deposit transaction. `prepareCreateDeposit()` does not add
326
347
  the policy; custom senders call `client.accessPolicy.prepareConfigurePeerPayMerchantDeposit()`
327
348
  once the deposit ID is known.
@@ -390,6 +411,7 @@ await client.setTakerAuthorization({ taker, authorized: true }); // signed by th
390
411
  await client.selectStakeOwner({ stakeOwner: safeAddress }); // signed by the taker
391
412
  await client.clearStakeOwner(); // taker returns to self-stake
392
413
 
414
+ // Explicit vaults are verified through the live stack independently by each client.
393
415
  const onchain = await client.getStakeVaultState({
394
416
  staker: takerAddress,
395
417
  taker: takerAddress,
@@ -451,32 +473,34 @@ withdraws the complete claimable balance. Chargebackable capacity is exactly
451
473
  the effective owner's free stake (1:1); never sum a taker's self-stake with a
452
474
  sponsor's stake.
453
475
 
454
- `getActiveDisputeProtectionStack()` reads the live OrchestratorV3 hook, policy,
455
- and vault. It returns `null` for a zero hook, rejects unknown policies or vaults,
456
- and caches discovery per client for 30 seconds; failures are never cached.
457
- `getActiveStakeVaultContract()` returns that vault (canonical when the hook is zero).
458
- `getKnownStakeVaults()` lists canonical, Bypass, and any exported MethodScoped
459
- predecessor vault, deduplicated by address. `isNoStakeAvailable()` is true only
460
- when the live policy is the resolved payment-validation policy.
461
-
462
- Pass `stakeVaultAddress?: Address` to `getStakeVaultContract(options?)`,
463
- `getStakeVaultState({ staker, taker?, stakeVaultAddress? })`, every vault write,
464
- and `ensureStakeAllowance`. Single-value vault reads accept a trailing optional
465
- `options?: { stakeVaultAddress?: Address }`. Unknown vaults throw; reads, writes,
466
- and allowance default to the active vault (canonical only for a zero hook).
467
- The synchronous `getStakeVaultContract()` getter still returns bundle/canonical
468
- coordinates when no address is given; use `getActiveStakeVaultContract()` for
469
- the vault the live policy locks from. Each vault-state call resolves one vault
470
- for all accounting reads and its stake token; `admissionsPaused` comes from
471
- that vault's controller.
472
- The standalone `getAdmissionsPaused`, `getRiskWindow`, and
473
- `isDisputeProtectionEnabled` reads follow the live policy, using canonical only
474
- when the live hook is zero. `setDisputeProtectionEnabled` follows the same live
475
- policy unless `disputeProtectionPolicyAddress` is supplied.
476
-
477
- The vault's controller is the `DisputeProtectionPolicy` contract. It owns
476
+ `getDisputeProtectionStacks()` discovers `{ current, draining }` from
477
+ OrchestratorV4 and OrchestratorV3 respectively, following O4 settlementPolicy to its vault, and the August/O3 hook to its policy and
478
+ vault. Discovery is cached for 30 seconds; failures are retried. The active vault
479
+ is the V4 vault when V4 is deployed, otherwise the V3 vault. Known previous vaults
480
+ remain selectable with `stakeVaultAddress` for reads and withdrawals; on staging these
481
+ are the August and Bypass vaults. The Bypass policy also stays known, so O3 intents
482
+ holding records there still resolve. O4 discovery verifies
483
+ `policy.isOrchestratorAuthorized(O4)` and `vault.isController(policy)`. V2 uses
484
+ the StakeVaultV2 ABI; August vaults retain StakeVault and its single controller. A missing
485
+ current stack fails closed. `@zkp2p/contracts-v2@0.4.4-rc.6` exports the Base staging
486
+ V4 stack. Base exports zero placeholders, which never enable V4.
487
+
488
+ Explicit vault addresses passed to stake reads, writes and allowance setup are verified
489
+ against bundled deployments or the trusted V4 live stack, cached per client for 30 seconds.
490
+ Use `await client.resolveStakeVaultContract({ stakeVaultAddress })` when contract
491
+ coordinates are needed before a direct read. `getStakeVaultContract` remains a
492
+ synchronous lookup of already-known vaults.
493
+
494
+ `getAdmissionsPaused`, `getRiskWindow`, `isDisputeProtectionEnabled` and
495
+ `setDisputeProtectionEnabled` target only the August V1 policy behind
496
+ OrchestratorV3. An explicit V2 address or a missing V3 stack fails closed.
497
+ V2 has no on-chain risk-window or pause getter; `StakeVaultState.admissionsPaused`
498
+ is omitted for V2 vaults. Use the attestor rail policy for quote-time defaults.
499
+ Releases look up each intent's original policy;
500
+ a batch spanning policies throws `ValidationError`.
501
+
502
+ The August vault's controller is the `DisputeProtectionPolicy` contract. It owns
478
503
  per-payment-method risk windows (`getRiskWindow(paymentMethodHash)`),
479
- per-deposit chargeback enablement (`isDisputeProtectionEnabled(escrow, depositId)`),
480
504
  and the global `admissionsPaused()` switch. The selected `@zkp2p/contracts-v2`
481
505
  bundle resolves the StakeVault, DisputeProtectionPolicy, and OrchestratorV3 for
482
506
  both the production and staging bundles; a bundle without them fails closed
@@ -487,9 +511,11 @@ deployments).
487
511
  Keeper writes are direct and prepareable:
488
512
 
489
513
  ```ts
514
+ // August V1 policy behind OrchestratorV3 only.
490
515
  const enableProtection = await client.setDisputeProtectionEnabled.prepare({
491
516
  escrow: depositEscrow,
492
517
  depositId: 42n,
518
+ paymentMethod: paymentMethodHash,
493
519
  enabled: true,
494
520
  });
495
521
 
@@ -523,24 +549,8 @@ same finalized cutoff for every cursor in one ingestion pass. Follow
523
549
  `endCursor` with its final block/log position; `endCursor` is null only for an
524
550
  empty page.
525
551
 
526
- Before enabling dispute-protected admission, prove that the live Base contracts
527
- match the packaged manifest or verified Bypass successor records:
528
-
529
- ```ts
530
- const readiness = await client.getDisputeProtectionReadiness();
531
-
532
- if (readiness.state !== 'active_successor_verified' || !readiness.readyForDisputeProtection) {
533
- throw new Error(`Dispute protection is unavailable: ${readiness.state}`);
534
- }
535
- ```
536
-
537
- Known results expose `verifiedStack`: `manifest` for the manifest path or
538
- `bypass_records` for an active Bypass hook distinct from the manifest hook. The
539
- Bypass path proves code presence and the specified hook/registry/policy/vault
540
- relations at one block; it does not claim manifest runtime-hash or governance
541
- verification for those new deployments. Missing records leave this path off.
542
- On Base staging the manifest now names the Bypass stack, so its Bypass records
543
- equal the manifest identities and readiness runs the full manifest path there.
552
+ The existing readiness API verifies the V3 manifest only. V4 readiness is pending
553
+ the new deployment manifest.
544
554
 
545
555
  The manifest reader pins every code and state read to one `asOfBlock`, including exact
546
556
  governance owners, authorization sets, attestation witnesses, and signature
@@ -588,13 +598,11 @@ selection hashes. A local build has the deterministic `development` identity
588
598
  and is deliberately non-distributable, so its readiness reader returns
589
599
  `mismatch` before making RPC calls.
590
600
 
591
- Quote and orderbook requests accept `includeNoStakeCompatible?: boolean` to opt in to protected rows a taker may signal without stake (Venmo only today). Only `true` is sent; false and omitted values leave the field absent.
592
-
593
601
  For table-style liquidity UIs, the low-level `apiGetOrderbookTable()` adapter exposes curator's paginated public/private orderbook rows and row-level privacy metadata.
594
602
 
595
603
  ## Seller Credential Status
596
604
 
597
- Venmo and PayPal Seller Autopilot connections use `uploadGoogleOAuthSellerCredential` for Gmail, `uploadMicrosoftOAuthSellerCredential` for personal Outlook accounts, or `uploadICloudMailSellerCredential` for iCloud Mail. The normal `uploadSellerCredential` and extension seller-capture paths support Cash App, Wise (token upload only), and Amazon Pay UPI. Existing backend Venmo cookie bundles remain usable through status/verification and low-level encrypted-bundle APIs.
605
+ Venmo, PayPal and X Money Seller Autopilot connections use `uploadGoogleOAuthSellerCredential` for Gmail, `uploadMicrosoftOAuthSellerCredential` for personal Outlook accounts, or `uploadICloudMailSellerCredential` for iCloud Mail. The normal `uploadSellerCredential` and extension seller-capture paths support Cash App, Wise (token upload only), and Amazon Pay UPI. Existing backend Venmo cookie bundles remain usable through status/verification and low-level encrypted-bundle APIs.
598
606
 
599
607
  ```ts
600
608
  await client.uploadGoogleOAuthSellerCredential({
@@ -618,9 +626,11 @@ await client.uploadMicrosoftOAuthSellerCredential({
618
626
  });
619
627
  ```
620
628
 
629
+ X Money mailbox uploads require SDK 0.14.9 or later. For Gmail and Outlook, pass `platform: 'xmoney'` with the registered maker hash and the same OAuth code fields; no `payeeEmail` is needed. Enable X Money email notifications and receive a payment before connecting. The server accepts a credential only after validating a received-payment email for the registered X account. A historical receipt establishes delivery, not the current notification-setting state; keep notifications enabled for future payments. A missing or invalid receipt returns `XMONEY_EMAIL_RECEIPT_NOT_FOUND`.
630
+
621
631
  Validate OAuth state in the initiating browser tab before uploading. `redirectUri` must exactly match the one in the authorization request. Both methods exchange a one-time code without retrying; start fresh consent if the response is lost. The Microsoft client secret stays server-side. See [email connection setup](../../docs/email-oauth.md).
622
632
 
623
- iCloud Mail has no OAuth. Pass the seller's Apple app-specific password with the registered maker hash; PayPal also passes its receiving email. For Venmo, the SDK fetches the maker's account ID from Curator (`GET /v2/makers/venmo/{payeeDetails}/payee-id`) and checks it against `payeeDetails` before any mailbox work, so sellers never enter it.
633
+ iCloud Mail has no OAuth. Pass the seller's Apple app-specific password with the registered maker hash; PayPal also passes its receiving email, and X Money passes `payeeUsername` (normalized lowercase and checked against the maker hash). For Venmo, the SDK fetches the maker's account ID from Curator (`GET /v2/makers/venmo/{payeeDetails}/payee-id`) and checks it against `payeeDetails` before any mailbox work, so sellers never enter it.
624
634
 
625
635
  ```ts
626
636
  await client.uploadICloudMailSellerCredential({
@@ -675,90 +685,124 @@ await client.releaseFundsToPayer({
675
685
 
676
686
  Use `referralFees[]` for multi-recipient fee distribution on OrchestratorV2 and V3.
677
687
 
678
- ### Signal intent stake mode
679
-
680
- `signalIntent`, `signalIntent.prepare`, `prepareSignalIntent`, and the React
681
- `useSignalIntent` hook accept `noStake?: boolean` (default `false`). Use `true`
682
- only for Venmo Friends & Family payments funded from Venmo balance. Any other
683
- payment method, by name or hash, throws `ValidationError` with field `noStake`
684
- before `/v3/sign` is called. The SDK sends
685
- `abi.encode(paymentValidationPolicy, abi.encode(noStake))` to both `/v3/sign`
686
- and OrchestratorV3 whenever the selected deployment has a payment-validation
687
- policy. This target comes from the bundle, independently of the live hook.
688
-
689
- The installed `@zkp2p/contracts-v2@0.4.4-rc.5` bundle records the fresh Bypass trio
690
- redeployed on Base staging on 2026-10-13 as active: the staging canonical aliases
691
- (`StakeVault`, `DisputeProtectionPolicy`, `IntentLifecycleHookV1`) and the staging
692
- dispute-stack manifest name the same addresses as the Bypass records, staging
693
- selects UPV4, and staging signals carry the policy envelope. The predecessor
694
- staging vault stays exported as `StakeVaultMethodScoped`, so its stake remains
695
- readable and withdrawable. Base (production and preproduction) exports zero
696
- placeholders that the resolvers ignore: signals keep `0x`, UPV3 remains
697
- selected, and `noStake: true` throws `ValidationError` with field `noStake`.
698
- Base canonical aliases and its dispute-stack manifest are unchanged. No-stake
699
- availability still follows the live chain: the live lifecycle hook's policy
700
- must be the resolved payment-validation policy.
701
-
702
- **Breaking migration:** caller `data` is removed from signal parameters. UPV4
703
- interprets every non-empty V3 data value as a payment-validation envelope.
704
- Pass `noStake` instead; supply post-intent hook inputs as `postIntentHookData`
705
- at fulfillment. Legacy JavaScript calls with `data: '0x'` or `undefined`
706
- are accepted; other values throw `ValidationError` with field `data`.
707
-
708
- ### Read or change an intent's stake mode
709
-
710
- `client.getPaymentValidationPolicyContract()` returns `{ address, abi }` or
711
- `undefined`. The standalone `getPaymentValidationPolicyContract(chainId, env)`
712
- resolves only the named Bypass policy record or its staging override. Bypass
713
- records remain exported by name after canonical aliases move to their addresses;
714
- without a Bypass address, resolution fails closed.
715
- The returned ABI merges the bundle with `NO_STAKE_DISPUTE_PROTECTION_POLICY_ABI`.
716
-
717
- For a deployment with that policy, the taker can change a pending intent's mode:
688
+ ### Signal data and deferred payouts
689
+
690
+ New intents target OrchestratorV4 when its deployment is present, otherwise
691
+ OrchestratorV3. Caller `data?: Hex` and `postIntentHook` are O3-only;
692
+ O4 rejects non-empty caller data or a nonzero hook. Its `/v3/sign` request omits
693
+ `postIntentHook` and sends `data: '0x'`. O4 fulfilment contains only `paymentProof`, `intentHash`, and
694
+ `verificationData`; non-empty `postIntentHookData` is rejected. Existing intents resolve their original orchestrator through indexer
695
+ routing and on-chain reads; they keep fulfilling, cancelling and releasing there.
696
+ O4 reads use the package O4 ABI directly, including account-intent enumeration;
697
+ ProtocolViewerV2 only decodes O3 and older intents. O4 intents have no `postIntentHook`.
698
+ Use `getCurrentOrchestratorContract()` for new intents and
699
+ `resolveIntentOrchestrator(intentHash)` for existing ones.
700
+
701
+ Every O4 `signalIntent` struct ends with `bytes data`. The SDK always sends `0x`,
702
+ so the gating signature covers the plain V3 packed message. The contracts can
703
+ check a conditions hash carried in `data`, but nothing sets one yet. A stale O4
704
+ ABI fails before signing or encoding.
705
+
706
+ Attestation data retains the 448-byte UPV3 payload. For non-manual O4
707
+ fulfilment, extension word 0 is the required `disputeWindow` in seconds (0 means
708
+ pay now, maximum 180 days), so O4 payloads are 480 bytes. O3's 448-byte payload
709
+ is unchanged. The SDK forwards the attestor's bytes unchanged.
710
+
711
+ V2 may require stake or hold a payout when the rail default window is positive,
712
+ the deposit token is the vault's stake token (USDC), the deposit+method whitelist
713
+ is off, and the taker is not a registered relayer bypassing collateral.
714
+ Registered relayers bypass by default unless the deposit has opted out.
715
+ Registration means O4's current `relayerRegistry()`: the policy has no registry of
716
+ its own and reads O4's at settlement, so compute `takerIsRelayer` from O4's
717
+ registry at the same block as the other quote inputs. The SDK re-exports `DISPUTE_WINDOW_POLICY`,
718
+ `getDefaultDisputeWindow` and `DisputeWindowPaymentMethod` from the attestation
719
+ client. `mayRequireDisputeProtection({ paymentMethod, depositToken, stakeToken,
720
+ whitelistEnabled, takerIsRelayer, relayerBypassOptedOut })` implements this
721
+ quote-time rule. Venmo and PayPal default to 14 days; other supported rails default to zero. The attestor can sign a
722
+ shorter window, including zero for balance-funded Venmo buyer-TEE evidence or
723
+ when its window config switch is disabled.
724
+
725
+ Manual release, a signed zero window, or a non-stake deposit token pays now
726
+ without a policy record or event. With a positive window and USDC, an enabled
727
+ deposit+method whitelist with an allowed taker pays directly without a record
728
+ or policy event. Registered relayers also pay directly unless the deposit opts out.
729
+ Otherwise sufficient free stake produces `STAKE`, or the net payout is
730
+ held as `DEFERRED_PAYOUT` until `releaseEligibleAt = settlement time + window`.
731
+ Whitelist membership, relayer registration (O4's registry, read by the policy at
732
+ settlement) and the deposit opt-out are read live at fulfilment. O4 never executes post-intent hooks.
733
+
734
+ `setDepositOptOutForRelayerBypass({ escrow, depositId, optOut })` and its
735
+ `.prepare()` form configure a V2 deposit after creation. Only the depositor may
736
+ write, not a delegate (`NotDepositor`). Read the setting with
737
+ `isDepositOptedOutForRelayerBypass({ escrow, depositId })`; false is the default.
738
+ Both methods resolve and verify the current O4 `settlementPolicy()` and fail
739
+ closed without O4. This is deposit-wide, independent of payment method; indexer
740
+ projections use `relayerBypassOptedOut`. It does not disable protection generally
741
+ or change the whitelist. No deposit-creation setting is added.
742
+
743
+ `CollateralSource` is `NONE, STAKE, DEFERRED_PAYOUT`. A missing (`NONE`)
744
+ record returns `NOT_PROTECTED`; callers treat that as paid only after successful
745
+ fulfilment. Whitelisted orders also read as `NONE`. Deferred records are `SETTLED` from
746
+ creation. Classify a fulfil receipt using the policy's
747
+ `DisputeProtectionIntentSettled` log first (it has no `isManualRelease`), then
748
+ O4's `IntentPayoutDeferred` (deferred, including after policy rotation), then
749
+ `IntentFulfilled.isManualRelease` for direct/unprotected payouts.
750
+ `parsePayoutSettlementFromLogs(logs, intentHash, resolvedAddresses)` applies this
751
+ order and rejects logs from other addresses or intents; call it only for a successful receipt.
718
752
 
719
753
  ```ts
720
- const policy = client.getPaymentValidationPolicyContract();
721
- if (!policy) throw new Error('No payment-validation policy in this deployment');
722
-
723
- const prepared = await client.setIntentNoStake.prepare({
724
- intentHash,
725
- noStake: true,
726
- });
727
- // Or execute using client.setIntentNoStake({ intentHash, noStake: true }).
728
-
729
- const noStake = await client.isIntentNoStake(intentHash);
730
- const intent = await client.getDisputeProtectionIntent(intentHash, {
731
- disputeProtectionPolicyAddress: policy.address,
732
- blockNumber: 123456n, // Optional historical read
733
- });
754
+ const payout = await client.getPayoutStatus(intentHash);
755
+ const payouts = await client.getPayoutStatuses(intentHashes);
756
+ const prepared = await client.claimFor.prepare({ intentHash });
734
757
  ```
735
758
 
736
- `setIntentNoStake` accepts optional `orchestratorAddress`,
737
- `disputeProtectionPolicyAddress`, and `txOverrides`. The default orchestrator
738
- is configured OrchestratorV3; an override must be a configured orchestrator.
739
- The policy defaults to the payment-validation policy and throws if neither
740
- resolution nor an explicit address supplies one. The contract enforces taker
741
- ownership, pending status, and sufficient free stake when switching back to
742
- staked mode.
743
-
744
- `isIntentNoStake(intentHash, { disputeProtectionPolicyAddress? })` uses the same
745
- policy default. `getDisputeProtectionIntent(intentHash, options?)` also accepts
746
- `blockNumber` and falls back to the canonical policy: its eight-field tuple is
747
- identical on the old deployment. Pin the original policy when reading historical
748
- intents; this API does not discover which policy owns an intent.
749
-
750
- `DisputeProtectionIntent` contains `taker`, `stakeOwner`, `depositor`,
751
- `paymentMethod`, `status`, `riskWindow`, `releaseEligibleAt`, and
752
- `releaseAmount`. Its exported `DisputeProtectionIntentStatus` enum is
753
- `None=0, Pending=1, Cancelled=2, Settled=3, Released=4, Disputed=5`.
754
- `parseDisputeProtectionIntent(value: unknown)` accepts a named object or
755
- positional tuple and returns `null` for malformed values. The client getter
756
- throws for malformed results.
757
-
758
- `encodePaymentValidationHookData(policy: Address, noStake: boolean): Hex` and
759
- `decodePaymentValidationHookData(data: Hex): { policy: Address; noStake: boolean } | null`
760
- are exported for integrations validating envelopes; decoding rejects malformed
761
- or noncanonical data.
759
+ `PayoutState` contains `NOT_PROTECTED`, `PAID`, `HELD`,
760
+ `CLAIMABLE`, `CLAIMED`, `RELEASED_TO_STAKE`, and `DISPUTED`. Results include
761
+ `collateralSource`, `recipient`, `taker`, `lockedAmount`, `claimedAmount`,
762
+ `releaseEligibleAt`, and `claimable`. Reads pin the intent and dependent free-stake
763
+ batches to one block. A released payout can be claimed only up to the remaining
764
+ per-intent amount and the recipient's free stake. The taker or recipient may call
765
+ `claimFor`; funds always go to the recipient.
766
+
767
+ `claimFor({ intentHash, disputeProtectionPolicyAddress?, txOverrides? })` resolves
768
+ the policy holding the intent. An optional `disputeProtectionPolicyAddress` must
769
+ match that resolved policy; a mismatch, missing intent, invalid hash, or
770
+ non-cascade policy throws `ValidationError`. Both sending and `.prepare()` target
771
+ the resolved cascade policy. Omitting the address performs the same lookup.
772
+ Payout reads and `claimFor` first ask the client's indexer for the intent's
773
+ settlement policy (`IntentSettlementPolicyState`, recorded from O4's
774
+ `IntentSettlementPolicySnapshotted`; O4 deletes its own snapshot at settlement).
775
+ They read the record from that policy and use that policy's `stakeVault()`, so a
776
+ policy rotated out before a package update still resolves. Only rows for the
777
+ client's chain and packaged OrchestratorV4 count, and the indexed policy must
778
+ return a valid V2 record for the intent. When the indexer has no row (for example
779
+ just after fulfilment), fails, or does not serve the entity yet, or the indexed
780
+ policy has no record, the SDK falls back to the packaged `disputeProtectionPolicyV2`
781
+ and the live `settlementPolicy()` read from the packaged OrchestratorV4 address.
782
+ Zero addresses are skipped and candidates are deduplicated; both are classified
783
+ as V2. If the indexed policy's read fails and the fallback finds no record, the
784
+ read throws instead of returning `NOT_PROTECTED`. Payout reads pin every record,
785
+ each holding policy's `stakeVault()`, and its recipient's `freeStake` to one
786
+ block. No record returns `NOT_PROTECTED`; records on two fallback policies fail
787
+ closed. Claims target the policy holding the record, which pays only the
788
+ record's recipient. The standalone `getPayoutStatuses(publicClient, chainId, env,
789
+ hashes, { indexer? })` and `findDisputeProtectionIntent({ ..., indexer? })` use an
790
+ indexer only when one is passed. The strict current-stack checks for stake writes
791
+ are unchanged.
792
+
793
+ `client.parseDeferredPayoutFromLogs(logs, intentHash)` accepts only events from
794
+ its packaged cascade policy or V4 orchestrator. O4's `IntentPayoutDeferred` also identifies deferrals after policy rotation. The standalone parser additionally
795
+ requires `{ disputeProtectionPolicyV2Address, orchestratorV4Address }`.
796
+
797
+ `findDisputeProtectionIntent` returns a discriminated `generation`:
798
+ `PolicyGeneration.V2` has a validated ten-field record (no `riskWindow` or `releaseAmount`) and four statuses (`NONE`, `SETTLED`, `RELEASED`, `DISPUTED`, ordinals 0–3);
799
+ `PolicyGeneration.V1` retains the eight-field record and six statuses.
800
+ `readDisputeAttestationConfig({ policyAddress })` follows either known generation.
801
+
802
+ V4 readiness and indexer APIs that list deferred payouts await the new manifest
803
+ and indexer schema; payout reads already use the indexer's settlement-policy rows
804
+ when it serves them. The retained readiness reader verifies V3 only and is not V4 readiness
805
+ proof. This SDK version is `0.15.0-rc.2`, a prerelease on the npm `dev` tag.
762
806
 
763
807
  ## Extend an Intent Lifetime
764
808
 
@@ -807,15 +851,28 @@ The guardian charges the target deposit's token and pays the deposit owner.
807
851
  Extension payments are prepaid and non-refundable. `extendIntentLifetime.prepare()`
808
852
  returns calldata without sending it.
809
853
 
810
- ## Historical Deposit Access Policy
854
+ ## Deposit Access Policy
811
855
 
812
856
  `client.accessPolicy` exposes environment-aware reads and unwind-only prepared
813
857
  transactions for policies written before Groups were retired. Gate use on
814
858
  `client.accessPolicy.isSupported`; unsupported deployments fail closed.
815
859
 
860
+ It targets the current orchestrator's deposit whitelist. That is
861
+ `WhitelistPolicyV4` when OrchestratorV4 is deployed (Base staging), otherwise
862
+ `WhitelistPolicy` (Base production). `getContracts().addresses.whitelistPolicy`
863
+ and `client.whitelistPolicyAddress` resolve the same contract, which equals
864
+ `whitelistPolicy()` on OrchestratorV4 and DisputeProtectionPolicyV2. If
865
+ OrchestratorV4 is deployed without `WhitelistPolicyV4`, both are undefined and
866
+ access-policy calls fail closed. They never fall back to O3's whitelist.
867
+ Every deposit starts with the whitelist off on `WhitelistPolicyV4`. Entries on
868
+ O3's `WhitelistPolicy` don't carry over and only gate OrchestratorV3 signals.
869
+
816
870
  Existing policies remain enforced on-chain until disabled or their deposits
817
- close. The only new policy is the trusted group bypass that `createDeposit()`
818
- adds to Venmo and PayPal deposits by default. Depositors can use
871
+ close. The only new policy is the opt-in trusted-group whitelist that
872
+ `createDeposit()` adds to Venmo and PayPal deposits only with
873
+ `enableTrustedGroupBypass: true` (defaults to false). On OrchestratorV4 an enabled
874
+ whitelist makes the deposit private: only Peer Pay merchant-group members can
875
+ signal, and members skip dispute-protection staking. Depositors can use
819
876
  `prepareDisable`, `prepareRemoveAllowedGroups`, and
820
877
  `prepareRemoveWhitelistedAddresses` to fail open and remove historical entries.
821
878
 
@@ -1325,11 +1382,15 @@ Amazon capture requires extension 0.6.8 or later. Staging SAR uses the trusted s
1325
1382
  ### Historical dispute policy lookup
1326
1383
 
1327
1384
  `findDisputeProtectionIntent({ publicClient, chainId, environment, intentHash,
1328
- blockNumber? })` queries the payment-validation, canonical, manifest, and
1329
- recognized predecessor policies in that order, deduplicated. The first intent
1330
- whose status is not `None` wins. It returns `null` only when every intent read
1331
- parses as `None`; an unreadable intent throws. The result contains
1332
- `policyAddress`, the parsed `intent`, and `noStake` (false if that getter reverts).
1385
+ blockNumber?, indexer? })` first reads the indexer's settlement policy for the
1386
+ intent when an `indexer` is passed (see deferred payouts). Otherwise, or when that
1387
+ policy holds no record, it queries the packaged V2 and O4 live settlement policies, then
1388
+ canonical, manifest, and recognized predecessor policies, deduplicated at one
1389
+ block. Both V2 candidates use the cascade ABI. Multiple V2 records for one hash
1390
+ throw; otherwise the first intent whose status is not `None` wins. It returns `null` only when every intent read parses as
1391
+ `None`; an unreadable intent throws. The result contains the policy
1392
+ `generation` (`v2` or `v1`), `policyAddress`, and the `intent` parsed with
1393
+ that generation's struct and status enum.
1333
1394
  All calls share a block. Use the returned policy for dispute submission and
1334
1395
  `readDisputeAttestationConfig({ publicClient, chainId, environment, policyAddress })`.
1335
1396
  For a known non-manifest policy, the latter follows `disputeVerifier()` and
@@ -2,12 +2,12 @@
2
2
  "schemaVersion": 2,
3
3
  "source": {
4
4
  "repository": "zkp2p/zkp2p-clients",
5
- "commitSha": "471e50cadb04d41b35fe48316aa94c56dec4d4a7",
5
+ "commitSha": "82e9074021ad0c79aa3391e64e2879f0f23c118e",
6
6
  "kind": "github",
7
7
  "distributable": true
8
8
  },
9
- "sdkPackageVersion": "0.15.0-rc.0",
10
- "contractsPackageVersion": "0.4.4-rc.5",
9
+ "sdkPackageVersion": "0.15.0-rc.2",
10
+ "contractsPackageVersion": "0.4.4-rc.6",
11
11
  "indexerSchemaPackageVersion": "0.22.0",
12
12
  "activeDisputeStack": {
13
13
  "base": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "baseStaging": {
18
18
  "version": 2,
19
- "selectionHash": "e3b360a09c2f541052ab7519987862e1f59c0efc9bc25daea09937e9ea59b783"
19
+ "selectionHash": "6c49c1ce51eec594a22385c4efcab08aa9926b6b63a725e14a0027ec5d2f4fc9"
20
20
  }
21
21
  }
22
22
  }