@provex/extension-react 1.11.0 → 2.0.0

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.
Files changed (50) hide show
  1. package/README.md +23 -123
  2. package/dist/ZKTLSContext.cjs +7 -75
  3. package/dist/ZKTLSContext.cjs.map +1 -1
  4. package/dist/ZKTLSContext.d.cts +1 -15
  5. package/dist/ZKTLSContext.d.ts +1 -15
  6. package/dist/ZKTLSContext.js +2 -2
  7. package/dist/adapters/extensionEnclave.cjs.map +1 -1
  8. package/dist/adapters/extensionEnclave.js +1 -1
  9. package/dist/capability.cjs +50 -0
  10. package/dist/capability.cjs.map +1 -0
  11. package/dist/capability.d.cts +95 -0
  12. package/dist/capability.d.ts +95 -0
  13. package/dist/capability.js +3 -0
  14. package/dist/capability.js.map +1 -0
  15. package/dist/{chunk-T42E7EM7.js → chunk-B7RU2WKQ.js} +3 -3
  16. package/dist/{chunk-T42E7EM7.js.map → chunk-B7RU2WKQ.js.map} +1 -1
  17. package/dist/chunk-JMVW4H4H.js +44 -0
  18. package/dist/chunk-JMVW4H4H.js.map +1 -0
  19. package/dist/chunk-JU2T2TGY.js +123 -0
  20. package/dist/chunk-JU2T2TGY.js.map +1 -0
  21. package/dist/chunk-VQFWUZNJ.js +59 -0
  22. package/dist/chunk-VQFWUZNJ.js.map +1 -0
  23. package/dist/{chunk-AMJXMSDV.js → chunk-WSLUOLBW.js} +2 -2
  24. package/dist/chunk-WSLUOLBW.js.map +1 -0
  25. package/dist/index.cjs +101 -187
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +5 -3
  30. package/dist/useProofCapability.cjs +104 -0
  31. package/dist/useProofCapability.cjs.map +1 -0
  32. package/dist/useProofCapability.d.cts +27 -0
  33. package/dist/useProofCapability.d.ts +27 -0
  34. package/dist/useProofCapability.js +4 -0
  35. package/dist/useProofCapability.js.map +1 -0
  36. package/dist/useZKTLS.cjs +7 -195
  37. package/dist/useZKTLS.cjs.map +1 -1
  38. package/dist/useZKTLS.d.cts +10 -230
  39. package/dist/useZKTLS.d.ts +10 -230
  40. package/dist/useZKTLS.js +1 -1
  41. package/dist/useZKTLSContext.cjs +0 -5
  42. package/dist/useZKTLSContext.cjs.map +1 -1
  43. package/dist/useZKTLSContext.d.cts +0 -2
  44. package/dist/useZKTLSContext.d.ts +0 -2
  45. package/dist/useZKTLSContext.js +2 -2
  46. package/package.json +15 -4
  47. package/skills/extension-react-integration/SKILL.md +16 -73
  48. package/dist/chunk-AMJXMSDV.js.map +0 -1
  49. package/dist/chunk-TAS2MB4S.js +0 -301
  50. package/dist/chunk-TAS2MB4S.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useZKTLS.ts"],"names":[],"mappings":"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","file":"chunk-JU2T2TGY.js","sourcesContent":["/**\n * @fileoverview React hook and utilities for PeerAuth browser extension integration.\n *\n * The PeerAuth extension (formerly ZK-TLS) provides secure proof generation\n * for off-chain payment verification. This module provides:\n *\n * 1. Type definitions for extension API (window.peer)\n * 2. useZKTLS hook for React components\n * 3. The trusted-execution on-ramp helpers\n *\n * @remarks\n * The extension exposes `window.peer` (or deprecated `window.zktls`) for:\n * - Requesting connection (OAuth-like flow)\n * - Authenticating with payment providers (buyer trusted-execution capture)\n * - Fetching transaction metadata\n *\n * The Reclaim proof path (generateProof, proof encoding, proof types) was\n * removed in the next major version.\n *\n * @see {@link ZKTLSContext} for the React context provider\n */\n\nimport { useEffect, useMemo, useState } from \"react\"\nimport { type Hex } from \"viem\"\nimport { type ActionType, type PlatformKey, type ProviderKey } from \"@provex/utils/payment\"\n\ndeclare global {\n interface ZKTLS {\n requestConnection(): Promise<boolean>\n checkConnectionStatus(): Promise<Status>\n getVersion(): Promise<string>\n authenticate(inputs: AuthenticationInputs): Promise<void>\n openSidebar(route: string): Promise<void>\n onMetadataMessage(fn: (data: MetadataMessageResponse) => void): () => void\n /**\n * Trusted-execution on-ramp (PeerAuth v3+). The extension opens the\n * buyer's bank in a new tab, intercepts the authenticated session, calls\n * the zkp2p trusted-execution attestation service, and returns\n * ready-to-send `fulfillIntent` calldata through the callback. Replaces\n * the retired Reclaim proof path.\n *\n * Optional: older extension builds predate the on-ramp API, so guard\n * with a runtime presence check (`supportsOnramp`) before calling.\n *\n * @param queryString serialized on-ramp params (see {@link buildOnrampQuery})\n * @param callback invoked once the prepared transaction is ready\n */\n onramp?(\n queryString: string,\n callback: (result: PeerOnrampPreparedTransaction) => void,\n ): void\n /** Retrieve a previously prepared on-ramp transaction by intent hash. */\n getOnrampTransaction?(intentHash: string): Promise<PeerOnrampPreparedTransaction | null>\n }\n interface Window {\n /** @deprecated Use window.peer instead */\n zktls: ZKTLS | undefined\n /** PeerAuth extension API (replaces window.zktls) */\n peer: ZKTLS | undefined\n }\n}\n\n/** the status of the PeerAuth extension */\nexport type Status = 'connected' | 'disconnected' | 'pending'\n\n/** the individual metadata message sent by the PeerAuth extension */\nexport interface MetadataMessage {\n amount: number\n /** Currency code (e.g., 'USD', 'EUR') */\n currency?: string\n /**\n * Date of the transaction.\n * Can be a string in YYYYMMDD format, a number (YYYYMMDD or timestamp), or Date object.\n * The actual type depends on the extension implementation.\n */\n date: string | number | Date\n hidden: boolean\n originalIndex: number\n paymentId: number\n recipient: string\n}\n\n/** the response from the PeerAuth extension when a metadata message is received */\nexport interface MetadataMessageResponse {\n expiresAt: number\n platform: ProviderKey\n requestId: string\n metadata: MetadataMessage[]\n /**\n * Present only when `authenticate` was called with `captureMode: 'buyerTee'`.\n * Carries the bank session already encrypted toward the enclave — feed\n * `encryptedSessionMaterial` (plus the matched transaction's index) to the\n * trusted-execution verify endpoint.\n */\n buyerTeeCapture?: {\n encryptedSessionMaterial: string\n }\n /** Error surfaced by the extension during capture (e.g. missing platform). */\n errorMessage?: string\n}\n\n/** the inputs for the authenticate function */\nexport type AuthenticationInputs = {\n actionType: ActionType\n platform: PlatformKey\n /**\n * Session capture mode. `'buyerTee'` makes the extension intercept the bank\n * session and return it encrypted toward the trusted-execution enclave in\n * `MetadataMessageResponse.buyerTeeCapture`. The extension defaults the\n * attestation service URL when this is set.\n */\n captureMode?: 'buyerTee' | 'sellerCredential'\n /**\n * Enclave the extension establishes the Buyer TEE session with and encrypts\n * the captured session material toward (zkp2p 0.6.0 spec §7). Pass explicitly\n * for newer providers — relying on the extension's built-in default is what\n * left non-Revolut rails with \"no buyerTee capture\".\n */\n attestationServiceUrl?: string\n /** Attestation action type; defaults to `actionType` when omitted (spec §2). */\n attestationActionType?: ActionType\n}\n\n/**\n * Ready-to-send transaction the PeerAuth extension hands back once the\n * trusted-execution attestation service has signed the payment. The `data`\n * already embeds the enclave attestation, so the client signs and sends it\n * verbatim — no client-side proof encoding or gate re-signing.\n */\nexport type PeerOnrampPreparedTransaction = {\n intentHash: Hex\n status: 'calldata_ready'\n transaction: {\n to: Hex\n data: Hex\n /** Wei, as a decimal string (the extension keeps it out of JSON-number range). */\n value: string\n chainId: number\n }\n}\n\n/**\n * Structured on-ramp inputs. The extension looks the intent up from\n * `intentHash` (and the optional context fields) to resolve the chain,\n * contracts, and payment platform — we never pass contract addresses.\n */\nexport type PeerExtensionOnrampParams = {\n intentHash: string\n referrer?: string\n referrerLogo?: string\n inputCurrency?: string\n inputAmount?: string | number\n paymentPlatform?: string\n depositId?: string | number | bigint\n toToken?: string\n amountUsdc?: string | number | bigint\n recipientAddress?: string\n}\n\n/**\n * Serialize on-ramp params into the query string the extension's `onramp`\n * entry point expects. Mirrors the mapping `@zkp2p/sdk`'s\n * `peerExtensionSdk.onramp` performs: every defined field becomes one query\n * parameter, `undefined`/`null` fields are dropped, and `bigint` values are\n * stringified.\n */\nexport const buildOnrampQuery = (params: PeerExtensionOnrampParams): string => {\n const search = new URLSearchParams()\n Object.entries(params).forEach(([key, value]) => {\n if (value === undefined || value === null) return\n search.set(key, String(value))\n })\n return search.toString()\n}\n\nexport const extensionUrl = 'https://chromewebstore.google.com/detail/ijpgccednehjpeclfcllnjjcmiohdjih'\n\n/** React hook to interact with the PeerAuth extension */\nexport const useZKTLS = () => {\n const [isInitialized, setIsInitialized] = useState(false)\n useEffect(() => {\n const setup = () => {\n setIsInitialized(true)\n }\n const eventName = 'peer#initialized'\n if (typeof window.peer !== 'undefined') {\n // api is available\n setup()\n } else {\n window.addEventListener(eventName, setup)\n return () => {\n window.removeEventListener(eventName, setup)\n }\n }\n }, [])\n return useMemo(() => {\n return {\n /** check if the PeerAuth extension is initialized */\n isInitialized,\n /** request a connection to the PeerAuth extension */\n requestConnection: async () => {\n if (!window.peer) return false\n return window.peer!.requestConnection()\n },\n /**\n * check the connection status of the PeerAuth extension\n * @returns the connection status\n */\n checkConnectionStatus: async () => {\n return await window.peer!.checkConnectionStatus()\n },\n /** get the version of the PeerAuth extension */\n getVersion: async () => {\n return await window.peer?.getVersion()\n },\n /** authenticate with the PeerAuth extension */\n authenticate: async (inputs: AuthenticationInputs) => {\n // Create promise first to avoid race condition where message arrives before resolver is assigned\n let resolve: (value: MetadataMessageResponse) => void\n const promise = new Promise<MetadataMessageResponse>((resolver) => {\n resolve = resolver\n })\n\n // Now set up the listener - resolve is guaranteed to be the real resolver\n const unsub = window.peer!.onMetadataMessage((data) => {\n if (`transfer_${data.platform}` === inputs.actionType) {\n resolve(data)\n }\n })\n\n await window.peer!.authenticate(inputs)\n\n return await promise.then((data) => {\n unsub()\n return data\n })\n },\n /** subscribe to metadata messages */\n onMetadataMessage: (fn: (data: any) => void) => {\n return window.peer!.onMetadataMessage(fn)\n },\n /** open the PeerAuth sidebar */\n openSidebar: async (route: string) => {\n return await window.peer!.openSidebar(route)\n },\n /* v8 ignore start -- @preserve\n * Excluded from coverage on purpose: the extension's `window.peer.onramp`\n * API is retired (the buyer flow verifies through the zkp2p enclave\n * adapter instead) and no caller in this repository invokes\n * supportsOnramp, onramp or getOnrampTransaction. Testing them would\n * assert behaviour production never reaches. */\n /** Whether the connected extension exposes the trusted-execution on-ramp. */\n supportsOnramp: (): boolean => typeof window.peer?.onramp === 'function',\n /**\n * Begin the trusted-execution on-ramp and resolve with ready-to-send\n * `fulfillIntent` calldata once the extension has intercepted the bank\n * session and the zkp2p attestation service has signed the payment.\n *\n * The underlying extension API is callback-only with no error channel:\n * the extension surfaces progress and failures in its own sidebar/tab,\n * and only invokes the callback on success. The returned promise\n * therefore stays pending until the prepared transaction arrives — the\n * caller drives the user toward the PeerAuth window and may offer a\n * cancel affordance rather than racing a timeout against a buyer who is\n * still logging into their bank.\n */\n onramp: async (\n params: PeerExtensionOnrampParams,\n ): Promise<PeerOnrampPreparedTransaction> => {\n if (typeof window.peer?.onramp !== 'function') {\n throw new Error(\n 'This PeerAuth extension version does not support the trusted-execution on-ramp. Please update the extension.',\n )\n }\n const queryString = buildOnrampQuery(params)\n return new Promise<PeerOnrampPreparedTransaction>((resolve) => {\n window.peer!.onramp!(queryString, resolve)\n })\n },\n /**\n * Retrieve a previously prepared on-ramp transaction by intent hash.\n * Returns null when the extension lacks the API or has nothing prepared\n * yet — usable as a poll alongside {@link onramp} to recover a result\n * if the one-shot callback is missed.\n */\n getOnrampTransaction: async (\n intentHash: string,\n ): Promise<PeerOnrampPreparedTransaction | null> => {\n if (typeof window.peer?.getOnrampTransaction !== 'function') return null\n return window.peer.getOnrampTransaction(intentHash)\n },\n /* v8 ignore stop -- @preserve */\n }\n }, [isInitialized])\n}\n"]}
@@ -0,0 +1,59 @@
1
+ import { DEFAULT_INJECTION_WAIT_MS, resolveProofCapability, detectMobileDevice } from './chunk-JMVW4H4H.js';
2
+ import { useState, useEffect } from 'react';
3
+
4
+ var PEER_INITIALIZED_EVENT = "peer#initialized";
5
+ var readIsMobileDevice = () => {
6
+ if (typeof navigator === "undefined") return false;
7
+ return detectMobileDevice({
8
+ userAgent: navigator.userAgent ?? "",
9
+ maxTouchPoints: navigator.maxTouchPoints ?? 0,
10
+ platform: navigator.platform ?? ""
11
+ });
12
+ };
13
+ var readExtensionVersion = async () => {
14
+ try {
15
+ const version = await window.peer?.getVersion?.();
16
+ return version ? version : null;
17
+ } catch {
18
+ return null;
19
+ }
20
+ };
21
+ var useProofCapability = ({
22
+ injection = "not-guaranteed",
23
+ injectionWaitMs = DEFAULT_INJECTION_WAIT_MS
24
+ } = {}) => {
25
+ const [isMobileDevice, setIsMobileDevice] = useState(false);
26
+ const [isPeerPresent, setIsPeerPresent] = useState(false);
27
+ const [hasInjectionWaitElapsed, setHasInjectionWaitElapsed] = useState(false);
28
+ const [version, setVersion] = useState(void 0);
29
+ useEffect(() => {
30
+ setIsMobileDevice(readIsMobileDevice());
31
+ }, []);
32
+ useEffect(() => {
33
+ let isCancelled = false;
34
+ const markPresent = () => {
35
+ setIsPeerPresent(true);
36
+ void readExtensionVersion().then((found) => {
37
+ if (!isCancelled) setVersion(found);
38
+ });
39
+ };
40
+ if (typeof window.peer !== "undefined") {
41
+ markPresent();
42
+ return () => {
43
+ isCancelled = true;
44
+ };
45
+ }
46
+ window.addEventListener(PEER_INITIALIZED_EVENT, markPresent);
47
+ const timer = setTimeout(() => setHasInjectionWaitElapsed(true), injectionWaitMs);
48
+ return () => {
49
+ isCancelled = true;
50
+ clearTimeout(timer);
51
+ window.removeEventListener(PEER_INITIALIZED_EVENT, markPresent);
52
+ };
53
+ }, [injectionWaitMs]);
54
+ return resolveProofCapability({ isMobileDevice, isPeerPresent, hasInjectionWaitElapsed, injection, version });
55
+ };
56
+
57
+ export { useProofCapability };
58
+ //# sourceMappingURL=chunk-VQFWUZNJ.js.map
59
+ //# sourceMappingURL=chunk-VQFWUZNJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useProofCapability.ts"],"names":[],"mappings":";;;AAsBA,IAAM,sBAAA,GAAyB,kBAAA;AAG/B,IAAM,qBAAqB,MAAe;AACxC,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,EAAa,OAAO,KAAA;AAC7C,EAAA,OAAO,kBAAA,CAAmB;AAAA,IACxB,SAAA,EAAW,UAAU,SAAA,IAAa,EAAA;AAAA,IAClC,cAAA,EAAgB,UAAU,cAAA,IAAkB,CAAA;AAAA,IAC5C,QAAA,EAAU,UAAU,QAAA,IAAY;AAAA,GACjC,CAAA;AACH,CAAA;AAGA,IAAM,uBAAuB,YAAoC;AAC/D,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,IAAA,EAAM,UAAA,IAAa;AAChD,IAAA,OAAO,UAAU,OAAA,GAAU,IAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;AAYO,IAAM,qBAAqB,CAAC;AAAA,EACjC,SAAA,GAAY,gBAAA;AAAA,EACZ,eAAA,GAAkB;AACpB,CAAA,GAA+B,EAAC,KAAuB;AACrD,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAS,KAAK,CAAA;AAC1D,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AACxD,EAAA,MAAM,CAAC,uBAAA,EAAyB,0BAA0B,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5E,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAoC,MAAS,CAAA;AAE3E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,iBAAA,CAAkB,oBAAoB,CAAA;AAAA,EACxC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAA,GAAc,KAAA;AAClB,IAAA,MAAM,cAAc,MAAM;AACxB,MAAA,gBAAA,CAAiB,IAAI,CAAA;AACrB,MAAA,KAAK,oBAAA,EAAqB,CAAE,IAAA,CAAK,CAAC,KAAA,KAAU;AAC1C,QAAA,IAAI,CAAC,WAAA,EAAa,UAAA,CAAW,KAAK,CAAA;AAAA,MACpC,CAAC,CAAA;AAAA,IACH,CAAA;AACA,IAAA,IAAI,OAAO,MAAA,CAAO,IAAA,KAAS,WAAA,EAAa;AACtC,MAAA,WAAA,EAAY;AACZ,MAAA,OAAO,MAAM;AACX,QAAA,WAAA,GAAc,IAAA;AAAA,MAChB,CAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,wBAAwB,WAAW,CAAA;AAC3D,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,0BAAA,CAA2B,IAAI,GAAG,eAAe,CAAA;AAChF,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,MAAA,CAAO,mBAAA,CAAoB,wBAAwB,WAAW,CAAA;AAAA,IAChE,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,eAAe,CAAC,CAAA;AAEpB,EAAA,OAAO,uBAAuB,EAAE,cAAA,EAAgB,eAAe,uBAAA,EAAyB,SAAA,EAAW,SAAS,CAAA;AAC9G","file":"chunk-VQFWUZNJ.js","sourcesContent":["/**\n * @fileoverview React hook that reports whether this browser can make a\n * Provex payment proof. See `capability.ts` for the decision rules.\n */\n\nimport { useEffect, useState } from 'react'\nimport {\n DEFAULT_INJECTION_WAIT_MS,\n detectMobileDevice,\n resolveProofCapability,\n type InjectionGuarantee,\n type ProofCapability,\n} from './capability.js'\n\n/** Options for {@link useProofCapability}. */\nexport interface UseProofCapabilityOptions {\n /** Default `not-guaranteed`. Pass `guaranteed` on origins where the extension injects. */\n injection?: InjectionGuarantee\n /** Wait before a missing `window.peer` counts as missing. Default 1500 ms. */\n injectionWaitMs?: number\n}\n\nconst PEER_INITIALIZED_EVENT = 'peer#initialized'\n\n/** Read the device signals from `navigator`. Returns false on the server. */\nconst readIsMobileDevice = (): boolean => {\n if (typeof navigator === 'undefined') return false\n return detectMobileDevice({\n userAgent: navigator.userAgent ?? '',\n maxTouchPoints: navigator.maxTouchPoints ?? 0,\n platform: navigator.platform ?? '',\n })\n}\n\n/** Ask the extension for its version. Returns null when it cannot answer. */\nconst readExtensionVersion = async (): Promise<string | null> => {\n try {\n const version = await window.peer?.getVersion?.()\n return version ? version : null\n } catch {\n return null\n }\n}\n\n/**\n * Report whether this browser can make a Provex payment proof.\n *\n * The first render, on the server and in the browser, assumes a desktop device\n * with no extension seen. The real values load in effects, so server and\n * client markup match.\n *\n * @param options - injection guarantee and wait time\n * @returns the current {@link ProofCapability}\n */\nexport const useProofCapability = ({\n injection = 'not-guaranteed',\n injectionWaitMs = DEFAULT_INJECTION_WAIT_MS,\n}: UseProofCapabilityOptions = {}): ProofCapability => {\n const [isMobileDevice, setIsMobileDevice] = useState(false)\n const [isPeerPresent, setIsPeerPresent] = useState(false)\n const [hasInjectionWaitElapsed, setHasInjectionWaitElapsed] = useState(false)\n const [version, setVersion] = useState<string | null | undefined>(undefined)\n\n useEffect(() => {\n setIsMobileDevice(readIsMobileDevice())\n }, [])\n\n useEffect(() => {\n let isCancelled = false\n const markPresent = () => {\n setIsPeerPresent(true)\n void readExtensionVersion().then((found) => {\n if (!isCancelled) setVersion(found)\n })\n }\n if (typeof window.peer !== 'undefined') {\n markPresent()\n return () => {\n isCancelled = true\n }\n }\n window.addEventListener(PEER_INITIALIZED_EVENT, markPresent)\n const timer = setTimeout(() => setHasInjectionWaitElapsed(true), injectionWaitMs)\n return () => {\n isCancelled = true\n clearTimeout(timer)\n window.removeEventListener(PEER_INITIALIZED_EVENT, markPresent)\n }\n }, [injectionWaitMs])\n\n return resolveProofCapability({ isMobileDevice, isPeerPresent, hasInjectionWaitElapsed, injection, version })\n}\n"]}
@@ -167,5 +167,5 @@ function createExtensionEnclaveAdapter(config) {
167
167
  }
168
168
 
169
169
  export { DEFAULT_ATTESTATION_SERVICE_URL, createExtensionEnclaveAdapter };
170
- //# sourceMappingURL=chunk-AMJXMSDV.js.map
171
- //# sourceMappingURL=chunk-AMJXMSDV.js.map
170
+ //# sourceMappingURL=chunk-WSLUOLBW.js.map
171
+ //# sourceMappingURL=chunk-WSLUOLBW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/extensionEnclave.ts"],"names":[],"mappings":"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","file":"chunk-WSLUOLBW.js","sourcesContent":["/**\n * @fileoverview Extension + enclave CaptureAdapter (the v1 capture backend).\n *\n * Mirrors the live AuthenticateStep + GenerateProofStep enclave path\n * (AuthenticateStep.tsx:224-281, GenerateProofStep.tsx:237-251) reduced to\n * the buyerTee branch. Talks to an injected `window.peer`-shaped client so\n * the injected-PeerClient seam is preserved exactly.\n */\n\nimport { parseUnits } from 'viem'\nimport type { Hex } from 'viem'\nimport { providerConfigs, providerRequiresMetadataIndex } from '@provex/utils/payment'\nimport type { ProviderKey, SubProviderKey } from '@provex/utils/payment'\nimport type { ChainId } from '@provex/utils/chain'\nimport { convertTokenInputToCurrency } from '@provex/utils/conversionRates'\nimport {\n EnclaveCaptureUnavailable,\n EnclaveCaptureFailed,\n ManualSelectionRequired,\n PaymentAlreadyUsedError,\n asBuyerTeeProof,\n requestBuyerTeeAttestation,\n type CaptureAdapter,\n type CaptureCandidate,\n type CaptureHandle,\n type CaptureRequest,\n type SelectTransaction,\n type VerifiedAttestation,\n} from '@provex/proving'\n\n/** Minimal shape of the metadata response the extension posts back. */\ninterface MetadataResponse {\n platform: string\n metadata: CaptureCandidate[]\n buyerTeeCapture?: { encryptedSessionMaterial: string }\n}\n\n/**\n * zkp2p enclave used for Buyer TEE capture when the caller doesn't override\n * `ExtensionEnclaveAdapterConfig.attestationServiceUrl`. Matches the gate's\n * `TEE_ATTESTATION_URL` — the enclave the gate forwards to for the re-sign.\n */\nexport const DEFAULT_ATTESTATION_SERVICE_URL = 'https://attestation-service.zkp2p.xyz'\n\n/** Minimal slice of `window.peer` this adapter depends on. */\nexport interface PeerClient {\n authenticate(inputs: {\n actionType: string\n platform: string\n captureMode: 'buyerTee'\n /**\n * Enclave the extension establishes the Buyer TEE session with and encrypts\n * the session material toward. Required by the zkp2p 0.6.0 spec\n * (onramp-llm.md §7) — without it the extension performs no Buyer TEE\n * capture and falls back to the retired Reclaim path.\n */\n attestationServiceUrl?: string\n /** Attestation action type; defaults to `actionType` when omitted (spec §2). */\n attestationActionType?: string\n /** Attestation platform; defaults to `platform` when omitted. */\n attestationPlatform?: string\n }): Promise<void>\n onMetadataMessage(fn: (data: MetadataResponse) => void): () => void\n /** Current extension connection status; `'connected'` means ready. */\n checkConnectionStatus(): Promise<string>\n /** Prompt the user to connect the extension. Resolves true when approved. */\n requestConnection(): Promise<boolean>\n}\n\nexport interface ExtensionEnclaveAdapterConfig {\n /** Resolve the live peer client (e.g. `() => window.peer`). */\n getPeer: () => PeerClient | undefined\n /** Gate/attestation origin handed to requestBuyerTeeAttestation (the re-sign hop). */\n attestationBaseUrl: string\n /**\n * Enclave URL passed to the extension's `authenticate` so it performs the\n * Buyer TEE capture (zkp2p 0.6.0 spec §7). This is the zkp2p enclave the\n * session material is encrypted toward — the SAME enclave the gate forwards\n * to for the re-sign — NOT our gate's re-sign origin (`attestationBaseUrl`).\n * Defaults to {@link DEFAULT_ATTESTATION_SERVICE_URL} when omitted; set it to\n * override the enclave for local testing.\n */\n attestationServiceUrl?: string\n /**\n * Milliseconds before awaitMetadata rejects if the extension never posts a\n * matching metadata message. Mirrors the 60-second timeout in\n * AuthenticateStep.tsx:232-236. Defaults to 60000.\n */\n timeoutMs?: number\n}\n\n/** One bank-history row the attestation may settle this order with. */\ninterface PaymentToTry {\n /** The row's index in the captured bank history. */\n index: number\n /** The row's provider attestation params (spec §4), forwarded to the enclave. */\n params?: Record<string, string | number | boolean>\n}\n\ninterface EnclaveMaterial {\n encryptedSessionMaterial: string\n /**\n * Payments to attest, in order. One entry for a single amount match or a\n * buyer's pick; every amount match (bank order, newest first) when several\n * payments share the order's amount. Bank dates resolve only to the day,\n * so they cannot tell same-day payments apart; the gate can, by refusing a\n * payment that already settled an order.\n */\n payments: PaymentToTry[]\n}\n\n/**\n * Normalize an amount from the extension into a bigint in token base units.\n *\n * This is the only amount normalizer; the retired `findMetadataMessage` was removed.\n * The extension sends amounts in different formats per provider:\n * - Venmo: `\"- $1.00\"` (formatted dollar string — isFormattedDollars=true)\n * - Others: `100` (integer cents) or `1.00` (decimal dollars)\n *\n * When `centIsInt` is true and the raw value has no currency symbol or decimal\n * point (i.e. it arrived as integer cents), we divide by 100 to convert to\n * the same base unit scale that parseUnits applied.\n */\nfunction normalizeAmount(amount: number | string, decimals: number, centIsInt: boolean): bigint {\n const raw = String(amount)\n const isFormattedDollars = /[^0-9\\-]/.test(raw) && raw.includes('.')\n const cleaned = raw.replace(/[^0-9.]/g, '')\n let value = parseUnits(cleaned, decimals)\n if (centIsInt && !isFormattedDollars) {\n value = value / 100n\n }\n return value\n}\n\n/**\n * The outcome of matching the bank history against the intent's amount.\n *\n * - `single`: exactly one payment has the intent's amount.\n * - `several`: more than one does (e.g. two $2 Zelle payments to the same\n * seller). All are tried in bank order; the gate refuses any that already\n * settled an order. Taking only the first sent an already-used payment and\n * failed the order with \"This payment has already been verified\".\n * - `none`: no payment has that amount; the buyer picks from the full list.\n */\ntype PaymentMatch =\n | { kind: 'single'; candidate: CaptureCandidate }\n | { kind: 'several'; candidates: CaptureCandidate[] }\n | { kind: 'none' }\n\n/**\n * Match the bank history against the intent's FIAT currency amount,\n * accepting a positive or negative sign.\n *\n * Uses the same pipeline as the live AuthenticateStep.tsx:147-163:\n * 1. Convert token amount → fiat via convertTokenInputToCurrency.\n * 2. Normalize each candidate with the provider's centIsInt flag.\n * 3. Match equality (or negated equality for debit/refund signs).\n *\n * `centIsInt` is sourced from `providerConfigs(chainId)[provider]?.amountInCents`\n * which mirrors how the live caller derives it:\n * `targetProvider?.amountInCents ?? false`\n * where `targetProvider` is resolved via `useProvider` from the same\n * providerConfigs call. Sub-providers (e.g. zelle-chase) do NOT currently\n * set amountInCents, so the parent-provider lookup is sufficient.\n *\n * Matches keep the bank's own order (newest first for the hosted templates).\n */\nfunction matchPayment(\n candidates: CaptureCandidate[],\n request: CaptureRequest,\n): PaymentMatch {\n const currencyAmount = convertTokenInputToCurrency({\n token: request.token,\n amountOutInt: BigInt(request.intent.amount),\n rate: BigInt(request.intent.conversionRate),\n })\n\n // Resolve centIsInt from the provider config — same source as the live UI.\n // The cast is safe: CaptureRequest.chainId must be a supported chain ID.\n const configs = providerConfigs(request.chainId as ChainId)\n const providerConfig = configs[request.provider as keyof typeof configs]\n const centIsInt = (providerConfig as { amountInCents?: boolean } | undefined)?.amountInCents ?? false\n\n const matches = candidates.filter((candidate) => {\n const converted = normalizeAmount(candidate.amount, request.token.decimals, centIsInt)\n return converted === currencyAmount || converted === currencyAmount * -1n\n })\n if (matches.length === 0) return { kind: 'none' }\n if (matches.length === 1) return { kind: 'single', candidate: matches[0]! }\n return { kind: 'several', candidates: matches }\n}\n\n/** The bank-history row as a payment to try. */\nfunction toPaymentToTry(candidate: CaptureCandidate): PaymentToTry {\n return { index: candidate.originalIndex, params: candidate.params }\n}\n\n/**\n * The payments to attest, in order: the amount matches, else the buyer's pick\n * from the whole history. Throws {@link ManualSelectionRequired} when a pick is\n * needed but no resolver was supplied.\n */\nasync function resolvePaymentsToTry({\n match,\n history,\n selectTransaction,\n}: {\n match: PaymentMatch\n history: CaptureCandidate[]\n selectTransaction?: SelectTransaction\n}): Promise<PaymentToTry[]> {\n if (match.kind === 'single') return [toPaymentToTry(match.candidate)]\n if (match.kind === 'several') return match.candidates.map(toPaymentToTry)\n if (!selectTransaction) throw new ManualSelectionRequired(history)\n const pickedIndex = await selectTransaction(history)\n const picked = history.find((row) => row.originalIndex === pickedIndex)\n return [{ index: pickedIndex, params: picked?.params }]\n}\n\n/**\n * Wait for the metadata message the extension posts for `platform`.\n *\n * Races a configurable timeout (default 60s) against the metadata event,\n * mirroring AuthenticateStep.tsx:232-236. The unsubscribe function is always\n * called on resolution, timeout, or error — no listener leaks.\n *\n * @param peer - Injected extension client.\n * @param platform - The platform handed to `authenticate()` (e.g. 'zelle').\n * Echoed back when the extension could not load a template.\n * @param actionType - The capture action type (e.g. 'transfer_venmo'). Its\n * parent is echoed back when the template DID load. Both are accepted; see\n * the guard below.\n * @param timeoutMs - Reject after this long with no matching message.\n */\nfunction awaitMetadata(\n peer: PeerClient,\n platform: string,\n actionType: string,\n timeoutMs: number,\n): Promise<MetadataResponse> {\n return new Promise<MetadataResponse>((resolve, reject) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n\n const unsub = peer.onMetadataMessage((data) => {\n // The extension echoes the platform from one of two places:\n //\n // - Template loaded (success): the TEMPLATE's own `metadata.platform`.\n // - Template fetch failed: there is no template, so it echoes the\n // platform we passed, alongside an errorMessage such as\n // 'Failed to fetch provider template: 404'.\n //\n // With hosted templates the two agree (generic Zelle: 'zelle' both\n // ways). Accepting either still guards against a template whose\n // metadata.platform is the parent rail rather than the platform we\n // passed, so a successful capture is never dropped into a 60s timeout.\n const echoesCapturePlatform = data.platform === platform\n const echoesActionTypeParent = `transfer_${data.platform}` === actionType\n if (echoesCapturePlatform || echoesActionTypeParent) {\n clearTimeout(timeoutId)\n unsub()\n resolve(data)\n }\n })\n\n timeoutId = setTimeout(() => {\n unsub()\n reject(new Error(`Enclave capture timed out for platform \"${platform}\"`))\n }, timeoutMs)\n })\n}\n\nexport function createExtensionEnclaveAdapter(config: ExtensionEnclaveAdapterConfig): CaptureAdapter {\n const timeoutMs = config.timeoutMs ?? 60000\n\n return {\n kind: 'extension-enclave',\n\n async authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle> {\n const peer = config.getPeer()\n if (!peer) {\n throw new EnclaveCaptureUnavailable(request.platform)\n }\n\n // Ensure the extension is connected BEFORE authenticate. A cold\n // authenticate() on a fresh page returns errorMessage \"Peer extension\n // connection required\" with no buyerTeeCapture — which trips the\n // enclave-unavailable path. The legacy AuthenticateStep connects first\n // (and onramp-llm.md §3 `ensurePeerReady` requires it); the adapter must\n // too. This is why a warm local tab worked but a fresh prod session failed.\n const status = await peer.checkConnectionStatus()\n if (status !== 'connected') {\n await peer.requestConnection()\n }\n\n // Register the listener BEFORE authenticate fires to avoid a race where\n // the extension posts metadata before the listener is attached.\n const metadataPromise = awaitMetadata(peer, request.platform, request.actionType, timeoutMs)\n await peer.authenticate({\n actionType: request.actionType,\n platform: request.platform,\n captureMode: 'buyerTee',\n // Required for the extension to actually perform the Buyer TEE capture\n // (zkp2p 0.6.0 spec §7). Omitting these is why non-Revolut rails got\n // \"no buyerTee capture\" and dead-ended on the retired Reclaim path.\n attestationServiceUrl: config.attestationServiceUrl ?? DEFAULT_ATTESTATION_SERVICE_URL,\n // The attestation route equals the capture pair for every rail today\n // (generic Zelle included: zelle/transfer_zelle_chase). No template is\n // passed: extension 0.6.7 always fetches it from api.zkp2p.xyz and\n // ignores a page-supplied one, so the capture pair must be hosted.\n attestationActionType: request.attestationActionType ?? request.actionType,\n attestationPlatform: request.attestationPlatform ?? request.platform,\n })\n const response = await metadataPromise\n\n // No buyerTee capture ⇒ the extension didn't produce an encrypted enclave\n // session. Log WHY before failing — the extension may have surfaced an\n // errorMessage, or returned metadata-only (no capture) for this platform.\n if (!response.buyerTeeCapture) {\n const errorMessage = (response as { errorMessage?: unknown }).errorMessage\n console.warn(\n '[extensionEnclave] no buyerTeeCapture for',\n request.platform,\n '— response keys:', Object.keys(response),\n '| errorMessage:', errorMessage,\n '| metadata rows:', Array.isArray(response.metadata) ? response.metadata.length : '(none)',\n '| buyerTeeCapture:', response.buyerTeeCapture,\n )\n // Two distinct failures share the \"no capture\" shape:\n // - The extension surfaced an `errorMessage` ⇒ it ATTEMPTED the capture\n // but something failed (most often: it could not reach the\n // attestation enclave — a buyer-side network / firewall / ad-blocker\n // issue). Surface that as a retryable EnclaveCaptureFailed carrying\n // the raw reason, so the user sees an actionable message instead of\n // \"provider unsupported\".\n // - No `errorMessage` ⇒ the provider produced no capture path at all\n // (non-migrated). That stays EnclaveCaptureUnavailable, the\n // Reclaim-fallback signal.\n if (typeof errorMessage === 'string' && errorMessage.trim().length > 0) {\n throw new EnclaveCaptureFailed(request.platform, errorMessage)\n }\n throw new EnclaveCaptureUnavailable(request.platform)\n }\n\n const material: EnclaveMaterial = {\n encryptedSessionMaterial: response.buyerTeeCapture.encryptedSessionMaterial,\n payments: await resolvePaymentsToTry({\n match: matchPayment(response.metadata, request),\n history: response.metadata,\n selectTransaction: options?.selectTransaction,\n }),\n }\n return { kind: 'extension-enclave', material }\n },\n\n async attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation> {\n if (handle.kind !== 'extension-enclave') {\n throw new Error(\n `ExtensionEnclaveAdapter.attest received a handle of kind \"${handle.kind}\"; expected \"extension-enclave\".`,\n )\n }\n const material = handle.material as EnclaveMaterial\n // Try each candidate payment in order. A payment that already settled an\n // order is refused by the gate (PaymentAlreadyUsedError) and skipped;\n // any other failure stops here and surfaces as-is.\n let alreadyUsed: PaymentAlreadyUsedError | undefined\n for (const payment of material.payments) {\n try {\n const attestation = await attestPayment({ payment, material, request })\n // verificationData = enclave signer (SubmitStep.tsx:473-474).\n return { attestation, verificationData: attestation.signer as Hex }\n } catch (error) {\n if (!(error instanceof PaymentAlreadyUsedError)) throw error\n alreadyUsed = error\n }\n }\n throw alreadyUsed ?? new Error('No bank payment was captured for this order.')\n },\n }\n\n /** Attest one candidate payment through the gate. */\n function attestPayment({\n payment,\n material,\n request,\n }: {\n payment: PaymentToTry\n material: EnclaveMaterial\n request: CaptureRequest\n }) {\n // The selected row's params ARE the attestation params (spec §5,\n // buildBuyerTeeProof) and MUST be forwarded — dropping them is why the\n // enclave 400s with \"Invalid <provider> query params\". The metadata\n // `index` is then merged in ONLY for providers that expect it (Venmo /\n // Cash App / Revolut / Zelle) and omitted for the rest (PayPal / Wise /\n // Monzo / Chime). See providerRequiresMetadataIndex.\n const params = {\n ...(payment.params ?? {}),\n ...(providerRequiresMetadataIndex(request.provider as ProviderKey | SubProviderKey)\n ? { index: payment.index }\n : {}),\n }\n return requestBuyerTeeAttestation({\n attestationBaseUrl: config.attestationBaseUrl,\n platform: request.attestationPlatform ?? request.platform,\n actionType: request.attestationActionType ?? request.actionType,\n encryptedSessionMaterial: material.encryptedSessionMaterial,\n params,\n verifyingContract: request.verifyingContract,\n intent: request.intent,\n })\n }\n}\n\n// Re-narrow export so consumers can validate proof shapes if they bypass the\n// adapter (kept for parity with the live SubmitStep check, SubmitStep.tsx:206).\nexport { asBuyerTeeProof }\n"]}
package/dist/index.cjs CHANGED
@@ -1,11 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
4
5
  var viem = require('viem');
5
- var time = require('@provex/utils/time');
6
6
  var payment = require('@provex/utils/payment');
7
7
  var conversionRates = require('@provex/utils/conversionRates');
8
- var jsxRuntime = require('react/jsx-runtime');
9
8
  var proving = require('@provex/proving');
10
9
 
11
10
  // src/useZKTLS.ts
@@ -17,80 +16,7 @@ var buildOnrampQuery = (params) => {
17
16
  });
18
17
  return search.toString();
19
18
  };
20
- var buildBuyerTeeNotaryRequest = ({
21
- encryptedSessionMaterial,
22
- index,
23
- actionType
24
- }) => ({
25
- metadata: [],
26
- actionType,
27
- id: `buyer-tee-${index}`,
28
- status: "success",
29
- timestamp: 0,
30
- // The runtime shape is a buyerTee proof, not a Reclaim NotarizedProof; the
31
- // submit step narrows it via `asBuyerTeeProof` before use.
32
- proof: {
33
- proofType: "buyerTee",
34
- encryptedSessionMaterial,
35
- params: { index }
36
- }
37
- });
38
19
  var extensionUrl = "https://chromewebstore.google.com/detail/ijpgccednehjpeclfcllnjjcmiohdjih";
39
- var byteArrayToHexString = (byteArray) => {
40
- return `0x${byteArray.map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
41
- };
42
- var parseExtensionProof = (proofObject) => {
43
- return {
44
- claimInfo: {
45
- provider: proofObject.claim.provider,
46
- parameters: proofObject.claim.parameters,
47
- context: proofObject.claim.context
48
- },
49
- signedClaim: {
50
- claim: {
51
- identifier: proofObject.claim.identifier,
52
- owner: proofObject.claim.owner,
53
- timestampS: proofObject.claim.timestampS,
54
- epoch: proofObject.claim.epoch
55
- },
56
- signatures: [byteArrayToHexString(Object.values(proofObject.signatures.claimSignature))]
57
- },
58
- isAppclipProof: false
59
- };
60
- };
61
- var PROOF_ENCODING = viem.parseAbiParameter("((string provider, string parameters, string context) claimInfo, ((bytes32 identifier, address owner, uint32 timestampS, uint32 epoch) claim, bytes[] signatures) signedClaim, bool isAppclipProof)");
62
- function encodeProofAsBytes(proof) {
63
- return viem.encodeAbiParameters([PROOF_ENCODING], [proof]);
64
- }
65
- var encodeTwoProofs = (proof1, proof2) => {
66
- return viem.encodeAbiParameters([PROOF_ENCODING, PROOF_ENCODING], [proof1, proof2]);
67
- };
68
- var encodeProof = ({ extensionProofs, subProvider, provider }) => {
69
- const reclaimProofs = extensionProofs.map((p) => parseExtensionProof(p));
70
- if (provider === "zelle") {
71
- const paymentMethod = payment.subProviderConfigs[provider][subProvider].paymentMethod;
72
- const twoEncoded = reclaimProofs.length === 2 ? encodeTwoProofs(reclaimProofs[0], reclaimProofs[1]) : encodeProofAsBytes(reclaimProofs[0]);
73
- const withPaymentMethod = encodeProofWithPaymentMethod({
74
- paymentMethod,
75
- proof: twoEncoded
76
- });
77
- return withPaymentMethod;
78
- }
79
- const [reclaimProof] = reclaimProofs;
80
- return encodeProofAsBytes(reclaimProof);
81
- };
82
- var encodeProofWithPaymentMethod = ({
83
- proof,
84
- paymentMethod
85
- }) => {
86
- return viem.encodePacked(["uint8", "bytes"], [paymentMethod, proof]);
87
- };
88
- var SeriesProgress = /* @__PURE__ */ ((SeriesProgress2) => {
89
- SeriesProgress2[SeriesProgress2["Generating"] = 0] = "Generating";
90
- SeriesProgress2[SeriesProgress2["Polling"] = 1] = "Polling";
91
- SeriesProgress2[SeriesProgress2["Complete"] = 2] = "Complete";
92
- return SeriesProgress2;
93
- })(SeriesProgress || {});
94
20
  var useZKTLS = () => {
95
21
  const [isInitialized, setIsInitialized] = react.useState(false);
96
22
  react.useEffect(() => {
@@ -108,36 +34,6 @@ var useZKTLS = () => {
108
34
  }
109
35
  }, []);
110
36
  return react.useMemo(() => {
111
- const fetchProofById = async (proofId) => {
112
- const proof = await window.peer.fetchProofById(proofId);
113
- if (proof && proof.notaryRequest) return proof.notaryRequest;
114
- const proofs = await window.peer.fetchProofs();
115
- const proofFromList = proofs.notaryRequests.find((p) => p.id === proofId);
116
- if (proofFromList) return proofFromList;
117
- return null;
118
- };
119
- const waitForProof = async (proofId, onCurrent) => {
120
- let notaryRequest = null;
121
- do {
122
- notaryRequest = await fetchProofById(proofId);
123
- onCurrent?.(notaryRequest);
124
- await time.sleep(1e3);
125
- } while (!notaryRequest || !notaryRequest.status || notaryRequest.status === "pending");
126
- if (notaryRequest.status === "failed" || notaryRequest.status === "expired" || notaryRequest.status === "cancelled" || notaryRequest.status === "error") {
127
- console.error("[waitForProof] proof terminal state:", JSON.stringify(notaryRequest, null, 2));
128
- const req = notaryRequest;
129
- const detail = req.errorMessage ?? req.error ?? req.message ?? notaryRequest.status;
130
- throw new Error(`Proof generation failed: ${detail}`);
131
- }
132
- return notaryRequest;
133
- };
134
- const generateProof = async (inputs) => {
135
- return await window.peer.generateProof({
136
- proofIndex: 0,
137
- ...inputs,
138
- intentHash: BigInt(inputs.intentHash).toString()
139
- });
140
- };
141
37
  return {
142
38
  /** check if the PeerAuth extension is initialized */
143
39
  isInitialized,
@@ -174,54 +70,20 @@ var useZKTLS = () => {
174
70
  return data;
175
71
  });
176
72
  },
177
- /** generate a proof */
178
- generateProof,
179
- /** wait for a proof to be generated */
180
- waitForProof,
181
- /** generate a proof series */
182
- generateProofSeries: async (inputs, { progress: progressCallback } = {}) => {
183
- const { platform, provider, intentHash, originalIndex } = inputs;
184
- const args = {
185
- platform: provider,
186
- originalIndex,
187
- intentHash
188
- };
189
- const isChase = platform === payment.extensionPlatforms.CHASE;
190
- const isZelle = provider === payment.providers.ZELLE;
191
- const expectedTotal = isZelle && isChase ? 2 : 1;
192
- progressCallback?.({ progress: 0 /* Generating */, step: 0, id: null, total: expectedTotal });
193
- const genProofA = await generateProof({ ...args, proofIndex: 0 });
194
- progressCallback?.({ progress: 1 /* Polling */, step: 0, id: genProofA.proofId, total: expectedTotal });
195
- const proofA = await waitForProof(genProofA.proofId);
196
- if (!isChase || !isZelle) {
197
- progressCallback?.({ progress: 2 /* Complete */, step: null, id: null, total: expectedTotal });
198
- return [proofA];
199
- }
200
- progressCallback?.({ progress: 0 /* Generating */, step: 1, id: null, total: expectedTotal });
201
- const genProofB = await generateProof({ ...args, proofIndex: 1 });
202
- progressCallback?.({ progress: 1 /* Polling */, step: 1, id: genProofB.proofId, total: expectedTotal });
203
- const proofB = await waitForProof(genProofB.proofId);
204
- progressCallback?.({ progress: 2 /* Complete */, step: null, id: null, total: expectedTotal });
205
- return [proofA, proofB];
206
- },
207
73
  /** subscribe to metadata messages */
208
74
  onMetadataMessage: (fn) => {
209
75
  return window.peer.onMetadataMessage(fn);
210
76
  },
211
- /** fetch a proof by its id */
212
- fetchProofById,
213
- /** fetch generated proofs */
214
- fetchGeneratedProofs: async (genProofResponses) => {
215
- return await Promise.all(genProofResponses.map((r) => fetchProofById(r.proofId)));
216
- },
217
- /** fetch all proofs */
218
- fetchProofs: async () => {
219
- return await window.peer.fetchProofs();
220
- },
221
77
  /** open the PeerAuth sidebar */
222
78
  openSidebar: async (route) => {
223
79
  return await window.peer.openSidebar(route);
224
80
  },
81
+ /* v8 ignore start -- @preserve
82
+ * Excluded from coverage on purpose: the extension's `window.peer.onramp`
83
+ * API is retired (the buyer flow verifies through the zkp2p enclave
84
+ * adapter instead) and no caller in this repository invokes
85
+ * supportsOnramp, onramp or getOnrampTransaction. Testing them would
86
+ * assert behaviour production never reaches. */
225
87
  /** Whether the connected extension exposes the trusted-execution on-ramp. */
226
88
  supportsOnramp: () => typeof window.peer?.onramp === "function",
227
89
  /**
@@ -258,46 +120,102 @@ var useZKTLS = () => {
258
120
  if (typeof window.peer?.getOnrampTransaction !== "function") return null;
259
121
  return window.peer.getOnrampTransaction(intentHash);
260
122
  }
123
+ /* v8 ignore stop -- @preserve */
261
124
  };
262
125
  }, [isInitialized]);
263
126
  };
264
- var findMetadataMessage = ({ metadata, intent, token, centIsInt = false }) => {
265
- const currencyAmount = conversionRates.convertTokenInputToCurrency({
266
- token,
267
- amountOutInt: intent.amount,
268
- rate: intent.conversionRate
127
+
128
+ // src/capability.ts
129
+ var MINIMUM_EXTENSION_VERSION = "0.6.0";
130
+ var DEFAULT_INJECTION_WAIT_MS = 1500;
131
+ var MOBILE_USER_AGENT_PATTERN = /Android|iPhone|iPad|iPod|Mobile|Windows Phone|IEMobile|BlackBerry|Opera Mini/i;
132
+ var detectMobileDevice = ({ userAgent, maxTouchPoints, platform }) => {
133
+ if (MOBILE_USER_AGENT_PATTERN.test(userAgent)) return true;
134
+ return platform === "MacIntel" && maxTouchPoints > 0;
135
+ };
136
+ var parseVersionParts = (version) => {
137
+ const parts = version.trim().replace(/^v/i, "").split(".");
138
+ return [0, 1, 2].map((index) => {
139
+ const parsed = Number.parseInt(parts[index] ?? "", 10);
140
+ return Number.isNaN(parsed) ? 0 : parsed;
269
141
  });
270
- const normalizeAmount2 = (amount) => {
271
- const raw = String(amount);
272
- const isFormattedDollars = /[^0-9\-]/.test(raw) && raw.includes(".");
273
- const cleaned = raw.replace(/[^0-9.]/g, "");
274
- let value = viem.parseUnits(cleaned, token.decimals);
275
- if (centIsInt && !isFormattedDollars) {
276
- value = value / 100n;
277
- }
278
- return value;
279
- };
280
- if (typeof window !== "undefined" && window.__FORCE_MANUAL_TX_SELECT__) {
281
- return null;
142
+ };
143
+ var isVersionAtLeast = ({ current, minimum }) => {
144
+ const currentParts = parseVersionParts(current);
145
+ const minimumParts = parseVersionParts(minimum);
146
+ const firstDifference = currentParts.findIndex((part, index) => part !== minimumParts[index]);
147
+ if (firstDifference === -1) return true;
148
+ return currentParts[firstDifference] > minimumParts[firstDifference];
149
+ };
150
+ var resolveProofCapability = ({
151
+ isMobileDevice,
152
+ isPeerPresent,
153
+ hasInjectionWaitElapsed,
154
+ injection,
155
+ version
156
+ }) => {
157
+ if (isMobileDevice) return { status: "mobile" };
158
+ if (!isPeerPresent && injection === "not-guaranteed") return { status: "unknown" };
159
+ if (!isPeerPresent && !hasInjectionWaitElapsed) return { status: "checking" };
160
+ if (!isPeerPresent) return { status: "no-extension" };
161
+ if (version === void 0) return { status: "checking" };
162
+ if (version === null) return { status: "unknown" };
163
+ if (!isVersionAtLeast({ current: version, minimum: MINIMUM_EXTENSION_VERSION })) {
164
+ return { status: "outdated", version };
282
165
  }
283
- const match = metadata.find((m) => {
284
- const converted = normalizeAmount2(m.amount);
285
- return converted === currencyAmount || converted === currencyAmount * -1n;
166
+ return { status: "ready", version };
167
+ };
168
+ var PEER_INITIALIZED_EVENT = "peer#initialized";
169
+ var readIsMobileDevice = () => {
170
+ if (typeof navigator === "undefined") return false;
171
+ return detectMobileDevice({
172
+ userAgent: navigator.userAgent ?? "",
173
+ maxTouchPoints: navigator.maxTouchPoints ?? 0,
174
+ platform: navigator.platform ?? ""
286
175
  });
287
- if (!match) {
288
- const hundredxMatch = metadata.find((m) => {
289
- const converted = normalizeAmount2(m.amount);
290
- const abs = converted < 0n ? -converted : converted;
291
- return abs === currencyAmount * 100n || abs * 100n === currencyAmount;
292
- });
293
- if (hundredxMatch) {
294
- console.warn(
295
- `[findMetadataMessage] tx[0] amount=${String(hundredxMatch.amount)} is 100x off from expected ${currencyAmount.toString()} \u2014 centIsInt is likely wrong for this provider (currently ${centIsInt})`
296
- );
297
- }
176
+ };
177
+ var readExtensionVersion = async () => {
178
+ try {
179
+ const version = await window.peer?.getVersion?.();
180
+ return version ? version : null;
181
+ } catch {
298
182
  return null;
299
183
  }
300
- return match;
184
+ };
185
+ var useProofCapability = ({
186
+ injection = "not-guaranteed",
187
+ injectionWaitMs = DEFAULT_INJECTION_WAIT_MS
188
+ } = {}) => {
189
+ const [isMobileDevice, setIsMobileDevice] = react.useState(false);
190
+ const [isPeerPresent, setIsPeerPresent] = react.useState(false);
191
+ const [hasInjectionWaitElapsed, setHasInjectionWaitElapsed] = react.useState(false);
192
+ const [version, setVersion] = react.useState(void 0);
193
+ react.useEffect(() => {
194
+ setIsMobileDevice(readIsMobileDevice());
195
+ }, []);
196
+ react.useEffect(() => {
197
+ let isCancelled = false;
198
+ const markPresent = () => {
199
+ setIsPeerPresent(true);
200
+ void readExtensionVersion().then((found) => {
201
+ if (!isCancelled) setVersion(found);
202
+ });
203
+ };
204
+ if (typeof window.peer !== "undefined") {
205
+ markPresent();
206
+ return () => {
207
+ isCancelled = true;
208
+ };
209
+ }
210
+ window.addEventListener(PEER_INITIALIZED_EVENT, markPresent);
211
+ const timer = setTimeout(() => setHasInjectionWaitElapsed(true), injectionWaitMs);
212
+ return () => {
213
+ isCancelled = true;
214
+ clearTimeout(timer);
215
+ window.removeEventListener(PEER_INITIALIZED_EVENT, markPresent);
216
+ };
217
+ }, [injectionWaitMs]);
218
+ return resolveProofCapability({ isMobileDevice, isPeerPresent, hasInjectionWaitElapsed, injection, version });
301
219
  };
302
220
  var ZKTLSContext = react.createContext(void 0);
303
221
  function useZKTLSContext() {
@@ -472,21 +390,17 @@ function createExtensionEnclaveAdapter(config) {
472
390
  }
473
391
  }
474
392
 
475
- exports.PROOF_ENCODING = PROOF_ENCODING;
476
- exports.SeriesProgress = SeriesProgress;
393
+ exports.DEFAULT_INJECTION_WAIT_MS = DEFAULT_INJECTION_WAIT_MS;
394
+ exports.MINIMUM_EXTENSION_VERSION = MINIMUM_EXTENSION_VERSION;
477
395
  exports.ZKTLSContext = ZKTLSContext;
478
396
  exports.ZKTLSProvider = ZKTLSProvider;
479
- exports.buildBuyerTeeNotaryRequest = buildBuyerTeeNotaryRequest;
480
397
  exports.buildOnrampQuery = buildOnrampQuery;
481
- exports.byteArrayToHexString = byteArrayToHexString;
482
398
  exports.createExtensionEnclaveAdapter = createExtensionEnclaveAdapter;
483
- exports.encodeProof = encodeProof;
484
- exports.encodeProofAsBytes = encodeProofAsBytes;
485
- exports.encodeProofWithPaymentMethod = encodeProofWithPaymentMethod;
486
- exports.encodeTwoProofs = encodeTwoProofs;
399
+ exports.detectMobileDevice = detectMobileDevice;
487
400
  exports.extensionUrl = extensionUrl;
488
- exports.findMetadataMessage = findMetadataMessage;
489
- exports.parseExtensionProof = parseExtensionProof;
401
+ exports.isVersionAtLeast = isVersionAtLeast;
402
+ exports.resolveProofCapability = resolveProofCapability;
403
+ exports.useProofCapability = useProofCapability;
490
404
  exports.useZKTLS = useZKTLS;
491
405
  exports.useZKTLSContext = useZKTLSContext;
492
406
  //# sourceMappingURL=index.cjs.map