@provex/extension-react 1.9.0 → 1.9.1-rc.20260612133336.a0dd0bf
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/ZKTLSContext.cjs.map +1 -1
- package/dist/ZKTLSContext.js +2 -2
- package/dist/adapters/extensionEnclave.cjs +32 -3
- package/dist/adapters/extensionEnclave.cjs.map +1 -1
- package/dist/adapters/extensionEnclave.d.cts +30 -2
- package/dist/adapters/extensionEnclave.d.ts +30 -2
- package/dist/adapters/extensionEnclave.js +1 -1
- package/dist/{chunk-7DKH4EXZ.js → chunk-3VHI3VNA.js} +35 -7
- package/dist/chunk-3VHI3VNA.js.map +1 -0
- package/dist/{chunk-OC5KRTNW.js → chunk-T42E7EM7.js} +3 -3
- package/dist/{chunk-OC5KRTNW.js.map → chunk-T42E7EM7.js.map} +1 -1
- package/dist/{chunk-N47KQ7OC.js → chunk-TAS2MB4S.js} +2 -2
- package/dist/chunk-TAS2MB4S.js.map +1 -0
- package/dist/index.cjs +31 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/useZKTLS.cjs.map +1 -1
- package/dist/useZKTLS.d.cts +9 -0
- package/dist/useZKTLS.d.ts +9 -0
- package/dist/useZKTLS.js +1 -1
- package/dist/useZKTLSContext.cjs.map +1 -1
- package/dist/useZKTLSContext.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-7DKH4EXZ.js.map +0 -1
- package/dist/chunk-N47KQ7OC.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useZKTLS.ts","../src/ZKTLSContext.tsx"],"names":["parseAbiParameter","useState","useEffect","useMemo","sleep","extensionPlatforms","providers","createContext","useContext"],"mappings":"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","file":"ZKTLSContext.cjs","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. Utility functions for transaction matching and proof encoding\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\n * - Generating notarized proofs\n * - Fetching transaction metadata\n *\n * @see {@link ZKTLSContext} for the React context provider\n */\n\nimport { useEffect, useMemo, useState } from \"react\"\nimport { encodeAbiParameters, encodePacked, parseAbiParameter, parseUnits, type Hex } from \"viem\"\nimport { sleep } from \"@provex/utils/time\"\nimport type { IntentStruct } from \"@provex/utils/intent\"\nimport type { TokenInfo } from \"@provex/utils/tokens\"\nimport { extensionPlatforms, providers, subProviderConfigs, type ActionType, type PlatformKey, type ProviderKey, type SubProviderKey } from \"@provex/utils/payment\"\nimport { convertTokenInputToCurrency } from \"@provex/utils/conversionRates\"\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 generateProof(inputs: GenerateProofInputs): Promise<GenerateProofResponse>\n fetchProofById(proofId: string): Promise<ProofResponse>\n fetchProofs(): Promise<FetchProofsResponse>\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 `authenticate` + `generateProof` Reclaim 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. No `generateProof` call is involved.\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 response from the PeerAuth extension when fetching proofs */\nexport interface FetchProofsResponse {\n notaryRequests: NotaryRequest[]\n}\n\n/** the inputs for the generateProof function */\nexport type GenerateProofInputsAbstracted = {\n platform: PlatformKey\n provider: ProviderKey\n intentHash: Hex\n originalIndex: number\n}\n\n/** the inputs for the generateProof function */\nexport type GenerateProofInputs = {\n platform: ProviderKey\n originalIndex: number\n proofIndex?: number\n intentHash: string\n}\n\n/**\n * inputs for the generate proof function that requires the intent hash\n * as hex to ensure it can be converted to a bigint string accurately\n */\nexport type HexIntentGenerateProofInputs = Exclude<GenerateProofInputs, 'intentHash'> & { intentHash: Hex }\n\n/** the response from the PeerAuth extension when generating a proof */\nexport type GenerateProofResponse = {\n proofId: string\n platform: ProviderKey\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 replacement for the retired\n * Reclaim `generateProof` path. Omit for the legacy Reclaim flow. The\n * extension defaults the attestation service URL when this is set.\n */\n captureMode?: 'buyerTee' | 'sellerCredential'\n}\n\n/** the response from the PeerAuth extension when fetching a proof */\nexport interface Proof {\n claimInfo: ClaimInfo\n signedClaim: SignedClaim\n isAppclipProof: boolean\n status: string\n}\n\n/** the response from the PeerAuth extension when fetching a proof */\nexport interface NotarizedProof {\n claim: {\n context: string\n epoch: number\n identifier: Hex\n owner: Hex\n parameters: string\n provider: string\n timestampS: number\n },\n signatures: {\n attestorAddress: Hex\n claimSignature: Record<number, number>\n resultSignature: Record<number, number>\n },\n}\n\n/** the claim info for a proof */\nexport interface ClaimInfo {\n provider: string\n parameters: string\n context: string\n}\n\n/** the complete claim data for a proof */\nexport interface CompleteClaimData {\n identifier: Hex\n owner: Hex\n timestampS: number\n epoch: number\n}\n\n/** the signed claim for a proof */\nexport interface SignedClaim {\n claim: CompleteClaimData\n signatures: Hex[]\n}\n\n/** the reclaim proof for a proof */\nexport type ReclaimProof = {\n claimInfo: ClaimInfo\n signedClaim: SignedClaim\n isAppclipProof: boolean\n}\n\n/** the notary request for a proof */\nexport interface NotaryRequest {\n metadata: (string | number)[]\n actionType: ActionType\n id: string\n status: string\n timestamp: number\n proof: NotarizedProof\n}\n\n/** the response from the PeerAuth extension when fetching a proof */\nexport interface ProofResponse {\n notaryRequest: NotaryRequest\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\n/**\n * Wrap a buyerTee capture (from {@link MetadataMessageResponse.buyerTeeCapture})\n * as a `NotaryRequest` so it flows through the existing submit step, which\n * narrows `proof` to a buyerTee proof and routes it to the enclave verify\n * endpoint. The `index` is the matched transaction's `originalIndex` — the\n * extension keys the captured payment on it.\n *\n * This replaces `generateProof` entirely for migrated providers: the session\n * was already captured + encrypted during `authenticate({ captureMode: 'buyerTee' })`.\n */\nexport const buildBuyerTeeNotaryRequest = ({\n encryptedSessionMaterial,\n index,\n actionType,\n}: {\n encryptedSessionMaterial: string\n index: number\n actionType: ActionType\n}): NotaryRequest => ({\n metadata: [],\n actionType,\n id: `buyer-tee-${index}`,\n status: 'success',\n timestamp: 0,\n // The runtime shape is a buyerTee proof, not a Reclaim NotarizedProof; the\n // submit step narrows it via `asBuyerTeeProof` before use.\n proof: {\n proofType: 'buyerTee',\n encryptedSessionMaterial,\n params: { index },\n } as unknown as NotarizedProof,\n})\n\nexport const extensionUrl = 'https://chromewebstore.google.com/detail/ijpgccednehjpeclfcllnjjcmiohdjih'\n\n/** convert a byte array to a hex string */\nexport const byteArrayToHexString = (byteArray: number[]): Hex => {\n return `0x${byteArray\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('')}`\n}\n\n/** parse a proof object from the PeerAuth extension */\nexport const parseExtensionProof = (proofObject: NotarizedProof) => {\n return {\n claimInfo: {\n provider: proofObject.claim.provider,\n parameters: proofObject.claim.parameters,\n context: proofObject.claim.context\n },\n signedClaim: {\n claim: {\n identifier: proofObject.claim.identifier as Hex,\n owner: proofObject.claim.owner as Hex,\n timestampS: proofObject.claim.timestampS,\n epoch: proofObject.claim.epoch\n },\n signatures: [byteArrayToHexString(Object.values(proofObject.signatures.claimSignature))]\n },\n isAppclipProof: false\n } as ReclaimProof\n}\n\n/** the abi encoding for a proof */\nexport const PROOF_ENCODING = parseAbiParameter(\"((string provider, string parameters, string context) claimInfo, ((bytes32 identifier, address owner, uint32 timestampS, uint32 epoch) claim, bytes[] signatures) signedClaim, bool isAppclipProof)\")\n\n/** encode a single proof as a bytes array */\nexport function encodeProofAsBytes(proof: ReclaimProof): Hex {\n return encodeAbiParameters([PROOF_ENCODING], [proof])\n}\n\n/** encode two proofs to be passed to the underlying sub-provider */\nexport const encodeTwoProofs = (proof1: ReclaimProof, proof2: ReclaimProof) => {\n return encodeAbiParameters([PROOF_ENCODING, PROOF_ENCODING], [proof1, proof2])\n}\n\n/** a high level function to encode a proof for a given provider and sub-provider */\nexport const encodeProof = ({ extensionProofs, subProvider, provider }: {\n extensionProofs: NotarizedProof[],\n subProvider: PlatformKey | null,\n provider: ProviderKey\n}) => {\n const reclaimProofs = extensionProofs.map((p) => parseExtensionProof(p))\n if (provider === 'zelle') {\n // move this to check the blockchain for the payment method\n const paymentMethod = subProviderConfigs![provider]![subProvider as SubProviderKey]!.paymentMethod\n const twoEncoded = reclaimProofs.length === 2\n ? encodeTwoProofs(reclaimProofs[0]!, reclaimProofs[1]!)\n : encodeProofAsBytes(reclaimProofs[0]!)\n // select between various possible sub-providers\n const withPaymentMethod = encodeProofWithPaymentMethod({\n paymentMethod,\n proof: twoEncoded,\n })\n return withPaymentMethod\n }\n const [reclaimProof] = reclaimProofs\n return encodeProofAsBytes(reclaimProof!)\n}\n\n/**\n * wrap a proof with a payment method and proof\n * this allows us to use a single verifier as an entry point for multiple underlying sub-providers\n */\nexport const encodeProofWithPaymentMethod = ({\n proof,\n paymentMethod\n}: {\n proof: Hex,\n paymentMethod: number\n}): Hex => {\n return encodePacked(['uint8', 'bytes'], [paymentMethod, proof])\n}\n\n/** the progress of a proof series */\nexport enum SeriesProgress {\n Generating,\n Polling,\n Complete,\n}\n\nexport type ProgressInfo = {\n progress: SeriesProgress\n total: number\n step: number | null\n id: string | null\n}\n\n/** the mutations for the generate proof series function */\nexport type GenerateProofSeriesMutations = {\n progress?: ({ progress, step, id }: ProgressInfo) => void\n setOwnerName?: (name: string) => void\n}\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 /** fetch a proof by its id */\n const fetchProofById = async (proofId: string): Promise<NotaryRequest | null> => {\n const proof = await window.peer!.fetchProofById(proofId)\n if (proof && proof.notaryRequest) return proof.notaryRequest\n const proofs = await window.peer!.fetchProofs()\n const proofFromList = proofs.notaryRequests.find((p) => p.id === proofId)\n if (proofFromList) return proofFromList\n return null\n }\n /** wait for a proof to be generated */\n const waitForProof = async (proofId: string, onCurrent?: (notaryRequest: NotaryRequest | null) => void) => {\n let notaryRequest: NotaryRequest | null = null\n do {\n notaryRequest = await fetchProofById(proofId)\n onCurrent?.(notaryRequest)\n await sleep(1_000)\n } while (!notaryRequest || !notaryRequest.status || notaryRequest.status === 'pending')\n if (notaryRequest.status === 'failed' || notaryRequest.status === 'expired' || notaryRequest.status === 'cancelled' || notaryRequest.status === 'error') {\n console.error('[waitForProof] proof terminal state:', JSON.stringify(notaryRequest, null, 2))\n const req = notaryRequest as unknown as Record<string, unknown>\n const detail = req.errorMessage ?? req.error ?? req.message ?? notaryRequest.status\n throw new Error(`Proof generation failed: ${detail}`)\n }\n return notaryRequest\n }\n /** generate a proof */\n const generateProof = async (inputs: HexIntentGenerateProofInputs): Promise<GenerateProofResponse> => {\n return await window.peer!.generateProof({\n proofIndex: 0,\n ...inputs,\n intentHash: BigInt(inputs.intentHash).toString(),\n })\n }\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 /** generate a proof */\n generateProof,\n /** wait for a proof to be generated */\n waitForProof,\n /** generate a proof series */\n generateProofSeries: async (\n inputs: GenerateProofInputsAbstracted,\n { progress: progressCallback }: GenerateProofSeriesMutations = {},\n ): Promise<NotaryRequest[]> => {\n const { platform, provider, intentHash, originalIndex } = inputs\n const args = {\n platform: provider,\n originalIndex,\n intentHash,\n } as const\n // Chase requires 2 proofs (account verification + transaction)\n // Platform is the bank name (e.g., 'chase') from extensionPlatform config\n const isChase = platform === extensionPlatforms.CHASE\n const isZelle = provider === providers.ZELLE\n const expectedTotal = isZelle && isChase ? 2 : 1\n progressCallback?.({ progress: SeriesProgress.Generating, step: 0, id: null, total: expectedTotal })\n const genProofA = await generateProof({ ...args, proofIndex: 0 })\n progressCallback?.({ progress: SeriesProgress.Polling, step: 0, id: genProofA.proofId, total: expectedTotal })\n const proofA = await waitForProof(genProofA.proofId)\n if (!isChase || !isZelle) {\n progressCallback?.({ progress: SeriesProgress.Complete, step: null, id: null, total: expectedTotal })\n return [proofA]\n }\n progressCallback?.({ progress: SeriesProgress.Generating, step: 1, id: null, total: expectedTotal })\n const genProofB = await generateProof({ ...args, proofIndex: 1 })\n progressCallback?.({ progress: SeriesProgress.Polling, step: 1, id: genProofB.proofId, total: expectedTotal })\n const proofB = await waitForProof(genProofB.proofId)\n progressCallback?.({ progress: SeriesProgress.Complete, step: null, id: null, total: expectedTotal })\n return [proofA, proofB]\n },\n /** subscribe to metadata messages */\n onMetadataMessage: (fn: (data: any) => void) => {\n return window.peer!.onMetadataMessage(fn)\n },\n /** fetch a proof by its id */\n fetchProofById,\n /** fetch generated proofs */\n fetchGeneratedProofs: async (genProofResponses: GenerateProofResponse[]) => {\n return await Promise.all(genProofResponses.map((r) => fetchProofById(r.proofId)))\n },\n /** fetch all proofs */\n fetchProofs: async () => {\n return await window.peer!.fetchProofs()\n },\n /** open the PeerAuth sidebar */\n openSidebar: async (route: string) => {\n return await window.peer!.openSidebar(route)\n },\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 }\n }, [isInitialized])\n}\n\n/**\n * Find the metadata message that matches the intent by amount.\n * Returns the first (most recent) transaction in the metadata array that matches the expected amount.\n *\n * Simplified matching: just find transactions with the correct amount.\n * The user can manually select if multiple matches exist.\n */\nexport const findMetadataMessage = ({ metadata, intent, token, centIsInt = false }: {\n metadata: MetadataMessage[],\n intent: IntentStruct,\n token: TokenInfo,\n /** @deprecated No longer used - kept for API compatibility */\n recipient?: string,\n centIsInt?: boolean,\n}): MetadataMessage | null => {\n const currencyAmount = convertTokenInputToCurrency({\n token,\n amountOutInt: intent.amount,\n rate: intent.conversionRate,\n })\n /**\n * Normalize an amount from the extension into a bigint in token base units.\n *\n * The extension sends amounts in different formats per provider:\n * - Venmo: `\"- $1.00\"` (formatted dollar string)\n * - Others: `100` (integer cents) or `1.00` (decimal dollars)\n *\n * We detect the format by checking for currency symbols or decimal points\n * in the raw value. If present, the value is already in dollars and we\n * skip the centIsInt division.\n */\n const normalizeAmount = (amount: number | string): 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, token.decimals)\n if (centIsInt && !isFormattedDollars) {\n value = value / 100n\n }\n return value\n }\n\n // Skip auto-match when manual selection is forced (dev/testing)\n if (typeof window !== 'undefined' && (window as unknown as Record<string, unknown>).__FORCE_MANUAL_TX_SELECT__) {\n return null\n }\n\n // Find the first transaction that matches the amount (first in array = most recent)\n // Note: We ignore the hidden flag - matching is purely by amount\n const match = metadata.find((m) => {\n const converted = normalizeAmount(m.amount)\n return converted === currencyAmount || converted === (currencyAmount * -1n)\n })\n\n if (!match) {\n // Detect 100x scale mismatch — the most common failure mode when centIsInt\n // is wrong for a provider. Fires loudly so it's obvious in dev/staging logs.\n const hundredxMatch = metadata.find((m) => {\n const converted = normalizeAmount(m.amount)\n const abs = converted < 0n ? -converted : converted\n return abs === currencyAmount * 100n || abs * 100n === currencyAmount\n })\n if (hundredxMatch) {\n console.warn(\n `[findMetadataMessage] tx[0] amount=${String(hundredxMatch.amount)} is 100x off from expected ${currencyAmount.toString()} — centIsInt is likely wrong for this provider (currently ${centIsInt})`\n )\n }\n return null\n }\n\n return match\n}\n","/**\n * @fileoverview React context for PeerAuth extension state.\n *\n * Provides global access to the useZKTLS hook state throughout the\n * component tree, avoiding prop drilling for extension status.\n *\n * @see {@link useZKTLS} for the underlying hook\n */\n\nimport { createContext, useContext, type ReactNode } from 'react'\nimport { useZKTLS } from './useZKTLS'\n\n/**\n * Context type derived from useZKTLS hook return value.\n */\ntype ZKTLSContextType = ReturnType<typeof useZKTLS>\n\nexport const ZKTLSContext = createContext<ZKTLSContextType | undefined>(undefined)\n\ninterface ZKTLSProviderProps {\n children: ReactNode\n}\n\n/**\n * Hook to consume the ZKTLS context.\n * Must be used within a ZKTLSProvider.\n *\n * @throws Error if used outside of ZKTLSProvider\n */\nexport function useZKTLSContext() {\n const context = useContext(ZKTLSContext)\n if (context === undefined) {\n throw new Error('useZKTLSContext must be used within a ZKTLSProvider')\n }\n return context\n}\n\nexport function ZKTLSProvider({ children }: ZKTLSProviderProps) {\n const zktlsHook = useZKTLS()\n\n return (\n <ZKTLSContext.Provider value={zktlsHook}>\n {children}\n </ZKTLSContext.Provider>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/useZKTLS.ts","../src/ZKTLSContext.tsx"],"names":["parseAbiParameter","useState","useEffect","useMemo","sleep","extensionPlatforms","providers","createContext","useContext"],"mappings":";;;;;;;;;;AAkRO,IAAM,gBAAA,GAAmB,CAAC,MAAA,KAA8C;AAC7E,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AAC/C,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAC3C,IAAA,MAAA,CAAO,GAAA,CAAI,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,EAC/B,CAAC,CAAA;AACD,EAAA,OAAO,OAAO,QAAA,EAAS;AACzB,CAAA;AAkE8BA,uBAAkB,qMAAqM;AAuE9O,IAAM,WAAW,MAAM;AAC5B,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAIC,eAAS,KAAK,CAAA;AACxD,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,MAAM;AAClB,MAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,IACvB,CAAA;AACA,IAAA,MAAM,SAAA,GAAY,kBAAA;AAClB,IAAA,IAAI,OAAO,MAAA,CAAO,IAAA,KAAS,WAAA,EAAa;AAEtC,MAAA,KAAA,EAAM;AAAA,IACR,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,KAAK,CAAA;AACxC,MAAA,OAAO,MAAM;AACX,QAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,KAAK,CAAA;AAAA,MAC7C,CAAA;AAAA,IACF;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AACL,EAAA,OAAOC,cAAQ,MAAM;AAEnB,IAAA,MAAM,cAAA,GAAiB,OAAO,OAAA,KAAmD;AAC/E,MAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,IAAA,CAAM,eAAe,OAAO,CAAA;AACvD,MAAA,IAAI,KAAA,IAAS,KAAA,CAAM,aAAA,EAAe,OAAO,KAAA,CAAM,aAAA;AAC/C,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,IAAA,CAAM,WAAA,EAAY;AAC9C,MAAA,MAAM,aAAA,GAAgB,OAAO,cAAA,CAAe,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AACxE,MAAA,IAAI,eAAe,OAAO,aAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAEA,IAAA,MAAM,YAAA,GAAe,OAAO,OAAA,EAAiB,SAAA,KAA8D;AACzG,MAAA,IAAI,aAAA,GAAsC,IAAA;AAC1C,MAAA,GAAG;AACD,QAAA,aAAA,GAAgB,MAAM,eAAe,OAAO,CAAA;AAC5C,QAAA,SAAA,GAAY,aAAa,CAAA;AACzB,QAAA,MAAMC,WAAM,GAAK,CAAA;AAAA,MACnB,SAAS,CAAC,aAAA,IAAiB,CAAC,aAAA,CAAc,MAAA,IAAU,cAAc,MAAA,KAAW,SAAA;AAC7E,MAAA,IAAI,aAAA,CAAc,MAAA,KAAW,QAAA,IAAY,aAAA,CAAc,MAAA,KAAW,SAAA,IAAa,aAAA,CAAc,MAAA,KAAW,WAAA,IAAe,aAAA,CAAc,MAAA,KAAW,OAAA,EAAS;AACvJ,QAAA,OAAA,CAAQ,MAAM,sCAAA,EAAwC,IAAA,CAAK,UAAU,aAAA,EAAe,IAAA,EAAM,CAAC,CAAC,CAAA;AAC5F,QAAA,MAAM,GAAA,GAAM,aAAA;AACZ,QAAA,MAAM,SAAS,GAAA,CAAI,YAAA,IAAgB,IAAI,KAAA,IAAS,GAAA,CAAI,WAAW,aAAA,CAAc,MAAA;AAC7E,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yBAAA,EAA4B,MAAM,CAAA,CAAE,CAAA;AAAA,MACtD;AACA,MAAA,OAAO,aAAA;AAAA,IACT,CAAA;AAEA,IAAA,MAAM,aAAA,GAAgB,OAAO,MAAA,KAAyE;AACpG,MAAA,OAAO,MAAM,MAAA,CAAO,IAAA,CAAM,aAAA,CAAc;AAAA,QACtC,UAAA,EAAY,CAAA;AAAA,QACZ,GAAG,MAAA;AAAA,QACH,UAAA,EAAY,MAAA,CAAO,MAAA,CAAO,UAAU,EAAE,QAAA;AAAS,OAChD,CAAA;AAAA,IACH,CAAA;AACA,IAAA,OAAO;AAAA;AAAA,MAEL,aAAA;AAAA;AAAA,MAEA,mBAAmB,YAAY;AAC7B,QAAA,IAAI,CAAC,MAAA,CAAO,IAAA,EAAM,OAAO,KAAA;AACzB,QAAA,OAAO,MAAA,CAAO,KAAM,iBAAA,EAAkB;AAAA,MACxC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,uBAAuB,YAAY;AACjC,QAAA,OAAO,MAAM,MAAA,CAAO,IAAA,CAAM,qBAAA,EAAsB;AAAA,MAClD,CAAA;AAAA;AAAA,MAEA,YAAY,YAAY;AACtB,QAAA,OAAO,MAAM,MAAA,CAAO,IAAA,EAAM,UAAA,EAAW;AAAA,MACvC,CAAA;AAAA;AAAA,MAEA,YAAA,EAAc,OAAO,MAAA,KAAiC;AAEpD,QAAA,IAAI,OAAA;AACJ,QAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAiC,CAAC,QAAA,KAAa;AACjE,UAAA,OAAA,GAAU,QAAA;AAAA,QACZ,CAAC,CAAA;AAGD,QAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAM,iBAAA,CAAkB,CAAC,IAAA,KAAS;AACrD,UAAA,IAAI,CAAA,SAAA,EAAY,IAAA,CAAK,QAAQ,CAAA,CAAA,KAAO,OAAO,UAAA,EAAY;AACrD,YAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,UACd;AAAA,QACF,CAAC,CAAA;AAED,QAAA,MAAM,MAAA,CAAO,IAAA,CAAM,YAAA,CAAa,MAAM,CAAA;AAEtC,QAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,IAAA,KAAS;AAClC,UAAA,KAAA,EAAM;AACN,UAAA,OAAO,IAAA;AAAA,QACT,CAAC,CAAA;AAAA,MACH,CAAA;AAAA;AAAA,MAEA,aAAA;AAAA;AAAA,MAEA,YAAA;AAAA;AAAA,MAEA,mBAAA,EAAqB,OACnB,MAAA,EACA,EAAE,UAAU,gBAAA,EAAiB,GAAkC,EAAC,KACnC;AAC7B,QAAA,MAAM,EAAE,QAAA,EAAU,QAAA,EAAU,UAAA,EAAY,eAAc,GAAI,MAAA;AAC1D,QAAA,MAAM,IAAA,GAAO;AAAA,UACX,QAAA,EAAU,QAAA;AAAA,UACV,aAAA;AAAA,UACA;AAAA,SACF;AAGA,QAAA,MAAM,OAAA,GAAU,aAAaC,0BAAA,CAAmB,KAAA;AAChD,QAAA,MAAM,OAAA,GAAU,aAAaC,iBAAA,CAAU,KAAA;AACvC,QAAA,MAAM,aAAA,GAAgB,OAAA,IAAW,OAAA,GAAU,CAAA,GAAI,CAAA;AAC/C,QAAA,gBAAA,GAAmB,EAAE,UAAU,CAAA,mBAA2B,IAAA,EAAM,GAAG,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,aAAA,EAAe,CAAA;AACnG,QAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAc,EAAE,GAAG,IAAA,EAAM,UAAA,EAAY,GAAG,CAAA;AAChE,QAAA,gBAAA,GAAmB,EAAE,QAAA,EAAU,CAAA,gBAAwB,IAAA,EAAM,CAAA,EAAG,IAAI,SAAA,CAAU,OAAA,EAAS,KAAA,EAAO,aAAA,EAAe,CAAA;AAC7G,QAAA,MAAM,MAAA,GAAS,MAAM,YAAA,CAAa,SAAA,CAAU,OAAO,CAAA;AACnD,QAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,EAAS;AACxB,UAAA,gBAAA,GAAmB,EAAE,UAAU,CAAA,iBAAyB,IAAA,EAAM,MAAM,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,aAAA,EAAe,CAAA;AACpG,UAAA,OAAO,CAAC,MAAM,CAAA;AAAA,QAChB;AACA,QAAA,gBAAA,GAAmB,EAAE,UAAU,CAAA,mBAA2B,IAAA,EAAM,GAAG,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,aAAA,EAAe,CAAA;AACnG,QAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAc,EAAE,GAAG,IAAA,EAAM,UAAA,EAAY,GAAG,CAAA;AAChE,QAAA,gBAAA,GAAmB,EAAE,QAAA,EAAU,CAAA,gBAAwB,IAAA,EAAM,CAAA,EAAG,IAAI,SAAA,CAAU,OAAA,EAAS,KAAA,EAAO,aAAA,EAAe,CAAA;AAC7G,QAAA,MAAM,MAAA,GAAS,MAAM,YAAA,CAAa,SAAA,CAAU,OAAO,CAAA;AACnD,QAAA,gBAAA,GAAmB,EAAE,UAAU,CAAA,iBAAyB,IAAA,EAAM,MAAM,EAAA,EAAI,IAAA,EAAM,KAAA,EAAO,aAAA,EAAe,CAAA;AACpG,QAAA,OAAO,CAAC,QAAQ,MAAM,CAAA;AAAA,MACxB,CAAA;AAAA;AAAA,MAEA,iBAAA,EAAmB,CAAC,EAAA,KAA4B;AAC9C,QAAA,OAAO,MAAA,CAAO,IAAA,CAAM,iBAAA,CAAkB,EAAE,CAAA;AAAA,MAC1C,CAAA;AAAA;AAAA,MAEA,cAAA;AAAA;AAAA,MAEA,oBAAA,EAAsB,OAAO,iBAAA,KAA+C;AAC1E,QAAA,OAAO,MAAM,OAAA,CAAQ,GAAA,CAAI,iBAAA,CAAkB,GAAA,CAAI,CAAC,CAAA,KAAM,cAAA,CAAe,CAAA,CAAE,OAAO,CAAC,CAAC,CAAA;AAAA,MAClF,CAAA;AAAA;AAAA,MAEA,aAAa,YAAY;AACvB,QAAA,OAAO,MAAM,MAAA,CAAO,IAAA,CAAM,WAAA,EAAY;AAAA,MACxC,CAAA;AAAA;AAAA,MAEA,WAAA,EAAa,OAAO,KAAA,KAAkB;AACpC,QAAA,OAAO,MAAM,MAAA,CAAO,IAAA,CAAM,WAAA,CAAY,KAAK,CAAA;AAAA,MAC7C,CAAA;AAAA;AAAA,MAEA,cAAA,EAAgB,MAAe,OAAO,MAAA,CAAO,MAAM,MAAA,KAAW,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAc9D,MAAA,EAAQ,OACN,MAAA,KAC2C;AAC3C,QAAA,IAAI,OAAO,MAAA,CAAO,IAAA,EAAM,MAAA,KAAW,UAAA,EAAY;AAC7C,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,MAAM,WAAA,GAAc,iBAAiB,MAAM,CAAA;AAC3C,QAAA,OAAO,IAAI,OAAA,CAAuC,CAAC,OAAA,KAAY;AAC7D,UAAA,MAAA,CAAO,IAAA,CAAM,MAAA,CAAQ,WAAA,EAAa,OAAO,CAAA;AAAA,QAC3C,CAAC,CAAA;AAAA,MACH,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,oBAAA,EAAsB,OACpB,UAAA,KACkD;AAClD,QAAA,IAAI,OAAO,MAAA,CAAO,IAAA,EAAM,oBAAA,KAAyB,YAAY,OAAO,IAAA;AACpE,QAAA,OAAO,MAAA,CAAO,IAAA,CAAK,oBAAA,CAAqB,UAAU,CAAA;AAAA,MACpD;AAAA,KACF;AAAA,EACF,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AACpB,CAAA;AC5kBO,IAAM,YAAA,GAAeC,oBAA4C,MAAS;AAY1E,SAAS,eAAA,GAAkB;AAChC,EAAA,MAAM,OAAA,GAAUC,iBAAW,YAAY,CAAA;AACvC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAS,EAAuB;AAC9D,EAAA,MAAM,YAAY,QAAA,EAAS;AAE3B,EAAA,sCACG,YAAA,CAAa,QAAA,EAAb,EAAsB,KAAA,EAAO,WAC3B,QAAA,EACH,CAAA;AAEJ","file":"ZKTLSContext.cjs","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. Utility functions for transaction matching and proof encoding\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\n * - Generating notarized proofs\n * - Fetching transaction metadata\n *\n * @see {@link ZKTLSContext} for the React context provider\n */\n\nimport { useEffect, useMemo, useState } from \"react\"\nimport { encodeAbiParameters, encodePacked, parseAbiParameter, parseUnits, type Hex } from \"viem\"\nimport { sleep } from \"@provex/utils/time\"\nimport type { IntentStruct } from \"@provex/utils/intent\"\nimport type { TokenInfo } from \"@provex/utils/tokens\"\nimport { extensionPlatforms, providers, subProviderConfigs, type ActionType, type PlatformKey, type ProviderKey, type SubProviderKey } from \"@provex/utils/payment\"\nimport { convertTokenInputToCurrency } from \"@provex/utils/conversionRates\"\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 generateProof(inputs: GenerateProofInputs): Promise<GenerateProofResponse>\n fetchProofById(proofId: string): Promise<ProofResponse>\n fetchProofs(): Promise<FetchProofsResponse>\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 `authenticate` + `generateProof` Reclaim 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. No `generateProof` call is involved.\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 response from the PeerAuth extension when fetching proofs */\nexport interface FetchProofsResponse {\n notaryRequests: NotaryRequest[]\n}\n\n/** the inputs for the generateProof function */\nexport type GenerateProofInputsAbstracted = {\n platform: PlatformKey\n provider: ProviderKey\n intentHash: Hex\n originalIndex: number\n}\n\n/** the inputs for the generateProof function */\nexport type GenerateProofInputs = {\n platform: ProviderKey\n originalIndex: number\n proofIndex?: number\n intentHash: string\n}\n\n/**\n * inputs for the generate proof function that requires the intent hash\n * as hex to ensure it can be converted to a bigint string accurately\n */\nexport type HexIntentGenerateProofInputs = Exclude<GenerateProofInputs, 'intentHash'> & { intentHash: Hex }\n\n/** the response from the PeerAuth extension when generating a proof */\nexport type GenerateProofResponse = {\n proofId: string\n platform: ProviderKey\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 replacement for the retired\n * Reclaim `generateProof` path. Omit for the legacy Reclaim flow. The\n * extension defaults the 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/** the response from the PeerAuth extension when fetching a proof */\nexport interface Proof {\n claimInfo: ClaimInfo\n signedClaim: SignedClaim\n isAppclipProof: boolean\n status: string\n}\n\n/** the response from the PeerAuth extension when fetching a proof */\nexport interface NotarizedProof {\n claim: {\n context: string\n epoch: number\n identifier: Hex\n owner: Hex\n parameters: string\n provider: string\n timestampS: number\n },\n signatures: {\n attestorAddress: Hex\n claimSignature: Record<number, number>\n resultSignature: Record<number, number>\n },\n}\n\n/** the claim info for a proof */\nexport interface ClaimInfo {\n provider: string\n parameters: string\n context: string\n}\n\n/** the complete claim data for a proof */\nexport interface CompleteClaimData {\n identifier: Hex\n owner: Hex\n timestampS: number\n epoch: number\n}\n\n/** the signed claim for a proof */\nexport interface SignedClaim {\n claim: CompleteClaimData\n signatures: Hex[]\n}\n\n/** the reclaim proof for a proof */\nexport type ReclaimProof = {\n claimInfo: ClaimInfo\n signedClaim: SignedClaim\n isAppclipProof: boolean\n}\n\n/** the notary request for a proof */\nexport interface NotaryRequest {\n metadata: (string | number)[]\n actionType: ActionType\n id: string\n status: string\n timestamp: number\n proof: NotarizedProof\n}\n\n/** the response from the PeerAuth extension when fetching a proof */\nexport interface ProofResponse {\n notaryRequest: NotaryRequest\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\n/**\n * Wrap a buyerTee capture (from {@link MetadataMessageResponse.buyerTeeCapture})\n * as a `NotaryRequest` so it flows through the existing submit step, which\n * narrows `proof` to a buyerTee proof and routes it to the enclave verify\n * endpoint. The `index` is the matched transaction's `originalIndex` — the\n * extension keys the captured payment on it.\n *\n * This replaces `generateProof` entirely for migrated providers: the session\n * was already captured + encrypted during `authenticate({ captureMode: 'buyerTee' })`.\n */\nexport const buildBuyerTeeNotaryRequest = ({\n encryptedSessionMaterial,\n index,\n actionType,\n}: {\n encryptedSessionMaterial: string\n index: number\n actionType: ActionType\n}): NotaryRequest => ({\n metadata: [],\n actionType,\n id: `buyer-tee-${index}`,\n status: 'success',\n timestamp: 0,\n // The runtime shape is a buyerTee proof, not a Reclaim NotarizedProof; the\n // submit step narrows it via `asBuyerTeeProof` before use.\n proof: {\n proofType: 'buyerTee',\n encryptedSessionMaterial,\n params: { index },\n } as unknown as NotarizedProof,\n})\n\nexport const extensionUrl = 'https://chromewebstore.google.com/detail/ijpgccednehjpeclfcllnjjcmiohdjih'\n\n/** convert a byte array to a hex string */\nexport const byteArrayToHexString = (byteArray: number[]): Hex => {\n return `0x${byteArray\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('')}`\n}\n\n/** parse a proof object from the PeerAuth extension */\nexport const parseExtensionProof = (proofObject: NotarizedProof) => {\n return {\n claimInfo: {\n provider: proofObject.claim.provider,\n parameters: proofObject.claim.parameters,\n context: proofObject.claim.context\n },\n signedClaim: {\n claim: {\n identifier: proofObject.claim.identifier as Hex,\n owner: proofObject.claim.owner as Hex,\n timestampS: proofObject.claim.timestampS,\n epoch: proofObject.claim.epoch\n },\n signatures: [byteArrayToHexString(Object.values(proofObject.signatures.claimSignature))]\n },\n isAppclipProof: false\n } as ReclaimProof\n}\n\n/** the abi encoding for a proof */\nexport const PROOF_ENCODING = parseAbiParameter(\"((string provider, string parameters, string context) claimInfo, ((bytes32 identifier, address owner, uint32 timestampS, uint32 epoch) claim, bytes[] signatures) signedClaim, bool isAppclipProof)\")\n\n/** encode a single proof as a bytes array */\nexport function encodeProofAsBytes(proof: ReclaimProof): Hex {\n return encodeAbiParameters([PROOF_ENCODING], [proof])\n}\n\n/** encode two proofs to be passed to the underlying sub-provider */\nexport const encodeTwoProofs = (proof1: ReclaimProof, proof2: ReclaimProof) => {\n return encodeAbiParameters([PROOF_ENCODING, PROOF_ENCODING], [proof1, proof2])\n}\n\n/** a high level function to encode a proof for a given provider and sub-provider */\nexport const encodeProof = ({ extensionProofs, subProvider, provider }: {\n extensionProofs: NotarizedProof[],\n subProvider: PlatformKey | null,\n provider: ProviderKey\n}) => {\n const reclaimProofs = extensionProofs.map((p) => parseExtensionProof(p))\n if (provider === 'zelle') {\n // move this to check the blockchain for the payment method\n const paymentMethod = subProviderConfigs![provider]![subProvider as SubProviderKey]!.paymentMethod\n const twoEncoded = reclaimProofs.length === 2\n ? encodeTwoProofs(reclaimProofs[0]!, reclaimProofs[1]!)\n : encodeProofAsBytes(reclaimProofs[0]!)\n // select between various possible sub-providers\n const withPaymentMethod = encodeProofWithPaymentMethod({\n paymentMethod,\n proof: twoEncoded,\n })\n return withPaymentMethod\n }\n const [reclaimProof] = reclaimProofs\n return encodeProofAsBytes(reclaimProof!)\n}\n\n/**\n * wrap a proof with a payment method and proof\n * this allows us to use a single verifier as an entry point for multiple underlying sub-providers\n */\nexport const encodeProofWithPaymentMethod = ({\n proof,\n paymentMethod\n}: {\n proof: Hex,\n paymentMethod: number\n}): Hex => {\n return encodePacked(['uint8', 'bytes'], [paymentMethod, proof])\n}\n\n/** the progress of a proof series */\nexport enum SeriesProgress {\n Generating,\n Polling,\n Complete,\n}\n\nexport type ProgressInfo = {\n progress: SeriesProgress\n total: number\n step: number | null\n id: string | null\n}\n\n/** the mutations for the generate proof series function */\nexport type GenerateProofSeriesMutations = {\n progress?: ({ progress, step, id }: ProgressInfo) => void\n setOwnerName?: (name: string) => void\n}\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 /** fetch a proof by its id */\n const fetchProofById = async (proofId: string): Promise<NotaryRequest | null> => {\n const proof = await window.peer!.fetchProofById(proofId)\n if (proof && proof.notaryRequest) return proof.notaryRequest\n const proofs = await window.peer!.fetchProofs()\n const proofFromList = proofs.notaryRequests.find((p) => p.id === proofId)\n if (proofFromList) return proofFromList\n return null\n }\n /** wait for a proof to be generated */\n const waitForProof = async (proofId: string, onCurrent?: (notaryRequest: NotaryRequest | null) => void) => {\n let notaryRequest: NotaryRequest | null = null\n do {\n notaryRequest = await fetchProofById(proofId)\n onCurrent?.(notaryRequest)\n await sleep(1_000)\n } while (!notaryRequest || !notaryRequest.status || notaryRequest.status === 'pending')\n if (notaryRequest.status === 'failed' || notaryRequest.status === 'expired' || notaryRequest.status === 'cancelled' || notaryRequest.status === 'error') {\n console.error('[waitForProof] proof terminal state:', JSON.stringify(notaryRequest, null, 2))\n const req = notaryRequest as unknown as Record<string, unknown>\n const detail = req.errorMessage ?? req.error ?? req.message ?? notaryRequest.status\n throw new Error(`Proof generation failed: ${detail}`)\n }\n return notaryRequest\n }\n /** generate a proof */\n const generateProof = async (inputs: HexIntentGenerateProofInputs): Promise<GenerateProofResponse> => {\n return await window.peer!.generateProof({\n proofIndex: 0,\n ...inputs,\n intentHash: BigInt(inputs.intentHash).toString(),\n })\n }\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 /** generate a proof */\n generateProof,\n /** wait for a proof to be generated */\n waitForProof,\n /** generate a proof series */\n generateProofSeries: async (\n inputs: GenerateProofInputsAbstracted,\n { progress: progressCallback }: GenerateProofSeriesMutations = {},\n ): Promise<NotaryRequest[]> => {\n const { platform, provider, intentHash, originalIndex } = inputs\n const args = {\n platform: provider,\n originalIndex,\n intentHash,\n } as const\n // Chase requires 2 proofs (account verification + transaction)\n // Platform is the bank name (e.g., 'chase') from extensionPlatform config\n const isChase = platform === extensionPlatforms.CHASE\n const isZelle = provider === providers.ZELLE\n const expectedTotal = isZelle && isChase ? 2 : 1\n progressCallback?.({ progress: SeriesProgress.Generating, step: 0, id: null, total: expectedTotal })\n const genProofA = await generateProof({ ...args, proofIndex: 0 })\n progressCallback?.({ progress: SeriesProgress.Polling, step: 0, id: genProofA.proofId, total: expectedTotal })\n const proofA = await waitForProof(genProofA.proofId)\n if (!isChase || !isZelle) {\n progressCallback?.({ progress: SeriesProgress.Complete, step: null, id: null, total: expectedTotal })\n return [proofA]\n }\n progressCallback?.({ progress: SeriesProgress.Generating, step: 1, id: null, total: expectedTotal })\n const genProofB = await generateProof({ ...args, proofIndex: 1 })\n progressCallback?.({ progress: SeriesProgress.Polling, step: 1, id: genProofB.proofId, total: expectedTotal })\n const proofB = await waitForProof(genProofB.proofId)\n progressCallback?.({ progress: SeriesProgress.Complete, step: null, id: null, total: expectedTotal })\n return [proofA, proofB]\n },\n /** subscribe to metadata messages */\n onMetadataMessage: (fn: (data: any) => void) => {\n return window.peer!.onMetadataMessage(fn)\n },\n /** fetch a proof by its id */\n fetchProofById,\n /** fetch generated proofs */\n fetchGeneratedProofs: async (genProofResponses: GenerateProofResponse[]) => {\n return await Promise.all(genProofResponses.map((r) => fetchProofById(r.proofId)))\n },\n /** fetch all proofs */\n fetchProofs: async () => {\n return await window.peer!.fetchProofs()\n },\n /** open the PeerAuth sidebar */\n openSidebar: async (route: string) => {\n return await window.peer!.openSidebar(route)\n },\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 }\n }, [isInitialized])\n}\n\n/**\n * Find the metadata message that matches the intent by amount.\n * Returns the first (most recent) transaction in the metadata array that matches the expected amount.\n *\n * Simplified matching: just find transactions with the correct amount.\n * The user can manually select if multiple matches exist.\n */\nexport const findMetadataMessage = ({ metadata, intent, token, centIsInt = false }: {\n metadata: MetadataMessage[],\n intent: IntentStruct,\n token: TokenInfo,\n /** @deprecated No longer used - kept for API compatibility */\n recipient?: string,\n centIsInt?: boolean,\n}): MetadataMessage | null => {\n const currencyAmount = convertTokenInputToCurrency({\n token,\n amountOutInt: intent.amount,\n rate: intent.conversionRate,\n })\n /**\n * Normalize an amount from the extension into a bigint in token base units.\n *\n * The extension sends amounts in different formats per provider:\n * - Venmo: `\"- $1.00\"` (formatted dollar string)\n * - Others: `100` (integer cents) or `1.00` (decimal dollars)\n *\n * We detect the format by checking for currency symbols or decimal points\n * in the raw value. If present, the value is already in dollars and we\n * skip the centIsInt division.\n */\n const normalizeAmount = (amount: number | string): 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, token.decimals)\n if (centIsInt && !isFormattedDollars) {\n value = value / 100n\n }\n return value\n }\n\n // Skip auto-match when manual selection is forced (dev/testing)\n if (typeof window !== 'undefined' && (window as unknown as Record<string, unknown>).__FORCE_MANUAL_TX_SELECT__) {\n return null\n }\n\n // Find the first transaction that matches the amount (first in array = most recent)\n // Note: We ignore the hidden flag - matching is purely by amount\n const match = metadata.find((m) => {\n const converted = normalizeAmount(m.amount)\n return converted === currencyAmount || converted === (currencyAmount * -1n)\n })\n\n if (!match) {\n // Detect 100x scale mismatch — the most common failure mode when centIsInt\n // is wrong for a provider. Fires loudly so it's obvious in dev/staging logs.\n const hundredxMatch = metadata.find((m) => {\n const converted = normalizeAmount(m.amount)\n const abs = converted < 0n ? -converted : converted\n return abs === currencyAmount * 100n || abs * 100n === currencyAmount\n })\n if (hundredxMatch) {\n console.warn(\n `[findMetadataMessage] tx[0] amount=${String(hundredxMatch.amount)} is 100x off from expected ${currencyAmount.toString()} — centIsInt is likely wrong for this provider (currently ${centIsInt})`\n )\n }\n return null\n }\n\n return match\n}\n","/**\n * @fileoverview React context for PeerAuth extension state.\n *\n * Provides global access to the useZKTLS hook state throughout the\n * component tree, avoiding prop drilling for extension status.\n *\n * @see {@link useZKTLS} for the underlying hook\n */\n\nimport { createContext, useContext, type ReactNode } from 'react'\nimport { useZKTLS } from './useZKTLS'\n\n/**\n * Context type derived from useZKTLS hook return value.\n */\ntype ZKTLSContextType = ReturnType<typeof useZKTLS>\n\nexport const ZKTLSContext = createContext<ZKTLSContextType | undefined>(undefined)\n\ninterface ZKTLSProviderProps {\n children: ReactNode\n}\n\n/**\n * Hook to consume the ZKTLS context.\n * Must be used within a ZKTLSProvider.\n *\n * @throws Error if used outside of ZKTLSProvider\n */\nexport function useZKTLSContext() {\n const context = useContext(ZKTLSContext)\n if (context === undefined) {\n throw new Error('useZKTLSContext must be used within a ZKTLSProvider')\n }\n return context\n}\n\nexport function ZKTLSProvider({ children }: ZKTLSProviderProps) {\n const zktlsHook = useZKTLS()\n\n return (\n <ZKTLSContext.Provider value={zktlsHook}>\n {children}\n </ZKTLSContext.Provider>\n )\n}\n"]}
|
package/dist/ZKTLSContext.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ZKTLSContext, ZKTLSProvider, useZKTLSContext } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
export { ZKTLSContext, ZKTLSProvider, useZKTLSContext } from './chunk-T42E7EM7.js';
|
|
2
|
+
import './chunk-TAS2MB4S.js';
|
|
3
3
|
//# sourceMappingURL=ZKTLSContext.js.map
|
|
4
4
|
//# sourceMappingURL=ZKTLSContext.js.map
|
|
@@ -6,6 +6,7 @@ var conversionRates = require('@provex/utils/conversionRates');
|
|
|
6
6
|
var proving = require('@provex/proving');
|
|
7
7
|
|
|
8
8
|
// src/adapters/extensionEnclave.ts
|
|
9
|
+
var DEFAULT_ATTESTATION_SERVICE_URL = "https://attestation-service.zkp2p.xyz";
|
|
9
10
|
function normalizeAmount(amount, decimals, centIsInt) {
|
|
10
11
|
const raw = String(amount);
|
|
11
12
|
const isFormattedDollars = /[^0-9\-]/.test(raw) && raw.includes(".");
|
|
@@ -55,14 +56,35 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
55
56
|
if (!peer) {
|
|
56
57
|
throw new proving.EnclaveCaptureUnavailable(request.platform);
|
|
57
58
|
}
|
|
59
|
+
const status = await peer.checkConnectionStatus();
|
|
60
|
+
if (status !== "connected") {
|
|
61
|
+
await peer.requestConnection();
|
|
62
|
+
}
|
|
58
63
|
const metadataPromise = awaitMetadata(peer, request.actionType, request.platform, timeoutMs);
|
|
59
64
|
await peer.authenticate({
|
|
60
65
|
actionType: request.actionType,
|
|
61
66
|
platform: request.platform,
|
|
62
|
-
captureMode: "buyerTee"
|
|
67
|
+
captureMode: "buyerTee",
|
|
68
|
+
// Required for the extension to actually perform the Buyer TEE capture
|
|
69
|
+
// (zkp2p 0.6.0 spec §7). Omitting these is why non-Revolut rails got
|
|
70
|
+
// "no buyerTee capture" and dead-ended on the retired Reclaim path.
|
|
71
|
+
attestationServiceUrl: config.attestationServiceUrl ?? DEFAULT_ATTESTATION_SERVICE_URL,
|
|
72
|
+
attestationActionType: request.actionType
|
|
63
73
|
});
|
|
64
74
|
const response = await metadataPromise;
|
|
65
75
|
if (!response.buyerTeeCapture) {
|
|
76
|
+
console.warn(
|
|
77
|
+
"[extensionEnclave] no buyerTeeCapture for",
|
|
78
|
+
request.platform,
|
|
79
|
+
"\u2014 response keys:",
|
|
80
|
+
Object.keys(response),
|
|
81
|
+
"| errorMessage:",
|
|
82
|
+
response.errorMessage,
|
|
83
|
+
"| metadata rows:",
|
|
84
|
+
Array.isArray(response.metadata) ? response.metadata.length : "(none)",
|
|
85
|
+
"| buyerTeeCapture:",
|
|
86
|
+
response.buyerTeeCapture
|
|
87
|
+
);
|
|
66
88
|
throw new proving.EnclaveCaptureUnavailable(request.platform);
|
|
67
89
|
}
|
|
68
90
|
const matched = autoMatch(response.metadata, request);
|
|
@@ -74,9 +96,11 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
74
96
|
} else {
|
|
75
97
|
throw new proving.ManualSelectionRequired(response.metadata);
|
|
76
98
|
}
|
|
99
|
+
const matchedRow = response.metadata.find((row) => row.originalIndex === matchedIndex);
|
|
77
100
|
const material = {
|
|
78
101
|
encryptedSessionMaterial: response.buyerTeeCapture.encryptedSessionMaterial,
|
|
79
|
-
matchedIndex
|
|
102
|
+
matchedIndex,
|
|
103
|
+
matchedParams: matchedRow?.params
|
|
80
104
|
};
|
|
81
105
|
return { kind: "extension-enclave", material };
|
|
82
106
|
},
|
|
@@ -87,12 +111,16 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
87
111
|
);
|
|
88
112
|
}
|
|
89
113
|
const material = handle.material;
|
|
114
|
+
const params = {
|
|
115
|
+
...material.matchedParams ?? {},
|
|
116
|
+
...payment.providerRequiresMetadataIndex(request.provider) ? { index: material.matchedIndex } : {}
|
|
117
|
+
};
|
|
90
118
|
const attestation = await proving.requestBuyerTeeAttestation({
|
|
91
119
|
attestationBaseUrl: config.attestationBaseUrl,
|
|
92
120
|
platform: request.platform,
|
|
93
121
|
actionType: request.actionType,
|
|
94
122
|
encryptedSessionMaterial: material.encryptedSessionMaterial,
|
|
95
|
-
params
|
|
123
|
+
params,
|
|
96
124
|
verifyingContract: request.verifyingContract,
|
|
97
125
|
intent: request.intent
|
|
98
126
|
});
|
|
@@ -105,6 +133,7 @@ Object.defineProperty(exports, "asBuyerTeeProof", {
|
|
|
105
133
|
enumerable: true,
|
|
106
134
|
get: function () { return proving.asBuyerTeeProof; }
|
|
107
135
|
});
|
|
136
|
+
exports.DEFAULT_ATTESTATION_SERVICE_URL = DEFAULT_ATTESTATION_SERVICE_URL;
|
|
108
137
|
exports.createExtensionEnclaveAdapter = createExtensionEnclaveAdapter;
|
|
109
138
|
//# sourceMappingURL=extensionEnclave.cjs.map
|
|
110
139
|
//# sourceMappingURL=extensionEnclave.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/extensionEnclave.ts"],"names":["parseUnits","convertTokenInputToCurrency","providerConfigs","EnclaveCaptureUnavailable","ManualSelectionRequired","requestBuyerTeeAttestation"],"mappings":"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","file":"extensionEnclave.cjs","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 } from '@provex/utils/payment'\nimport type { ChainId } from '@provex/utils/chain'\nimport { convertTokenInputToCurrency } from '@provex/utils/conversionRates'\nimport {\n EnclaveCaptureUnavailable,\n ManualSelectionRequired,\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/** Minimal slice of `window.peer` this adapter depends on. */\nexport interface PeerClient {\n authenticate(inputs: { actionType: string; platform: string; captureMode: 'buyerTee' }): Promise<void>\n onMetadataMessage(fn: (data: MetadataResponse) => void): () => void\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. */\n attestationBaseUrl: 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\ninterface EnclaveMaterial {\n encryptedSessionMaterial: string\n matchedIndex: number\n}\n\n/**\n * Normalize an amount from the extension into a bigint in token base units.\n *\n * Reproduces findMetadataMessage's normalizeAmount (useZKTLS.ts:630-639).\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 * Find the candidate whose amount equals the intent's FIAT currency amount,\n * matching positive or negative sign as findMetadataMessage does.\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 */\nfunction autoMatch(\n candidates: CaptureCandidate[],\n request: CaptureRequest,\n): CaptureCandidate | null {\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 return (\n candidates.find((candidate) => {\n const converted = normalizeAmount(candidate.amount, request.token.decimals, centIsInt)\n return converted === currencyAmount || converted === currencyAmount * -1n\n }) ?? null\n )\n}\n\n/**\n * Wait for the metadata message whose platform matches the action type,\n * mirroring the live listener resolve guard (AuthenticateStep.tsx:238-244).\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 */\nfunction awaitMetadata(\n peer: PeerClient,\n actionType: string,\n platform: 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 if (`transfer_${data.platform}` === actionType) {\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 // 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.actionType, request.platform, timeoutMs)\n await peer.authenticate({\n actionType: request.actionType,\n platform: request.platform,\n captureMode: 'buyerTee',\n })\n const response = await metadataPromise\n\n // No buyerTee capture ⇒ a non-migrated / Reclaim-only provider. The\n // caller falls back to UnifiedBuy's retained Reclaim branch.\n if (!response.buyerTeeCapture) {\n throw new EnclaveCaptureUnavailable(request.platform)\n }\n\n const matched = autoMatch(response.metadata, request)\n let matchedIndex: number\n if (matched) {\n matchedIndex = matched.originalIndex\n } else if (options?.selectTransaction) {\n matchedIndex = await options.selectTransaction(response.metadata)\n } else {\n throw new ManualSelectionRequired(response.metadata)\n }\n\n const material: EnclaveMaterial = {\n encryptedSessionMaterial: response.buyerTeeCapture.encryptedSessionMaterial,\n matchedIndex,\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 const attestation = await requestBuyerTeeAttestation({\n attestationBaseUrl: config.attestationBaseUrl,\n platform: request.platform,\n actionType: request.actionType,\n encryptedSessionMaterial: material.encryptedSessionMaterial,\n params: { index: material.matchedIndex },\n verifyingContract: request.verifyingContract,\n intent: request.intent,\n })\n // verificationData = enclave signer (SubmitStep.tsx:473-474).\n return { attestation, verificationData: attestation.signer as Hex }\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"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/extensionEnclave.ts"],"names":["parseUnits","convertTokenInputToCurrency","providerConfigs","EnclaveCaptureUnavailable","ManualSelectionRequired","providerRequiresMetadataIndex","requestBuyerTeeAttestation"],"mappings":"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","file":"extensionEnclave.cjs","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 ManualSelectionRequired,\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 }): 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\ninterface EnclaveMaterial {\n encryptedSessionMaterial: string\n matchedIndex: number\n /** Selected row's provider attestation params (spec §4) — forwarded to the enclave. */\n matchedParams?: Record<string, string | number | boolean>\n}\n\n/**\n * Normalize an amount from the extension into a bigint in token base units.\n *\n * Reproduces findMetadataMessage's normalizeAmount (useZKTLS.ts:630-639).\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 * Find the candidate whose amount equals the intent's FIAT currency amount,\n * matching positive or negative sign as findMetadataMessage does.\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 */\nfunction autoMatch(\n candidates: CaptureCandidate[],\n request: CaptureRequest,\n): CaptureCandidate | null {\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 return (\n candidates.find((candidate) => {\n const converted = normalizeAmount(candidate.amount, request.token.decimals, centIsInt)\n return converted === currencyAmount || converted === currencyAmount * -1n\n }) ?? null\n )\n}\n\n/**\n * Wait for the metadata message whose platform matches the action type,\n * mirroring the live listener resolve guard (AuthenticateStep.tsx:238-244).\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 */\nfunction awaitMetadata(\n peer: PeerClient,\n actionType: string,\n platform: 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 if (`transfer_${data.platform}` === actionType) {\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.actionType, request.platform, 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 attestationActionType: request.actionType,\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 console.warn(\n '[extensionEnclave] no buyerTeeCapture for',\n request.platform,\n '— response keys:', Object.keys(response),\n '| errorMessage:', (response as { errorMessage?: unknown }).errorMessage,\n '| metadata rows:', Array.isArray(response.metadata) ? response.metadata.length : '(none)',\n '| buyerTeeCapture:', response.buyerTeeCapture,\n )\n throw new EnclaveCaptureUnavailable(request.platform)\n }\n\n const matched = autoMatch(response.metadata, request)\n let matchedIndex: number\n if (matched) {\n matchedIndex = matched.originalIndex\n } else if (options?.selectTransaction) {\n matchedIndex = await options.selectTransaction(response.metadata)\n } else {\n throw new ManualSelectionRequired(response.metadata)\n }\n\n // The SELECTED row's params ARE the attestation params (spec §4) — carry\n // them through so attest() can forward them. Look up by originalIndex so\n // this works for both the auto-match and manual-selection branches.\n const matchedRow = response.metadata.find((row) => row.originalIndex === matchedIndex)\n const material: EnclaveMaterial = {\n encryptedSessionMaterial: response.buyerTeeCapture.encryptedSessionMaterial,\n matchedIndex,\n matchedParams: matchedRow?.params,\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 // 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 ...(material.matchedParams ?? {}),\n ...(providerRequiresMetadataIndex(request.provider as ProviderKey | SubProviderKey)\n ? { index: material.matchedIndex }\n : {}),\n }\n const attestation = await requestBuyerTeeAttestation({\n attestationBaseUrl: config.attestationBaseUrl,\n platform: request.platform,\n actionType: request.actionType,\n encryptedSessionMaterial: material.encryptedSessionMaterial,\n params,\n verifyingContract: request.verifyingContract,\n intent: request.intent,\n })\n // verificationData = enclave signer (SubmitStep.tsx:473-474).\n return { attestation, verificationData: attestation.signer as Hex }\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"]}
|
|
@@ -18,20 +18,48 @@ interface MetadataResponse {
|
|
|
18
18
|
encryptedSessionMaterial: string;
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* zkp2p enclave used for Buyer TEE capture when the caller doesn't override
|
|
23
|
+
* `ExtensionEnclaveAdapterConfig.attestationServiceUrl`. Matches the gate's
|
|
24
|
+
* `TEE_ATTESTATION_URL` — the enclave the gate forwards to for the re-sign.
|
|
25
|
+
*/
|
|
26
|
+
declare const DEFAULT_ATTESTATION_SERVICE_URL = "https://attestation-service.zkp2p.xyz";
|
|
21
27
|
/** Minimal slice of `window.peer` this adapter depends on. */
|
|
22
28
|
interface PeerClient {
|
|
23
29
|
authenticate(inputs: {
|
|
24
30
|
actionType: string;
|
|
25
31
|
platform: string;
|
|
26
32
|
captureMode: 'buyerTee';
|
|
33
|
+
/**
|
|
34
|
+
* Enclave the extension establishes the Buyer TEE session with and encrypts
|
|
35
|
+
* the session material toward. Required by the zkp2p 0.6.0 spec
|
|
36
|
+
* (onramp-llm.md §7) — without it the extension performs no Buyer TEE
|
|
37
|
+
* capture and falls back to the retired Reclaim path.
|
|
38
|
+
*/
|
|
39
|
+
attestationServiceUrl?: string;
|
|
40
|
+
/** Attestation action type; defaults to `actionType` when omitted (spec §2). */
|
|
41
|
+
attestationActionType?: string;
|
|
27
42
|
}): Promise<void>;
|
|
28
43
|
onMetadataMessage(fn: (data: MetadataResponse) => void): () => void;
|
|
44
|
+
/** Current extension connection status; `'connected'` means ready. */
|
|
45
|
+
checkConnectionStatus(): Promise<string>;
|
|
46
|
+
/** Prompt the user to connect the extension. Resolves true when approved. */
|
|
47
|
+
requestConnection(): Promise<boolean>;
|
|
29
48
|
}
|
|
30
49
|
interface ExtensionEnclaveAdapterConfig {
|
|
31
50
|
/** Resolve the live peer client (e.g. `() => window.peer`). */
|
|
32
51
|
getPeer: () => PeerClient | undefined;
|
|
33
|
-
/** Gate/attestation origin handed to requestBuyerTeeAttestation. */
|
|
52
|
+
/** Gate/attestation origin handed to requestBuyerTeeAttestation (the re-sign hop). */
|
|
34
53
|
attestationBaseUrl: string;
|
|
54
|
+
/**
|
|
55
|
+
* Enclave URL passed to the extension's `authenticate` so it performs the
|
|
56
|
+
* Buyer TEE capture (zkp2p 0.6.0 spec §7). This is the zkp2p enclave the
|
|
57
|
+
* session material is encrypted toward — the SAME enclave the gate forwards
|
|
58
|
+
* to for the re-sign — NOT our gate's re-sign origin (`attestationBaseUrl`).
|
|
59
|
+
* Defaults to {@link DEFAULT_ATTESTATION_SERVICE_URL} when omitted; set it to
|
|
60
|
+
* override the enclave for local testing.
|
|
61
|
+
*/
|
|
62
|
+
attestationServiceUrl?: string;
|
|
35
63
|
/**
|
|
36
64
|
* Milliseconds before awaitMetadata rejects if the extension never posts a
|
|
37
65
|
* matching metadata message. Mirrors the 60-second timeout in
|
|
@@ -41,4 +69,4 @@ interface ExtensionEnclaveAdapterConfig {
|
|
|
41
69
|
}
|
|
42
70
|
declare function createExtensionEnclaveAdapter(config: ExtensionEnclaveAdapterConfig): CaptureAdapter;
|
|
43
71
|
|
|
44
|
-
export { type ExtensionEnclaveAdapterConfig, type PeerClient, createExtensionEnclaveAdapter };
|
|
72
|
+
export { DEFAULT_ATTESTATION_SERVICE_URL, type ExtensionEnclaveAdapterConfig, type PeerClient, createExtensionEnclaveAdapter };
|
|
@@ -18,20 +18,48 @@ interface MetadataResponse {
|
|
|
18
18
|
encryptedSessionMaterial: string;
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* zkp2p enclave used for Buyer TEE capture when the caller doesn't override
|
|
23
|
+
* `ExtensionEnclaveAdapterConfig.attestationServiceUrl`. Matches the gate's
|
|
24
|
+
* `TEE_ATTESTATION_URL` — the enclave the gate forwards to for the re-sign.
|
|
25
|
+
*/
|
|
26
|
+
declare const DEFAULT_ATTESTATION_SERVICE_URL = "https://attestation-service.zkp2p.xyz";
|
|
21
27
|
/** Minimal slice of `window.peer` this adapter depends on. */
|
|
22
28
|
interface PeerClient {
|
|
23
29
|
authenticate(inputs: {
|
|
24
30
|
actionType: string;
|
|
25
31
|
platform: string;
|
|
26
32
|
captureMode: 'buyerTee';
|
|
33
|
+
/**
|
|
34
|
+
* Enclave the extension establishes the Buyer TEE session with and encrypts
|
|
35
|
+
* the session material toward. Required by the zkp2p 0.6.0 spec
|
|
36
|
+
* (onramp-llm.md §7) — without it the extension performs no Buyer TEE
|
|
37
|
+
* capture and falls back to the retired Reclaim path.
|
|
38
|
+
*/
|
|
39
|
+
attestationServiceUrl?: string;
|
|
40
|
+
/** Attestation action type; defaults to `actionType` when omitted (spec §2). */
|
|
41
|
+
attestationActionType?: string;
|
|
27
42
|
}): Promise<void>;
|
|
28
43
|
onMetadataMessage(fn: (data: MetadataResponse) => void): () => void;
|
|
44
|
+
/** Current extension connection status; `'connected'` means ready. */
|
|
45
|
+
checkConnectionStatus(): Promise<string>;
|
|
46
|
+
/** Prompt the user to connect the extension. Resolves true when approved. */
|
|
47
|
+
requestConnection(): Promise<boolean>;
|
|
29
48
|
}
|
|
30
49
|
interface ExtensionEnclaveAdapterConfig {
|
|
31
50
|
/** Resolve the live peer client (e.g. `() => window.peer`). */
|
|
32
51
|
getPeer: () => PeerClient | undefined;
|
|
33
|
-
/** Gate/attestation origin handed to requestBuyerTeeAttestation. */
|
|
52
|
+
/** Gate/attestation origin handed to requestBuyerTeeAttestation (the re-sign hop). */
|
|
34
53
|
attestationBaseUrl: string;
|
|
54
|
+
/**
|
|
55
|
+
* Enclave URL passed to the extension's `authenticate` so it performs the
|
|
56
|
+
* Buyer TEE capture (zkp2p 0.6.0 spec §7). This is the zkp2p enclave the
|
|
57
|
+
* session material is encrypted toward — the SAME enclave the gate forwards
|
|
58
|
+
* to for the re-sign — NOT our gate's re-sign origin (`attestationBaseUrl`).
|
|
59
|
+
* Defaults to {@link DEFAULT_ATTESTATION_SERVICE_URL} when omitted; set it to
|
|
60
|
+
* override the enclave for local testing.
|
|
61
|
+
*/
|
|
62
|
+
attestationServiceUrl?: string;
|
|
35
63
|
/**
|
|
36
64
|
* Milliseconds before awaitMetadata rejects if the extension never posts a
|
|
37
65
|
* matching metadata message. Mirrors the 60-second timeout in
|
|
@@ -41,4 +69,4 @@ interface ExtensionEnclaveAdapterConfig {
|
|
|
41
69
|
}
|
|
42
70
|
declare function createExtensionEnclaveAdapter(config: ExtensionEnclaveAdapterConfig): CaptureAdapter;
|
|
43
71
|
|
|
44
|
-
export { type ExtensionEnclaveAdapterConfig, type PeerClient, createExtensionEnclaveAdapter };
|
|
72
|
+
export { DEFAULT_ATTESTATION_SERVICE_URL, type ExtensionEnclaveAdapterConfig, type PeerClient, createExtensionEnclaveAdapter };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { asBuyerTeeProof, createExtensionEnclaveAdapter } from '../chunk-
|
|
1
|
+
export { DEFAULT_ATTESTATION_SERVICE_URL, asBuyerTeeProof, createExtensionEnclaveAdapter } from '../chunk-3VHI3VNA.js';
|
|
2
2
|
//# sourceMappingURL=extensionEnclave.js.map
|
|
3
3
|
//# sourceMappingURL=extensionEnclave.js.map
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { parseUnits } from 'viem';
|
|
2
|
-
import { providerConfigs } from '@provex/utils/payment';
|
|
2
|
+
import { providerRequiresMetadataIndex, providerConfigs } from '@provex/utils/payment';
|
|
3
3
|
import { convertTokenInputToCurrency } from '@provex/utils/conversionRates';
|
|
4
4
|
import { requestBuyerTeeAttestation, EnclaveCaptureUnavailable, ManualSelectionRequired } from '@provex/proving';
|
|
5
5
|
export { asBuyerTeeProof } from '@provex/proving';
|
|
6
6
|
|
|
7
7
|
// src/adapters/extensionEnclave.ts
|
|
8
|
+
var DEFAULT_ATTESTATION_SERVICE_URL = "https://attestation-service.zkp2p.xyz";
|
|
8
9
|
function normalizeAmount(amount, decimals, centIsInt) {
|
|
9
10
|
const raw = String(amount);
|
|
10
11
|
const isFormattedDollars = /[^0-9\-]/.test(raw) && raw.includes(".");
|
|
@@ -54,14 +55,35 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
54
55
|
if (!peer) {
|
|
55
56
|
throw new EnclaveCaptureUnavailable(request.platform);
|
|
56
57
|
}
|
|
58
|
+
const status = await peer.checkConnectionStatus();
|
|
59
|
+
if (status !== "connected") {
|
|
60
|
+
await peer.requestConnection();
|
|
61
|
+
}
|
|
57
62
|
const metadataPromise = awaitMetadata(peer, request.actionType, request.platform, timeoutMs);
|
|
58
63
|
await peer.authenticate({
|
|
59
64
|
actionType: request.actionType,
|
|
60
65
|
platform: request.platform,
|
|
61
|
-
captureMode: "buyerTee"
|
|
66
|
+
captureMode: "buyerTee",
|
|
67
|
+
// Required for the extension to actually perform the Buyer TEE capture
|
|
68
|
+
// (zkp2p 0.6.0 spec §7). Omitting these is why non-Revolut rails got
|
|
69
|
+
// "no buyerTee capture" and dead-ended on the retired Reclaim path.
|
|
70
|
+
attestationServiceUrl: config.attestationServiceUrl ?? DEFAULT_ATTESTATION_SERVICE_URL,
|
|
71
|
+
attestationActionType: request.actionType
|
|
62
72
|
});
|
|
63
73
|
const response = await metadataPromise;
|
|
64
74
|
if (!response.buyerTeeCapture) {
|
|
75
|
+
console.warn(
|
|
76
|
+
"[extensionEnclave] no buyerTeeCapture for",
|
|
77
|
+
request.platform,
|
|
78
|
+
"\u2014 response keys:",
|
|
79
|
+
Object.keys(response),
|
|
80
|
+
"| errorMessage:",
|
|
81
|
+
response.errorMessage,
|
|
82
|
+
"| metadata rows:",
|
|
83
|
+
Array.isArray(response.metadata) ? response.metadata.length : "(none)",
|
|
84
|
+
"| buyerTeeCapture:",
|
|
85
|
+
response.buyerTeeCapture
|
|
86
|
+
);
|
|
65
87
|
throw new EnclaveCaptureUnavailable(request.platform);
|
|
66
88
|
}
|
|
67
89
|
const matched = autoMatch(response.metadata, request);
|
|
@@ -73,9 +95,11 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
73
95
|
} else {
|
|
74
96
|
throw new ManualSelectionRequired(response.metadata);
|
|
75
97
|
}
|
|
98
|
+
const matchedRow = response.metadata.find((row) => row.originalIndex === matchedIndex);
|
|
76
99
|
const material = {
|
|
77
100
|
encryptedSessionMaterial: response.buyerTeeCapture.encryptedSessionMaterial,
|
|
78
|
-
matchedIndex
|
|
101
|
+
matchedIndex,
|
|
102
|
+
matchedParams: matchedRow?.params
|
|
79
103
|
};
|
|
80
104
|
return { kind: "extension-enclave", material };
|
|
81
105
|
},
|
|
@@ -86,12 +110,16 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
86
110
|
);
|
|
87
111
|
}
|
|
88
112
|
const material = handle.material;
|
|
113
|
+
const params = {
|
|
114
|
+
...material.matchedParams ?? {},
|
|
115
|
+
...providerRequiresMetadataIndex(request.provider) ? { index: material.matchedIndex } : {}
|
|
116
|
+
};
|
|
89
117
|
const attestation = await requestBuyerTeeAttestation({
|
|
90
118
|
attestationBaseUrl: config.attestationBaseUrl,
|
|
91
119
|
platform: request.platform,
|
|
92
120
|
actionType: request.actionType,
|
|
93
121
|
encryptedSessionMaterial: material.encryptedSessionMaterial,
|
|
94
|
-
params
|
|
122
|
+
params,
|
|
95
123
|
verifyingContract: request.verifyingContract,
|
|
96
124
|
intent: request.intent
|
|
97
125
|
});
|
|
@@ -100,6 +128,6 @@ function createExtensionEnclaveAdapter(config) {
|
|
|
100
128
|
};
|
|
101
129
|
}
|
|
102
130
|
|
|
103
|
-
export { createExtensionEnclaveAdapter };
|
|
104
|
-
//# sourceMappingURL=chunk-
|
|
105
|
-
//# sourceMappingURL=chunk-
|
|
131
|
+
export { DEFAULT_ATTESTATION_SERVICE_URL, createExtensionEnclaveAdapter };
|
|
132
|
+
//# sourceMappingURL=chunk-3VHI3VNA.js.map
|
|
133
|
+
//# sourceMappingURL=chunk-3VHI3VNA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/extensionEnclave.ts"],"names":[],"mappings":"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","file":"chunk-3VHI3VNA.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 ManualSelectionRequired,\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 }): 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\ninterface EnclaveMaterial {\n encryptedSessionMaterial: string\n matchedIndex: number\n /** Selected row's provider attestation params (spec §4) — forwarded to the enclave. */\n matchedParams?: Record<string, string | number | boolean>\n}\n\n/**\n * Normalize an amount from the extension into a bigint in token base units.\n *\n * Reproduces findMetadataMessage's normalizeAmount (useZKTLS.ts:630-639).\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 * Find the candidate whose amount equals the intent's FIAT currency amount,\n * matching positive or negative sign as findMetadataMessage does.\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 */\nfunction autoMatch(\n candidates: CaptureCandidate[],\n request: CaptureRequest,\n): CaptureCandidate | null {\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 return (\n candidates.find((candidate) => {\n const converted = normalizeAmount(candidate.amount, request.token.decimals, centIsInt)\n return converted === currencyAmount || converted === currencyAmount * -1n\n }) ?? null\n )\n}\n\n/**\n * Wait for the metadata message whose platform matches the action type,\n * mirroring the live listener resolve guard (AuthenticateStep.tsx:238-244).\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 */\nfunction awaitMetadata(\n peer: PeerClient,\n actionType: string,\n platform: 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 if (`transfer_${data.platform}` === actionType) {\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.actionType, request.platform, 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 attestationActionType: request.actionType,\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 console.warn(\n '[extensionEnclave] no buyerTeeCapture for',\n request.platform,\n '— response keys:', Object.keys(response),\n '| errorMessage:', (response as { errorMessage?: unknown }).errorMessage,\n '| metadata rows:', Array.isArray(response.metadata) ? response.metadata.length : '(none)',\n '| buyerTeeCapture:', response.buyerTeeCapture,\n )\n throw new EnclaveCaptureUnavailable(request.platform)\n }\n\n const matched = autoMatch(response.metadata, request)\n let matchedIndex: number\n if (matched) {\n matchedIndex = matched.originalIndex\n } else if (options?.selectTransaction) {\n matchedIndex = await options.selectTransaction(response.metadata)\n } else {\n throw new ManualSelectionRequired(response.metadata)\n }\n\n // The SELECTED row's params ARE the attestation params (spec §4) — carry\n // them through so attest() can forward them. Look up by originalIndex so\n // this works for both the auto-match and manual-selection branches.\n const matchedRow = response.metadata.find((row) => row.originalIndex === matchedIndex)\n const material: EnclaveMaterial = {\n encryptedSessionMaterial: response.buyerTeeCapture.encryptedSessionMaterial,\n matchedIndex,\n matchedParams: matchedRow?.params,\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 // 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 ...(material.matchedParams ?? {}),\n ...(providerRequiresMetadataIndex(request.provider as ProviderKey | SubProviderKey)\n ? { index: material.matchedIndex }\n : {}),\n }\n const attestation = await requestBuyerTeeAttestation({\n attestationBaseUrl: config.attestationBaseUrl,\n platform: request.platform,\n actionType: request.actionType,\n encryptedSessionMaterial: material.encryptedSessionMaterial,\n params,\n verifyingContract: request.verifyingContract,\n intent: request.intent,\n })\n // verificationData = enclave signer (SubmitStep.tsx:473-474).\n return { attestation, verificationData: attestation.signer as Hex }\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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useZKTLS } from './chunk-
|
|
1
|
+
import { useZKTLS } from './chunk-TAS2MB4S.js';
|
|
2
2
|
import { createContext, useContext } from 'react';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -16,5 +16,5 @@ function ZKTLSProvider({ children }) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export { ZKTLSContext, ZKTLSProvider, useZKTLSContext };
|
|
19
|
-
//# sourceMappingURL=chunk-
|
|
20
|
-
//# sourceMappingURL=chunk-
|
|
19
|
+
//# sourceMappingURL=chunk-T42E7EM7.js.map
|
|
20
|
+
//# sourceMappingURL=chunk-T42E7EM7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ZKTLSContext.tsx"],"names":[],"mappings":";;;;AAiBO,IAAM,YAAA,GAAe,cAA4C,MAAS;AAY1E,SAAS,eAAA,GAAkB;AAChC,EAAA,MAAM,OAAA,GAAU,WAAW,YAAY,CAAA;AACvC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAS,EAAuB;AAC9D,EAAA,MAAM,YAAY,QAAA,EAAS;AAE3B,EAAA,2BACG,YAAA,CAAa,QAAA,EAAb,EAAsB,KAAA,EAAO,WAC3B,QAAA,EACH,CAAA;AAEJ","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/ZKTLSContext.tsx"],"names":[],"mappings":";;;;AAiBO,IAAM,YAAA,GAAe,cAA4C,MAAS;AAY1E,SAAS,eAAA,GAAkB;AAChC,EAAA,MAAM,OAAA,GAAU,WAAW,YAAY,CAAA;AACvC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAS,EAAuB;AAC9D,EAAA,MAAM,YAAY,QAAA,EAAS;AAE3B,EAAA,2BACG,YAAA,CAAa,QAAA,EAAb,EAAsB,KAAA,EAAO,WAC3B,QAAA,EACH,CAAA;AAEJ","file":"chunk-T42E7EM7.js","sourcesContent":["/**\n * @fileoverview React context for PeerAuth extension state.\n *\n * Provides global access to the useZKTLS hook state throughout the\n * component tree, avoiding prop drilling for extension status.\n *\n * @see {@link useZKTLS} for the underlying hook\n */\n\nimport { createContext, useContext, type ReactNode } from 'react'\nimport { useZKTLS } from './useZKTLS'\n\n/**\n * Context type derived from useZKTLS hook return value.\n */\ntype ZKTLSContextType = ReturnType<typeof useZKTLS>\n\nexport const ZKTLSContext = createContext<ZKTLSContextType | undefined>(undefined)\n\ninterface ZKTLSProviderProps {\n children: ReactNode\n}\n\n/**\n * Hook to consume the ZKTLS context.\n * Must be used within a ZKTLSProvider.\n *\n * @throws Error if used outside of ZKTLSProvider\n */\nexport function useZKTLSContext() {\n const context = useContext(ZKTLSContext)\n if (context === undefined) {\n throw new Error('useZKTLSContext must be used within a ZKTLSProvider')\n }\n return context\n}\n\nexport function ZKTLSProvider({ children }: ZKTLSProviderProps) {\n const zktlsHook = useZKTLS()\n\n return (\n <ZKTLSContext.Provider value={zktlsHook}>\n {children}\n </ZKTLSContext.Provider>\n )\n}\n"]}
|
|
@@ -297,5 +297,5 @@ var findMetadataMessage = ({ metadata, intent, token, centIsInt = false }) => {
|
|
|
297
297
|
};
|
|
298
298
|
|
|
299
299
|
export { PROOF_ENCODING, SeriesProgress, buildBuyerTeeNotaryRequest, buildOnrampQuery, byteArrayToHexString, encodeProof, encodeProofAsBytes, encodeProofWithPaymentMethod, encodeTwoProofs, extensionUrl, findMetadataMessage, parseExtensionProof, useZKTLS };
|
|
300
|
-
//# sourceMappingURL=chunk-
|
|
301
|
-
//# sourceMappingURL=chunk-
|
|
300
|
+
//# sourceMappingURL=chunk-TAS2MB4S.js.map
|
|
301
|
+
//# sourceMappingURL=chunk-TAS2MB4S.js.map
|