@provex/proving 1.10.0 → 1.11.0-rc.20261018212258.bdc024c
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/{chunk-SQ3PVZQH.js → chunk-V2ESGZXE.js} +2 -2
- package/dist/chunk-V2ESGZXE.js.map +1 -0
- package/dist/index.cjs +28 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -11
- package/dist/index.d.ts +18 -11
- package/dist/index.js +29 -17
- package/dist/index.js.map +1 -1
- package/dist/{useProving-B3b3wfqz.d.cts → useProving-B4UsP7ku.d.cts} +9 -16
- package/dist/{useProving-B3b3wfqz.d.ts → useProving-B4UsP7ku.d.ts} +9 -16
- package/dist/useProving.cjs.map +1 -1
- package/dist/useProving.d.cts +1 -1
- package/dist/useProving.d.ts +1 -1
- package/dist/useProving.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-SQ3PVZQH.js.map +0 -1
|
@@ -172,5 +172,5 @@ function useProving(options) {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
export { EnclaveCaptureFailed, EnclaveCaptureUnavailable, ManualSelectionRequired, useProving };
|
|
175
|
-
//# sourceMappingURL=chunk-
|
|
176
|
-
//# sourceMappingURL=chunk-
|
|
175
|
+
//# sourceMappingURL=chunk-V2ESGZXE.js.map
|
|
176
|
+
//# sourceMappingURL=chunk-V2ESGZXE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/useProving.ts"],"names":[],"mappings":"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","file":"chunk-V2ESGZXE.js","sourcesContent":["/**\n * @fileoverview Seam contract + state types for the proving orchestrator.\n *\n * `CaptureAdapter` is the swappable capture backend (extension+enclave today,\n * TLSNotary tomorrow). `ProvingState` is the single discriminated-union\n * machine that subsumes the live proof-substep machine (ProofPanel) and the\n * attestation lifecycle (submitStepDerivations). Monetary values stay bigint\n * in base units; the only stringified amount is `ProvingIntent.amount`, which\n * mirrors the existing on-the-wire BuyerTeeIntent shape (buyerTeeAttestation).\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** Transaction candidate the adapter surfaces for manual selection. Mirrors\n * the `MetadataMessage` fields the live TransactionSelector consumes; kept\n * structural so the package needs no @provex/extension-react import. */\nexport interface CaptureCandidate {\n amount: number | string\n currency?: string\n date?: string | number | Date\n originalIndex: number\n /**\n * Provider attestation params for this row (zkp2p 0.6.0 spec §4). These ARE\n * the attestation query params the enclave verifies against — they must be\n * forwarded (merged with the metadata index) on the buyerTee proof, not\n * dropped. Optional because the display path doesn't read them.\n */\n params?: Record<string, string | number | boolean>\n}\n\n/** Identity the attestor binds the attestation to. Mirrors the live\n * attestationIntent built in SubmitStep.tsx:333-342 / BuyerTeeIntent. */\nexport interface ProvingIntent {\n intentHash: Hex\n /** bigint base units, stringified (matches BuyerTeeIntent.amount). */\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n /** payeeDetailsHash */\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\n/** What the orchestrator hands a capture adapter. */\nexport interface CaptureRequest {\n /** Parent provider key, e.g. 'zelle', 'revolut'. */\n provider: string\n /**\n * Extension platform, e.g. 'venmo', or 'zelle' for every Zelle bank. With\n * {@link actionType} it names the hosted provider template the extension\n * fetches: `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`.\n */\n platform: string\n /** Action type, e.g. 'transfer_venmo' or 'transfer_zelle_chase'. */\n actionType: string\n /**\n * Platform for the ATTESTATION hop, when it differs from {@link platform}.\n * No provider needs this today: generic Zelle captures and attests as\n * `zelle` + `transfer_zelle_{bank}`. Omit it; consumers fall back to\n * {@link platform}.\n */\n attestationPlatform?: string\n /**\n * Action type for the ATTESTATION hop, when it differs from\n * {@link actionType}. See {@link attestationPlatform}. Omit when they\n * coincide; consumers fall back to {@link actionType}.\n */\n attestationActionType?: string\n intent: ProvingIntent\n /** UnifiedPaymentVerifier the attestation is re-signed for. */\n verifyingContract: Hex\n chainId: number\n /**\n * Settlement token for this intent (e.g. USDC on the target chain).\n * Used by the adapter to compute the fiat currency amount via\n * convertTokenInputToCurrency so auto-match works correctly for non-1:1 rates.\n */\n token: TokenInfo\n}\n\n/** Opaque per-backend capture state; the orchestrator passes it back unmodified. */\nexport interface CaptureHandle {\n readonly kind: string\n readonly material: unknown\n}\n\n/** Uniform capture output. `verificationData` names the signing witness to\n * the on-chain verifier (enclave signer for buyerTee — SubmitStep.tsx:473-475). */\nexport interface VerifiedAttestation {\n attestation: V3AttestationResponseObject\n verificationData: Hex\n}\n\n/** Resolver the UI provides so the adapter can collect a manual transaction\n * pick when auto-match fails (GenerateProofStep.tsx:391-404). */\nexport type SelectTransaction = (candidates: CaptureCandidate[]) => Promise<number>\n\n/** A pluggable capture backend. One per capture mechanism. */\nexport interface CaptureAdapter {\n /** Stable id for telemetry, e.g. 'extension-enclave'. */\n readonly kind: string\n /** Phase 1: authenticate + capture, returning opaque material to attest. */\n authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle>\n /** Phase 2: produce a verified attestation from the captured material. */\n attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation>\n}\n\n/** Inputs the orchestrator hook needs to drive the machine. */\nexport interface ProvingRequest {\n request: CaptureRequest\n /** Optional manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n}\n\n/**\n * The proving state machine (spec lines 141-151). `attest` auto-runs on\n * entry; `readyToFulfill` waits for the user's explicit Complete Order\n * action; `error.isSubmissionError` routes retry to Retry Submission\n * (post-fulfilment) vs Retry Verification (pre-fulfilment) — exactly the\n * live SubmitStep.tsx:931-955 routing.\n */\nexport type ProvingState =\n | { status: 'authenticate' }\n | { status: 'capture' }\n | { status: 'attest' }\n | { status: 'readyToFulfill'; attestation: VerifiedAttestation }\n | { status: 'fulfillIntent'; attestation: VerifiedAttestation }\n | { status: 'confirm'; attestation: VerifiedAttestation }\n | { status: 'sync'; attestation: VerifiedAttestation }\n | { status: 'complete'; attestation: VerifiedAttestation }\n | {\n status: 'error'\n message: string\n /** true ⇒ failure occurred during/after on-chain submission. */\n isSubmissionError: boolean\n attestation?: VerifiedAttestation\n /** Typed reason, set for cases the consumer must branch on. Currently\n * 'enclave-unavailable' (non-migrated provider → caller falls back to\n * Reclaim) and 'manual-selection-required' (auto-match failed, no\n * resolver supplied). Absent for ordinary verification/submission errors. */\n code?: 'enclave-unavailable' | 'manual-selection-required'\n }\n\n/**\n * Thrown by a capture adapter when the provider returned no buyerTee capture\n * (a non-migrated / Reclaim-only provider). The caller (UnifiedBuy) catches\n * this to fall back to its retained Reclaim branch — the package itself does\n * not implement Reclaim (decision (a), spec line 256).\n */\nexport class EnclaveCaptureUnavailable extends Error {\n readonly platform: string\n constructor(platform: string) {\n super(`No enclave (buyerTee) capture available for platform \"${platform}\".`)\n this.name = 'EnclaveCaptureUnavailable'\n this.platform = platform\n }\n}\n\n/**\n * Thrown by a capture adapter when the enclave capture was *attempted* but the\n * extension surfaced a failure reason (an `errorMessage`) instead of a capture\n * — most commonly because the extension could not reach the attestation enclave\n * (a buyer-side network / firewall / privacy- or ad-blocker problem).\n *\n * Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the\n * provider has no enclave capture path at all (non-migrated). This one is an\n * operational failure the user can usually fix and retry. Keeping them separate\n * stops a transient network failure from being misread as \"this provider isn't\n * supported\", and — if the Reclaim fallback is ever re-enabled — from wrongly\n * dumping the user out of the enclave path (this error carries no\n * `enclave-unavailable` code, so it surfaces as an ordinary retryable error).\n *\n * `detail` carries the raw extension `errorMessage` for logs and support;\n * `message` is the user-facing, actionable copy.\n */\nexport class EnclaveCaptureFailed extends Error {\n readonly platform: string\n readonly detail: string\n constructor(platform: string, detail: string) {\n super(\n 'We could not reach the verification service needed to confirm this ' +\n 'payment. A firewall, or a privacy or ad-blocking tool, may be blocking ' +\n 'it — try disabling blockers or switching networks, then verify again.',\n )\n this.name = 'EnclaveCaptureFailed'\n this.platform = platform\n this.detail = detail\n }\n}\n\n/**\n * Thrown by a capture adapter when auto-match failed and no `selectTransaction`\n * resolver was provided (e.g. a headless wallet). Carries the candidates so a\n * UI caller can render the manual selector and retry (spec lines 263-267).\n */\nexport class ManualSelectionRequired extends Error {\n readonly candidates: CaptureCandidate[]\n constructor(candidates: CaptureCandidate[]) {\n super('Manual transaction selection is required but no resolver was provided.')\n this.name = 'ManualSelectionRequired'\n this.candidates = candidates\n }\n}\n","/**\n * @fileoverview useProving — headless orchestrator for the enclave proving\n * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep\n * state machine (ProofPanel) with no presentation. Renders nothing; returns\n * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.\n *\n * Owns: the ProvingState machine, the nullifier double-spend check\n * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing\n * (SubmitStep.tsx:931-955), and the minimum-visible-state timings\n * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,\n * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { Hex } from 'viem'\nimport { extractNullifier, type V3AttestationResponseObject } from '@provex/utils'\nimport { sleep } from '@provex/utils/time'\nimport { EnclaveCaptureUnavailable, ManualSelectionRequired } from './types'\nimport type {\n CaptureAdapter,\n CaptureRequest,\n SelectTransaction,\n ProvingState,\n VerifiedAttestation,\n} from './types'\n\n/**\n * Minimal context object that buildWritableMethod passes to the per-call hooks.\n * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.\n * Only `hash` is consumed by this hook — the rest is opaque context.\n */\ninterface TxCallbackContext {\n chainId: number\n request: unknown\n}\n\n/** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +\n * WriteHookParams from @provex/react but declared here to keep @provex/proving\n * from depending on the React package's built dist (which the test environment\n * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.\n *\n * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call\n * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):\n * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void\n * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void\n */\ninterface FulfillParams {\n intentHash: Hex\n attestation: V3AttestationResponseObject\n verificationData: Hex\n postIntentHookData?: Hex\n onAwaitingSignature?: (info: TxCallbackContext) => void\n onTransactionHash?: (info: TxCallbackContext & { hash: Hex }) => void\n}\n\n/** The structural slice of ProveXClient the orchestrator depends on. Keeping\n * it structural lets tests pass a light fake and avoids importing the class. */\nexport interface ProvingClient {\n /**\n * The chain this rail operates on. Must equal request.chainId — useProving\n * refuses to check the nullifier or submit on any other chain. A nullifier is\n * only meaningful on its intended chain; checking it on (or submitting it to)\n * a different chain is a cross-chain double-spend / replay vector.\n */\n readonly chainId: number\n fulfillIntent: (params: FulfillParams) => Promise<{ hash: Hex; receipt: unknown }>\n checkNullifierUsed: (params: { paymentMethodId: Hex; nullifier: Hex }) => Promise<boolean | null>\n}\n\nexport interface UseProvingOptions {\n client: ProvingClient\n captureAdapter: CaptureAdapter\n request: CaptureRequest\n /** Payment method id for the nullifier registry lookup. */\n paymentMethodId: Hex\n /** Manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n /**\n * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.\n * Receives the raw TxContext — the hook does not inspect the argument.\n */\n onAwaitingSignature?: () => void\n /** Receives the raw tx hash extracted from the TxContext+hash payload. */\n onTransactionHash?: (hash: Hex) => void\n /** Called once when the flow reaches `complete`. */\n onComplete?: (attestation: V3AttestationResponseObject) => void\n}\n\nexport interface UseProvingReturn {\n state: ProvingState\n /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */\n authenticate: () => Promise<void>\n /** Phase 3: submit fulfillIntent → confirm → sync → complete. */\n fulfill: () => Promise<void>\n /** Re-run attestation after a pre-fulfilment error. */\n retryVerification: () => Promise<void>\n /** Re-run submission after a post-fulfilment error. */\n retrySubmission: () => Promise<void>\n}\n\n/** Minimum visible-state pacing from SubmitStep.tsx:404-417 — keep a state\n * on screen at least `floorMs` so the user perceives the step. */\nasync function withMinimumVisible(floorMs: number, started: number): Promise<void> {\n const elapsed = Date.now() - started\n if (elapsed < floorMs) await sleep(floorMs - elapsed)\n}\n\n/**\n * Detect a wallet-rejection error structurally (without importing ProveXError).\n * ProveXClient always re-wraps user rejections as ProveXError with\n * code === 'WALLET_REJECTED' before the error escapes fulfillIntent.\n */\nfunction isWalletRejection(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code: unknown }).code === 'WALLET_REJECTED'\n )\n}\n\nexport function useProving(options: UseProvingOptions): UseProvingReturn {\n const {\n client,\n captureAdapter,\n request,\n paymentMethodId,\n selectTransaction,\n onAwaitingSignature,\n onTransactionHash,\n onComplete,\n } = options\n\n const [state, setState] = useState<ProvingState>({ status: 'authenticate' })\n // Re-entry guard mirroring isRequestingAttestationRef (SubmitStep.tsx:199).\n const inFlightRef = useRef(false)\n // Separate in-flight guard for submit — prevents double on-chain spend from\n // two rapid fulfill() calls before a re-render.\n const submitInFlightRef = useRef(false)\n\n const runCaptureAndAttest = useCallback(async (): Promise<void> => {\n if (inFlightRef.current) return\n inFlightRef.current = true\n try {\n // Chain pinning: the nullifier must only ever be checked on the chain the\n // intent targets. A client wired to any other chain is refused before we\n // touch capture/attest/registry — never verify on the wrong chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n\n setState({ status: 'capture' })\n const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction })\n\n const attestStarted = Date.now()\n setState({ status: 'attest' })\n const verified = await captureAdapter.attest(handle, request)\n // SubmitStep.tsx:414-417: verifying state must be visible for ≥1000ms.\n await withMinimumVisible(1000, attestStarted)\n\n // Nullifier double-spend re-check — ADVISORY. The authoritative guards are\n // (1) the gate's buyerTee re-sign, which now verifies the nullifier is\n // unused and reserves it cross-chain before signing (buyer-tee.ts), and\n // (2) the on-chain contract, which reverts fulfillIntent on a used\n // nullifier. A valid attestation therefore already implies an unused\n // nullifier. So this browser re-check must NOT fail closed on a flaky\n // public RPC (which falsely blocked legitimate buys with \"registry\n // unreachable\"): block ONLY on a positive \"already used\"; pass otherwise.\n const nullifier = extractNullifier(verified.attestation)\n if (nullifier) {\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed === true) {\n throw new Error(\n 'This payment has already been verified. The nullifier has already been used onchain.',\n )\n }\n if (isUsed === null) {\n console.warn(\n '[useProving] nullifier re-check unavailable (browser RPC) — proceeding; the gate already verified and reserved it',\n )\n }\n } else {\n console.warn(\n '[useProving] could not extract nullifier in the browser — proceeding; the gate already verified it',\n )\n }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n const code =\n error instanceof EnclaveCaptureUnavailable\n ? ('enclave-unavailable' as const)\n : error instanceof ManualSelectionRequired\n ? ('manual-selection-required' as const)\n : undefined\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\n code,\n })\n } finally {\n inFlightRef.current = false\n }\n }, [captureAdapter, client, request, paymentMethodId, selectTransaction])\n\n const submit = useCallback(\n async (verified: VerifiedAttestation): Promise<void> => {\n // Re-entry guard: prevent two rapid submit calls from both calling\n // fulfillIntent (double on-chain spend). Mirrors inFlightRef pattern.\n if (submitInFlightRef.current) return\n submitInFlightRef.current = true\n\n const submitStarted = Date.now()\n setState({ status: 'fulfillIntent', attestation: verified })\n try {\n // Chain pinning (re-asserted at submit): the nullifier was verified on\n // request.chainId; it must only ever be submitted to that same chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n await client.fulfillIntent({\n intentHash: request.intent.intentHash,\n attestation: verified.attestation,\n verificationData: verified.verificationData,\n // Wrap consumer callbacks to match the real buildWritableMethod\n // call shapes (ProveXClient.ts ~890, ~903).\n onAwaitingSignature: onAwaitingSignature\n ? (_info: TxCallbackContext) => { onAwaitingSignature() }\n : undefined,\n // buildWritableMethod fires onTransactionHash with { ...ctx, hash }\n // (ProveXClient.ts ~903). Extract hash and forward to the consumer's\n // simpler (hash: Hex) => void API.\n onTransactionHash: onTransactionHash\n ? (info: TxCallbackContext & { hash: Hex }) => {\n // After the tx is broadcast (a separate async tick before the\n // receipt arrives), surface the confirm state so it is\n // observable by consumers.\n setState({ status: 'confirm', attestation: verified })\n onTransactionHash(info.hash)\n }\n : ((_info: TxCallbackContext & { hash: Hex }) => {\n setState({ status: 'confirm', attestation: verified })\n }),\n })\n await withMinimumVisible(500, submitStarted)\n\n // fulfillIntent already awaited receipt + indexer sync internally\n // (ProveXClient.ts:906-915). Surface sync as a real visible state\n // (confirm was set in onTransactionHash above); then await a\n // minimum-visible window before complete so sync is not batched away.\n setState({ status: 'sync', attestation: verified })\n await sleep(600)\n setState({ status: 'complete', attestation: verified })\n onComplete?.(verified.attestation)\n } catch (error) {\n // Wallet rejection: return to readyToFulfill so the user can retry\n // without losing the attestation (mirrors SubmitStep.tsx:571-573).\n if (isWalletRejection(error)) {\n setState({ status: 'readyToFulfill', attestation: verified })\n return\n }\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Submission failed',\n isSubmissionError: true,\n attestation: verified,\n })\n } finally {\n submitInFlightRef.current = false\n }\n },\n [client, request.intent.intentHash, onAwaitingSignature, onTransactionHash, onComplete],\n )\n\n const authenticate = useCallback(() => runCaptureAndAttest(), [runCaptureAndAttest])\n\n const fulfill = useCallback(async () => {\n if (state.status !== 'readyToFulfill') return\n await submit(state.attestation)\n }, [state, submit])\n\n const retryVerification = useCallback(async () => {\n setState({ status: 'authenticate' })\n await runCaptureAndAttest()\n }, [runCaptureAndAttest])\n\n const retrySubmission = useCallback(async () => {\n if (state.status !== 'error' || !state.attestation) return\n await submit(state.attestation)\n }, [state, submit])\n\n return { state, authenticate, fulfill, retryVerification, retrySubmission }\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,21 @@ function extractGateErrorMessage(detail) {
|
|
|
28
28
|
}
|
|
29
29
|
return trimmed;
|
|
30
30
|
}
|
|
31
|
+
var PAYMENT_ALREADY_USED_CODE = "NULLIFIER_ALREADY_USED";
|
|
32
|
+
var PaymentAlreadyUsedError = class extends Error {
|
|
33
|
+
constructor(message) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "PaymentAlreadyUsedError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function extractGateErrorCode(detail) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(detail);
|
|
41
|
+
return typeof parsed.error === "string" ? parsed.error : void 0;
|
|
42
|
+
} catch {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
31
46
|
async function requestBuyerTeeAttestation(args) {
|
|
32
47
|
const url = `${args.attestationBaseUrl}/api/v0/attestation/buyer-verify/${encodeURIComponent(args.platform)}/${encodeURIComponent(args.actionType)}`;
|
|
33
48
|
const response = await fetch(url, {
|
|
@@ -42,9 +57,11 @@ async function requestBuyerTeeAttestation(args) {
|
|
|
42
57
|
});
|
|
43
58
|
if (!response.ok) {
|
|
44
59
|
const detail = await response.text().catch(() => "");
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
60
|
+
const message = `Trusted-execution attestation failed (${response.status}): ${extractGateErrorMessage(detail) || response.statusText}`;
|
|
61
|
+
if (response.status === 409 && extractGateErrorCode(detail) === PAYMENT_ALREADY_USED_CODE) {
|
|
62
|
+
throw new PaymentAlreadyUsedError(message);
|
|
63
|
+
}
|
|
64
|
+
throw new Error(message);
|
|
48
65
|
}
|
|
49
66
|
const json = await response.json();
|
|
50
67
|
const responseObject = json.responseObject ?? json;
|
|
@@ -275,27 +292,22 @@ function useManualTransactionSelect() {
|
|
|
275
292
|
}, []);
|
|
276
293
|
return { selectTransaction, candidates, pick };
|
|
277
294
|
}
|
|
278
|
-
function
|
|
295
|
+
function captureRouteFor({
|
|
279
296
|
providerKey,
|
|
280
297
|
parentProvider
|
|
281
298
|
}) {
|
|
282
|
-
if (parentProvider
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
};
|
|
299
|
+
if (parentProvider === "zelle" && providerKey.includes("-")) {
|
|
300
|
+
const bank = providerKey.slice(parentProvider.length + 1);
|
|
301
|
+
return { platform: parentProvider, actionType: `transfer_${parentProvider}_${bank}` };
|
|
302
|
+
}
|
|
303
|
+
return { platform: payment.getExtensionPlatform(providerKey), actionType: `transfer_${parentProvider}` };
|
|
288
304
|
}
|
|
289
305
|
function buildCaptureRequest(input) {
|
|
290
306
|
const providerKey = input.subProvider ?? input.providerKey;
|
|
291
307
|
const parentProvider = providerKey.includes("-") ? providerKey.split("-")[0] : providerKey;
|
|
292
|
-
const platform = payment.getExtensionPlatform(providerKey);
|
|
293
|
-
const actionType = `transfer_${parentProvider}`;
|
|
294
308
|
return {
|
|
295
309
|
provider: input.providerKey,
|
|
296
|
-
|
|
297
|
-
actionType,
|
|
298
|
-
...attestationRouteFor({ providerKey, parentProvider }),
|
|
310
|
+
...captureRouteFor({ providerKey, parentProvider }),
|
|
299
311
|
verifyingContract: input.verifyingContract,
|
|
300
312
|
chainId: input.chainId,
|
|
301
313
|
token: input.token,
|
|
@@ -315,6 +327,7 @@ function buildCaptureRequest(input) {
|
|
|
315
327
|
exports.EnclaveCaptureFailed = EnclaveCaptureFailed;
|
|
316
328
|
exports.EnclaveCaptureUnavailable = EnclaveCaptureUnavailable;
|
|
317
329
|
exports.ManualSelectionRequired = ManualSelectionRequired;
|
|
330
|
+
exports.PaymentAlreadyUsedError = PaymentAlreadyUsedError;
|
|
318
331
|
exports.asBuyerTeeProof = asBuyerTeeProof;
|
|
319
332
|
exports.buildCaptureRequest = buildCaptureRequest;
|
|
320
333
|
exports.computePartialFulfillment = computePartialFulfillment;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buyerTeeAttestation.ts","../src/types.ts","../src/derivations.ts","../src/useProving.ts","../src/useManualTransactionSelect.ts","../src/buildCaptureRequest.ts"],"names":["calculateNetAmount","calculateFeeAmount","convertTokenInputToCurrency","formatUnits","sleep","useState","useRef","useCallback","extractNullifier","getExtensionPlatform"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @fileoverview Buyer trusted-execution attestation client.\n *\n * In PeerAuth extension 0.5.2+, capture produces a buyerTee proof —\n * `{ proofType: 'buyerTee', encryptedSessionMaterial, params }` — where the\n * bank session is already encrypted toward the enclave (cookies never reach\n * our page in plaintext). Instead of re-signing a Reclaim proof through our\n * gate, we hand that material to zkp2p's trusted-execution attestation\n * service, which verifies the payment inside an Amazon Web Services Nitro\n * enclave and signs the attestation directly.\n *\n * The response `responseObject` is the same `V3AttestationResponseObject`\n * shape our gate returns, so it flows through the existing\n * `encodeV3PaymentProof` path unchanged.\n *\n * Moved verbatim from `@provex/ui` (packages/ui/src/lib/buyerTeeAttestation.ts)\n * with the base URL changed from a `@provex/ui` config import to an injected\n * `attestationBaseUrl` argument so this package carries no `@provex/ui` dep.\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\n\n/**\n * Runtime shape of a buyerTee proof emitted by the PeerAuth extension.\n * Narrow with {@link asBuyerTeeProof} before use.\n */\nexport interface BuyerTeeProof {\n proofType: 'buyerTee'\n /** Session material already encrypted toward the enclave (opaque to us). */\n encryptedSessionMaterial: string\n /** Platform-specific params, e.g. Revolut `{ index }`, Chase `{ index }`. */\n params: unknown\n actionType?: string\n actionPlatform?: string\n}\n\n/**\n * Narrow an extension proof to a buyerTee proof, or null when it is the\n * legacy Reclaim shape (or malformed).\n */\nexport function asBuyerTeeProof(proof: unknown): BuyerTeeProof | null {\n if (!proof || typeof proof !== 'object') return null\n const candidate = proof as Record<string, unknown>\n if (candidate.proofType !== 'buyerTee') return null\n if (typeof candidate.encryptedSessionMaterial !== 'string') return null\n return candidate as unknown as BuyerTeeProof\n}\n\n/** Intent details the enclave binds the attestation to. */\nexport interface BuyerTeeIntent {\n intentHash: Hex\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\nexport interface RequestBuyerTeeAttestationArgs {\n /** Gate/attestation origin (was `gateForwarderBaseUrl` in @provex/ui). */\n attestationBaseUrl: string\n /**\n * Platform for the ATTESTATION route — NOT necessarily the capture platform.\n * For generic Zelle this is `zelle`, even though capture used the bank\n * (`chase`). See `CaptureRequest.attestationPlatform`.\n */\n platform: string\n /**\n * Action type for the ATTESTATION route — NOT necessarily the capture action\n * type. For generic Zelle this is bank-specific (`transfer_zelle_chase`),\n * even though capture used `transfer_zelle`. See\n * `CaptureRequest.attestationActionType`.\n */\n actionType: string\n /** Encrypted session material from the buyerTee capture. */\n encryptedSessionMaterial: string\n /** Platform params from the capture (e.g. `{ index }`). */\n params: unknown\n /** PulseChain UnifiedPaymentVerifier the gate re-signs the attestation for. */\n verifyingContract: string\n intent: BuyerTeeIntent\n}\n\n/**\n * Pull a human-readable reason out of the gate's error body.\n *\n * The gate returns `{ success: false, error, message }` JSON, where `message`\n * is the enclave's own words (e.g. \"…payment not COMPLETE\"). Surface that. If\n * the body is not that shape — most notably a Cloudflare/Caddy HTML error page\n * when the gate is genuinely unreachable — fall back to a short marker rather\n * than dumping a page of markup into the thrown message.\n */\nfunction extractGateErrorMessage(detail: string): string {\n const trimmed = detail.trim()\n if (!trimmed) return ''\n try {\n const parsed = JSON.parse(trimmed) as { message?: unknown; error?: unknown }\n if (typeof parsed.message === 'string' && parsed.message) return parsed.message\n if (typeof parsed.error === 'string' && parsed.error) return parsed.error\n } catch {\n // Not JSON — an HTML gateway page (e.g. a raw Cloudflare 502) carries no\n // useful reason for the user, so collapse it instead of echoing markup.\n if (/^\\s*<(?:!doctype|html)/i.test(trimmed)) return 'the attestation gateway is unavailable'\n }\n return trimmed\n}\n\n/**\n * Verify a buyer payment via the gate. The gate proxies the encrypted capture\n * to zkp2p's enclave, then re-signs the resulting attestation for PulseChain\n * with our witness key — the enclave itself refuses to sign for 369.\n *\n * Throws with the gate's error text on a non-2xx response.\n */\nexport async function requestBuyerTeeAttestation(\n args: RequestBuyerTeeAttestationArgs,\n): Promise<V3AttestationResponseObject> {\n const url = `${args.attestationBaseUrl}/api/v0/attestation/buyer-verify/${encodeURIComponent(args.platform)}/${encodeURIComponent(args.actionType)}`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n encryptedSessionMaterial: args.encryptedSessionMaterial,\n params: args.params,\n verifyingContract: args.verifyingContract,\n intent: args.intent,\n }),\n })\n\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `Trusted-execution attestation failed (${response.status}): ${extractGateErrorMessage(detail) || response.statusText}`,\n )\n }\n\n const json = (await response.json()) as\n | { responseObject?: V3AttestationResponseObject }\n | V3AttestationResponseObject\n const responseObject =\n (json as { responseObject?: V3AttestationResponseObject }).responseObject ??\n (json as V3AttestationResponseObject)\n\n if (!responseObject?.typedDataValue || !responseObject?.signature) {\n throw new Error('Trusted-execution attestation response missing typedDataValue/signature')\n }\n return responseObject\n}\n","/**\n * @fileoverview Seam contract + state types for the proving orchestrator.\n *\n * `CaptureAdapter` is the swappable capture backend (extension+enclave today,\n * TLSNotary tomorrow). `ProvingState` is the single discriminated-union\n * machine that subsumes the live proof-substep machine (ProofPanel) and the\n * attestation lifecycle (submitStepDerivations). Monetary values stay bigint\n * in base units; the only stringified amount is `ProvingIntent.amount`, which\n * mirrors the existing on-the-wire BuyerTeeIntent shape (buyerTeeAttestation).\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** Transaction candidate the adapter surfaces for manual selection. Mirrors\n * the `MetadataMessage` fields the live TransactionSelector consumes; kept\n * structural so the package needs no @provex/extension-react import. */\nexport interface CaptureCandidate {\n amount: number | string\n currency?: string\n date?: string | number | Date\n originalIndex: number\n /**\n * Provider attestation params for this row (zkp2p 0.6.0 spec §4). These ARE\n * the attestation query params the enclave verifies against — they must be\n * forwarded (merged with the metadata index) on the buyerTee proof, not\n * dropped. Optional because the display path doesn't read them.\n */\n params?: Record<string, string | number | boolean>\n}\n\n/** Identity the attestor binds the attestation to. Mirrors the live\n * attestationIntent built in SubmitStep.tsx:333-342 / BuyerTeeIntent. */\nexport interface ProvingIntent {\n intentHash: Hex\n /** bigint base units, stringified (matches BuyerTeeIntent.amount). */\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n /** payeeDetailsHash */\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\n/** What the orchestrator hands a capture adapter. */\nexport interface CaptureRequest {\n /** Parent provider key, e.g. 'zelle', 'revolut'. */\n provider: string\n /** Extension platform slug, e.g. 'chase'. */\n platform: string\n /** Action type, e.g. 'transfer_zelle'. */\n actionType: string\n /**\n * Platform for the ATTESTATION hop, when it differs from {@link platform}.\n *\n * Capture and attestation use two different namespaces. Capture addresses a\n * provider template (`{platform}/{actionType}.json`), so a Zelle bank is\n * `chase` + `transfer_zelle`. Attestation addresses the enclave's verify\n * route, which for Zelle demands the GENERIC platform plus a BANK-SPECIFIC\n * action type — `zelle` + `transfer_zelle_chase`.\n *\n * Ground truth is zkp2p's own `resolveBuyerTeeAttestationRoute`\n * (@zkp2p/sdk 0.9.0), which throws otherwise:\n * 'Generic Zelle buyer TEE fulfillment requires actionPlatform \"zelle\"\n * and a bank-specific transfer_zelle_* actionType.'\n *\n * Omit for providers where the two namespaces coincide (venmo, cashapp, …);\n * consumers fall back to {@link platform}.\n */\n attestationPlatform?: string\n /**\n * Action type for the ATTESTATION hop, when it differs from\n * {@link actionType}. See {@link attestationPlatform}. Omit when they\n * coincide; consumers fall back to {@link actionType}.\n */\n attestationActionType?: string\n intent: ProvingIntent\n /** UnifiedPaymentVerifier the attestation is re-signed for. */\n verifyingContract: Hex\n chainId: number\n /**\n * Settlement token for this intent (e.g. USDC on the target chain).\n * Used by the adapter to compute the fiat currency amount via\n * convertTokenInputToCurrency so auto-match works correctly for non-1:1 rates.\n */\n token: TokenInfo\n}\n\n/** Opaque per-backend capture state; the orchestrator passes it back unmodified. */\nexport interface CaptureHandle {\n readonly kind: string\n readonly material: unknown\n}\n\n/** Uniform capture output. `verificationData` names the signing witness to\n * the on-chain verifier (enclave signer for buyerTee — SubmitStep.tsx:473-475). */\nexport interface VerifiedAttestation {\n attestation: V3AttestationResponseObject\n verificationData: Hex\n}\n\n/** Resolver the UI provides so the adapter can collect a manual transaction\n * pick when auto-match fails (GenerateProofStep.tsx:391-404). */\nexport type SelectTransaction = (candidates: CaptureCandidate[]) => Promise<number>\n\n/** A pluggable capture backend. One per capture mechanism. */\nexport interface CaptureAdapter {\n /** Stable id for telemetry, e.g. 'extension-enclave'. */\n readonly kind: string\n /** Phase 1: authenticate + capture, returning opaque material to attest. */\n authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle>\n /** Phase 2: produce a verified attestation from the captured material. */\n attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation>\n}\n\n/** Inputs the orchestrator hook needs to drive the machine. */\nexport interface ProvingRequest {\n request: CaptureRequest\n /** Optional manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n}\n\n/**\n * The proving state machine (spec lines 141-151). `attest` auto-runs on\n * entry; `readyToFulfill` waits for the user's explicit Complete Order\n * action; `error.isSubmissionError` routes retry to Retry Submission\n * (post-fulfilment) vs Retry Verification (pre-fulfilment) — exactly the\n * live SubmitStep.tsx:931-955 routing.\n */\nexport type ProvingState =\n | { status: 'authenticate' }\n | { status: 'capture' }\n | { status: 'attest' }\n | { status: 'readyToFulfill'; attestation: VerifiedAttestation }\n | { status: 'fulfillIntent'; attestation: VerifiedAttestation }\n | { status: 'confirm'; attestation: VerifiedAttestation }\n | { status: 'sync'; attestation: VerifiedAttestation }\n | { status: 'complete'; attestation: VerifiedAttestation }\n | {\n status: 'error'\n message: string\n /** true ⇒ failure occurred during/after on-chain submission. */\n isSubmissionError: boolean\n attestation?: VerifiedAttestation\n /** Typed reason, set for cases the consumer must branch on. Currently\n * 'enclave-unavailable' (non-migrated provider → caller falls back to\n * Reclaim) and 'manual-selection-required' (auto-match failed, no\n * resolver supplied). Absent for ordinary verification/submission errors. */\n code?: 'enclave-unavailable' | 'manual-selection-required'\n }\n\n/**\n * Thrown by a capture adapter when the provider returned no buyerTee capture\n * (a non-migrated / Reclaim-only provider). The caller (UnifiedBuy) catches\n * this to fall back to its retained Reclaim branch — the package itself does\n * not implement Reclaim (decision (a), spec line 256).\n */\nexport class EnclaveCaptureUnavailable extends Error {\n readonly platform: string\n constructor(platform: string) {\n super(`No enclave (buyerTee) capture available for platform \"${platform}\".`)\n this.name = 'EnclaveCaptureUnavailable'\n this.platform = platform\n }\n}\n\n/**\n * Thrown by a capture adapter when the enclave capture was *attempted* but the\n * extension surfaced a failure reason (an `errorMessage`) instead of a capture\n * — most commonly because the extension could not reach the attestation enclave\n * (a buyer-side network / firewall / privacy- or ad-blocker problem).\n *\n * Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the\n * provider has no enclave capture path at all (non-migrated). This one is an\n * operational failure the user can usually fix and retry. Keeping them separate\n * stops a transient network failure from being misread as \"this provider isn't\n * supported\", and — if the Reclaim fallback is ever re-enabled — from wrongly\n * dumping the user out of the enclave path (this error carries no\n * `enclave-unavailable` code, so it surfaces as an ordinary retryable error).\n *\n * `detail` carries the raw extension `errorMessage` for logs and support;\n * `message` is the user-facing, actionable copy.\n */\nexport class EnclaveCaptureFailed extends Error {\n readonly platform: string\n readonly detail: string\n constructor(platform: string, detail: string) {\n super(\n 'We could not reach the verification service needed to confirm this ' +\n 'payment. A firewall, or a privacy or ad-blocking tool, may be blocking ' +\n 'it — try disabling blockers or switching networks, then verify again.',\n )\n this.name = 'EnclaveCaptureFailed'\n this.platform = platform\n this.detail = detail\n }\n}\n\n/**\n * Thrown by a capture adapter when auto-match failed and no `selectTransaction`\n * resolver was provided (e.g. a headless wallet). Carries the candidates so a\n * UI caller can render the manual selector and retry (spec lines 263-267).\n */\nexport class ManualSelectionRequired extends Error {\n readonly candidates: CaptureCandidate[]\n constructor(candidates: CaptureCandidate[]) {\n super('Manual transaction selection is required but no resolver was provided.')\n this.name = 'ManualSelectionRequired'\n this.candidates = candidates\n }\n}\n","/**\n * @fileoverview Pure proving derivations moved from @provex/ui\n * (packages/ui/src/components/UnifiedBuy/ProofPanel/submitStepDerivations.ts).\n *\n * Bigint throughout — see the `money-values-stay-bigint-until-display` rule.\n * The only string conversions happen at the formatter boundary (formatUnits),\n * which is the display layer.\n */\n\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\nimport { formatUnits } from '@provex/utils/units'\nimport { convertTokenInputToCurrency } from '@provex/utils/conversionRates'\nimport { calculateFeeAmount, calculateNetAmount } from '@provex/utils/fees'\n\nexport interface PartialFulfillment {\n /** Gross USDC the gate authorized for release (typedData.releaseAmount). */\n grossReleased: bigint\n /** Net USDC the buyer actually receives (grossReleased − fee). */\n netToBuyer: bigint\n /** Protocol fee in USDC base units. 0n when no fee applies. */\n protocolFee: bigint\n /** Locked USDC returned to the maker (intent.amount − grossReleased). */\n returnedToMaker: bigint\n contractedFiatDisplay: string\n attestedFiatDisplay: string\n netToBuyerDisplay: string\n returnedToMakerDisplay: string\n /** null when protocolFee === 0n so the caller can skip rendering the row. */\n protocolFeeDisplay: string | null\n}\n\n/**\n * Partial-fulfilment breakdown derived from the verified attestation.\n *\n * Returns `null` when:\n * - attestation isn't present (nothing to compare)\n * - releaseAmount >= intent.amount (full settlement, no partial story)\n * - intent/tokenInfo missing (defensive)\n */\nexport function computePartialFulfillment({\n attestation,\n intentAmount,\n conversionRate,\n tokenInfo,\n feeBps,\n}: {\n attestation: V3AttestationResponseObject | null\n intentAmount: bigint | null | undefined\n conversionRate: bigint | null | undefined\n tokenInfo: TokenInfo | null\n feeBps: bigint | null | undefined\n}): PartialFulfillment | null {\n if (!attestation || !intentAmount || !conversionRate || !tokenInfo) return null\n const grossReleased = BigInt(attestation.typedDataValue.releaseAmount)\n if (grossReleased >= intentAmount) return null\n\n const returnedToMaker = intentAmount - grossReleased\n const effectiveFeeBps = feeBps ?? 0n\n const netToBuyer =\n effectiveFeeBps > 0n\n ? calculateNetAmount({ grossAmount: grossReleased, feeBps: effectiveFeeBps })\n : grossReleased\n const protocolFee =\n effectiveFeeBps > 0n\n ? calculateFeeAmount({ amount: grossReleased, feeBps: effectiveFeeBps })\n : 0n\n\n const contractedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: intentAmount,\n rate: conversionRate,\n })\n const attestedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: grossReleased,\n rate: conversionRate,\n })\n\n const formatTokenDisplay = (value: bigint): string =>\n formatUnits(value, {\n decimals: tokenInfo.decimals,\n truncateDecimals: 2,\n padDecimals: 2,\n })\n\n return {\n grossReleased,\n netToBuyer,\n protocolFee,\n returnedToMaker,\n contractedFiatDisplay: formatTokenDisplay(contractedFiatRaw),\n attestedFiatDisplay: formatTokenDisplay(attestedFiatRaw),\n netToBuyerDisplay: formatTokenDisplay(netToBuyer),\n returnedToMakerDisplay: formatTokenDisplay(returnedToMaker),\n protocolFeeDisplay: protocolFee > 0n ? formatTokenDisplay(protocolFee) : null,\n }\n}\n","/**\n * @fileoverview useProving — headless orchestrator for the enclave proving\n * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep\n * state machine (ProofPanel) with no presentation. Renders nothing; returns\n * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.\n *\n * Owns: the ProvingState machine, the nullifier double-spend check\n * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing\n * (SubmitStep.tsx:931-955), and the minimum-visible-state timings\n * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,\n * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { Hex } from 'viem'\nimport { extractNullifier, type V3AttestationResponseObject } from '@provex/utils'\nimport { sleep } from '@provex/utils/time'\nimport { EnclaveCaptureUnavailable, ManualSelectionRequired } from './types'\nimport type {\n CaptureAdapter,\n CaptureRequest,\n SelectTransaction,\n ProvingState,\n VerifiedAttestation,\n} from './types'\n\n/**\n * Minimal context object that buildWritableMethod passes to the per-call hooks.\n * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.\n * Only `hash` is consumed by this hook — the rest is opaque context.\n */\ninterface TxCallbackContext {\n chainId: number\n request: unknown\n}\n\n/** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +\n * WriteHookParams from @provex/react but declared here to keep @provex/proving\n * from depending on the React package's built dist (which the test environment\n * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.\n *\n * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call\n * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):\n * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void\n * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void\n */\ninterface FulfillParams {\n intentHash: Hex\n attestation: V3AttestationResponseObject\n verificationData: Hex\n postIntentHookData?: Hex\n onAwaitingSignature?: (info: TxCallbackContext) => void\n onTransactionHash?: (info: TxCallbackContext & { hash: Hex }) => void\n}\n\n/** The structural slice of ProveXClient the orchestrator depends on. Keeping\n * it structural lets tests pass a light fake and avoids importing the class. */\nexport interface ProvingClient {\n /**\n * The chain this rail operates on. Must equal request.chainId — useProving\n * refuses to check the nullifier or submit on any other chain. A nullifier is\n * only meaningful on its intended chain; checking it on (or submitting it to)\n * a different chain is a cross-chain double-spend / replay vector.\n */\n readonly chainId: number\n fulfillIntent: (params: FulfillParams) => Promise<{ hash: Hex; receipt: unknown }>\n checkNullifierUsed: (params: { paymentMethodId: Hex; nullifier: Hex }) => Promise<boolean | null>\n}\n\nexport interface UseProvingOptions {\n client: ProvingClient\n captureAdapter: CaptureAdapter\n request: CaptureRequest\n /** Payment method id for the nullifier registry lookup. */\n paymentMethodId: Hex\n /** Manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n /**\n * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.\n * Receives the raw TxContext — the hook does not inspect the argument.\n */\n onAwaitingSignature?: () => void\n /** Receives the raw tx hash extracted from the TxContext+hash payload. */\n onTransactionHash?: (hash: Hex) => void\n /** Called once when the flow reaches `complete`. */\n onComplete?: (attestation: V3AttestationResponseObject) => void\n}\n\nexport interface UseProvingReturn {\n state: ProvingState\n /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */\n authenticate: () => Promise<void>\n /** Phase 3: submit fulfillIntent → confirm → sync → complete. */\n fulfill: () => Promise<void>\n /** Re-run attestation after a pre-fulfilment error. */\n retryVerification: () => Promise<void>\n /** Re-run submission after a post-fulfilment error. */\n retrySubmission: () => Promise<void>\n}\n\n/** Minimum visible-state pacing from SubmitStep.tsx:404-417 — keep a state\n * on screen at least `floorMs` so the user perceives the step. */\nasync function withMinimumVisible(floorMs: number, started: number): Promise<void> {\n const elapsed = Date.now() - started\n if (elapsed < floorMs) await sleep(floorMs - elapsed)\n}\n\n/**\n * Detect a wallet-rejection error structurally (without importing ProveXError).\n * ProveXClient always re-wraps user rejections as ProveXError with\n * code === 'WALLET_REJECTED' before the error escapes fulfillIntent.\n */\nfunction isWalletRejection(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code: unknown }).code === 'WALLET_REJECTED'\n )\n}\n\nexport function useProving(options: UseProvingOptions): UseProvingReturn {\n const {\n client,\n captureAdapter,\n request,\n paymentMethodId,\n selectTransaction,\n onAwaitingSignature,\n onTransactionHash,\n onComplete,\n } = options\n\n const [state, setState] = useState<ProvingState>({ status: 'authenticate' })\n // Re-entry guard mirroring isRequestingAttestationRef (SubmitStep.tsx:199).\n const inFlightRef = useRef(false)\n // Separate in-flight guard for submit — prevents double on-chain spend from\n // two rapid fulfill() calls before a re-render.\n const submitInFlightRef = useRef(false)\n\n const runCaptureAndAttest = useCallback(async (): Promise<void> => {\n if (inFlightRef.current) return\n inFlightRef.current = true\n try {\n // Chain pinning: the nullifier must only ever be checked on the chain the\n // intent targets. A client wired to any other chain is refused before we\n // touch capture/attest/registry — never verify on the wrong chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n\n setState({ status: 'capture' })\n const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction })\n\n const attestStarted = Date.now()\n setState({ status: 'attest' })\n const verified = await captureAdapter.attest(handle, request)\n // SubmitStep.tsx:414-417: verifying state must be visible for ≥1000ms.\n await withMinimumVisible(1000, attestStarted)\n\n // Nullifier double-spend re-check — ADVISORY. The authoritative guards are\n // (1) the gate's buyerTee re-sign, which now verifies the nullifier is\n // unused and reserves it cross-chain before signing (buyer-tee.ts), and\n // (2) the on-chain contract, which reverts fulfillIntent on a used\n // nullifier. A valid attestation therefore already implies an unused\n // nullifier. So this browser re-check must NOT fail closed on a flaky\n // public RPC (which falsely blocked legitimate buys with \"registry\n // unreachable\"): block ONLY on a positive \"already used\"; pass otherwise.\n const nullifier = extractNullifier(verified.attestation)\n if (nullifier) {\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed === true) {\n throw new Error(\n 'This payment has already been verified. The nullifier has already been used onchain.',\n )\n }\n if (isUsed === null) {\n console.warn(\n '[useProving] nullifier re-check unavailable (browser RPC) — proceeding; the gate already verified and reserved it',\n )\n }\n } else {\n console.warn(\n '[useProving] could not extract nullifier in the browser — proceeding; the gate already verified it',\n )\n }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n const code =\n error instanceof EnclaveCaptureUnavailable\n ? ('enclave-unavailable' as const)\n : error instanceof ManualSelectionRequired\n ? ('manual-selection-required' as const)\n : undefined\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\n code,\n })\n } finally {\n inFlightRef.current = false\n }\n }, [captureAdapter, client, request, paymentMethodId, selectTransaction])\n\n const submit = useCallback(\n async (verified: VerifiedAttestation): Promise<void> => {\n // Re-entry guard: prevent two rapid submit calls from both calling\n // fulfillIntent (double on-chain spend). Mirrors inFlightRef pattern.\n if (submitInFlightRef.current) return\n submitInFlightRef.current = true\n\n const submitStarted = Date.now()\n setState({ status: 'fulfillIntent', attestation: verified })\n try {\n // Chain pinning (re-asserted at submit): the nullifier was verified on\n // request.chainId; it must only ever be submitted to that same chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n await client.fulfillIntent({\n intentHash: request.intent.intentHash,\n attestation: verified.attestation,\n verificationData: verified.verificationData,\n // Wrap consumer callbacks to match the real buildWritableMethod\n // call shapes (ProveXClient.ts ~890, ~903).\n onAwaitingSignature: onAwaitingSignature\n ? (_info: TxCallbackContext) => { onAwaitingSignature() }\n : undefined,\n // buildWritableMethod fires onTransactionHash with { ...ctx, hash }\n // (ProveXClient.ts ~903). Extract hash and forward to the consumer's\n // simpler (hash: Hex) => void API.\n onTransactionHash: onTransactionHash\n ? (info: TxCallbackContext & { hash: Hex }) => {\n // After the tx is broadcast (a separate async tick before the\n // receipt arrives), surface the confirm state so it is\n // observable by consumers.\n setState({ status: 'confirm', attestation: verified })\n onTransactionHash(info.hash)\n }\n : ((_info: TxCallbackContext & { hash: Hex }) => {\n setState({ status: 'confirm', attestation: verified })\n }),\n })\n await withMinimumVisible(500, submitStarted)\n\n // fulfillIntent already awaited receipt + indexer sync internally\n // (ProveXClient.ts:906-915). Surface sync as a real visible state\n // (confirm was set in onTransactionHash above); then await a\n // minimum-visible window before complete so sync is not batched away.\n setState({ status: 'sync', attestation: verified })\n await sleep(600)\n setState({ status: 'complete', attestation: verified })\n onComplete?.(verified.attestation)\n } catch (error) {\n // Wallet rejection: return to readyToFulfill so the user can retry\n // without losing the attestation (mirrors SubmitStep.tsx:571-573).\n if (isWalletRejection(error)) {\n setState({ status: 'readyToFulfill', attestation: verified })\n return\n }\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Submission failed',\n isSubmissionError: true,\n attestation: verified,\n })\n } finally {\n submitInFlightRef.current = false\n }\n },\n [client, request.intent.intentHash, onAwaitingSignature, onTransactionHash, onComplete],\n )\n\n const authenticate = useCallback(() => runCaptureAndAttest(), [runCaptureAndAttest])\n\n const fulfill = useCallback(async () => {\n if (state.status !== 'readyToFulfill') return\n await submit(state.attestation)\n }, [state, submit])\n\n const retryVerification = useCallback(async () => {\n setState({ status: 'authenticate' })\n await runCaptureAndAttest()\n }, [runCaptureAndAttest])\n\n const retrySubmission = useCallback(async () => {\n if (state.status !== 'error' || !state.attestation) return\n await submit(state.attestation)\n }, [state, submit])\n\n return { state, authenticate, fulfill, retryVerification, retrySubmission }\n}\n","import { useCallback, useRef, useState } from 'react'\nimport type { CaptureCandidate, SelectTransaction } from './types'\n\nexport interface ManualTransactionSelect {\n /**\n * Resolver injected into useProving — the enclave adapter calls this when\n * auto-match of the bank transaction fails (extensionEnclave.ts:189-190).\n */\n selectTransaction: SelectTransaction\n /**\n * Non-null while the adapter is awaiting a pick; drives the\n * TransactionSelector rendered inside GenerateProofStep.\n */\n candidates: CaptureCandidate[] | null\n /**\n * Called by TransactionSelector with the chosen candidate's originalIndex.\n * Resolves the pending promise and resets candidates to null.\n */\n pick: (originalIndex: number) => void\n}\n\n/**\n * Bridge the framework-agnostic {@link SelectTransaction} resolver the enclave\n * adapter expects (extensionEnclave.ts:189-190) to React state the\n * GenerateProofStep TransactionSelector can render.\n *\n * The adapter awaits the promise returned by `selectTransaction`; the user's\n * call to `pick` resolves it with the chosen `originalIndex`.\n *\n * `useRef` is justified here exclusively for holding the pending promise\n * resolver across renders without triggering a re-render cycle — the single\n * sanctioned use per the react-state-preferences memory.\n */\nexport function useManualTransactionSelect(): ManualTransactionSelect {\n const [candidates, setCandidates] = useState<CaptureCandidate[] | null>(null)\n const resolveRef = useRef<((originalIndex: number) => void) | null>(null)\n\n const selectTransaction = useCallback<SelectTransaction>((next) => {\n return new Promise<number>((resolve) => {\n resolveRef.current = resolve\n setCandidates(next)\n })\n }, [])\n\n const pick = useCallback((originalIndex: number) => {\n resolveRef.current?.(originalIndex)\n resolveRef.current = null\n setCandidates(null)\n }, [])\n\n return { selectTransaction, candidates, pick }\n}\n","/**\n * @fileoverview Pure builder that assembles a {@link CaptureRequest} from\n * resolved order data.\n *\n * Hoisted verbatim from the intent-assembly block in SubmitStep.tsx:312-342\n * so the mapping is unit-tested in isolation and reusable by the proving shell.\n *\n * Monetary values stay bigint in base units through the entire call stack;\n * this is the single boundary where they are stringified for the attestor\n * wire format.\n */\n\nimport type { Hex } from 'viem'\nimport type { CaptureRequest } from './types'\nimport { getExtensionPlatform } from '@provex/utils/payment'\nimport type { ProviderKey, SubProviderKey } from '@provex/utils/payment'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** All resolved order fields required to build a {@link CaptureRequest}. */\nexport interface BuildCaptureRequestInput {\n /** On-chain intent hash. */\n intentHash: Hex\n /** Chain the intent lives on. */\n chainId: number\n /** Bank-level sub-provider, e.g. `'zelle-chase'`, or `null` for top-level providers. */\n subProvider: SubProviderKey | null\n /** Provider key from the deposit. May itself be a sub-provider key (contains a dash). */\n providerKey: ProviderKey | SubProviderKey\n /** Address of the UnifiedPaymentVerifier contract for this chain. */\n verifyingContract: Hex\n /** payeeDetailsHash from the matched verifier record. */\n payeeDetailsHash: Hex\n /** Settlement token for this intent (used by the adapter for amount conversion). */\n token: TokenInfo\n /** Resolved intent fields, monetary values in bigint base units. */\n intent: {\n amount: bigint\n timestamp: bigint\n paymentMethod: Hex | undefined\n fiatCurrency: Hex\n conversionRate: bigint\n }\n}\n\n/**\n * Attestation-route overrides for providers whose attestation namespace differs\n * from their capture namespace.\n *\n * Only generic-Zelle does today: capture addresses the bank's template\n * (`chase` + `transfer_zelle`) while the enclave's verify route demands the\n * generic platform plus a bank-specific action type (`zelle` +\n * `transfer_zelle_chase`). zkp2p's `resolveBuyerTeeAttestationRoute`\n * (@zkp2p/sdk 0.9.0) hard-fails anything else for Zelle, and its accepted set\n * is exactly `transfer_zelle_{bofa,chase,citi}` — which matches our\n * `zelle-{bofa,chase,citi}` sub-provider keys one-for-one.\n *\n * Returns an empty object for every other provider, where the two namespaces\n * coincide and the fallbacks in {@link CaptureRequest} already do the right\n * thing. Keeping the override absent (rather than echoing platform/actionType)\n * means \"no override needed\" stays distinguishable from \"overridden\".\n *\n * @param providerKey - Sub-provider key when present, else the provider key.\n * @param parentProvider - The portion before the dash, e.g. 'zelle'.\n */\nfunction attestationRouteFor({\n providerKey,\n parentProvider,\n}: {\n providerKey: string\n parentProvider: string\n}): Pick<CaptureRequest, 'attestationPlatform' | 'attestationActionType'> {\n if (parentProvider !== 'zelle' || !providerKey.includes('-')) return {}\n const bank = providerKey.slice(parentProvider.length + 1)\n return {\n attestationPlatform: parentProvider,\n attestationActionType: `transfer_${parentProvider}_${bank}`,\n }\n}\n\n/**\n * Build the {@link CaptureRequest} from resolved order data.\n *\n * Pure hoist of the intent-assembly block in SubmitStep.tsx:312-342.\n * No side effects, no I/O, no React. Safe to call in any environment.\n *\n * @param input - All resolved order fields needed to build the request.\n * @returns A fully-formed {@link CaptureRequest} ready for a {@link CaptureAdapter}.\n */\nexport function buildCaptureRequest(input: BuildCaptureRequestInput): CaptureRequest {\n const providerKey = input.subProvider ?? input.providerKey\n const parentProvider = providerKey.includes('-')\n ? providerKey.split('-')[0]\n : providerKey\n\n const platform = getExtensionPlatform(providerKey)\n const actionType = `transfer_${parentProvider}`\n\n return {\n provider: input.providerKey,\n platform,\n actionType,\n ...attestationRouteFor({ providerKey, parentProvider }),\n verifyingContract: input.verifyingContract,\n chainId: input.chainId,\n token: input.token,\n intent: {\n intentHash: input.intentHash,\n amount: input.intent.amount.toString(),\n timestampMs: (input.intent.timestamp * 1000n).toString(),\n paymentMethod: input.intent.paymentMethod ?? ('0x' as Hex),\n fiatCurrency: input.intent.fiatCurrency,\n conversionRate: input.intent.conversionRate.toString(),\n payeeDetails: input.payeeDetailsHash,\n timestampBufferMs: '300000',\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/buyerTeeAttestation.ts","../src/types.ts","../src/derivations.ts","../src/useProving.ts","../src/useManualTransactionSelect.ts","../src/buildCaptureRequest.ts"],"names":["calculateNetAmount","calculateFeeAmount","convertTokenInputToCurrency","formatUnits","sleep","useState","useRef","useCallback","extractNullifier","getExtensionPlatform"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @fileoverview Buyer trusted-execution attestation client.\n *\n * In PeerAuth extension 0.5.2+, capture produces a buyerTee proof —\n * `{ proofType: 'buyerTee', encryptedSessionMaterial, params }` — where the\n * bank session is already encrypted toward the enclave (cookies never reach\n * our page in plaintext). Instead of re-signing a Reclaim proof through our\n * gate, we hand that material to zkp2p's trusted-execution attestation\n * service, which verifies the payment inside an Amazon Web Services Nitro\n * enclave and signs the attestation directly.\n *\n * The response `responseObject` is the same `V3AttestationResponseObject`\n * shape our gate returns, so it flows through the existing\n * `encodeV3PaymentProof` path unchanged.\n *\n * Moved verbatim from `@provex/ui` (packages/ui/src/lib/buyerTeeAttestation.ts)\n * with the base URL changed from a `@provex/ui` config import to an injected\n * `attestationBaseUrl` argument so this package carries no `@provex/ui` dep.\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\n\n/**\n * Runtime shape of a buyerTee proof emitted by the PeerAuth extension.\n * Narrow with {@link asBuyerTeeProof} before use.\n */\nexport interface BuyerTeeProof {\n proofType: 'buyerTee'\n /** Session material already encrypted toward the enclave (opaque to us). */\n encryptedSessionMaterial: string\n /** Platform-specific params, e.g. Revolut `{ index }`, Chase `{ index }`. */\n params: unknown\n actionType?: string\n actionPlatform?: string\n}\n\n/**\n * Narrow an extension proof to a buyerTee proof, or null when it is the\n * legacy Reclaim shape (or malformed).\n */\nexport function asBuyerTeeProof(proof: unknown): BuyerTeeProof | null {\n if (!proof || typeof proof !== 'object') return null\n const candidate = proof as Record<string, unknown>\n if (candidate.proofType !== 'buyerTee') return null\n if (typeof candidate.encryptedSessionMaterial !== 'string') return null\n return candidate as unknown as BuyerTeeProof\n}\n\n/** Intent details the enclave binds the attestation to. */\nexport interface BuyerTeeIntent {\n intentHash: Hex\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\nexport interface RequestBuyerTeeAttestationArgs {\n /** Gate/attestation origin (was `gateForwarderBaseUrl` in @provex/ui). */\n attestationBaseUrl: string\n /**\n * Platform for the ATTESTATION route, e.g. `zelle` for every Zelle bank.\n * See `CaptureRequest.attestationPlatform`.\n */\n platform: string\n /**\n * Action type for the ATTESTATION route, e.g. `transfer_zelle_chase`.\n * See `CaptureRequest.attestationActionType`.\n */\n actionType: string\n /** Encrypted session material from the buyerTee capture. */\n encryptedSessionMaterial: string\n /** Platform params from the capture (e.g. `{ index }`). */\n params: unknown\n /** PulseChain UnifiedPaymentVerifier the gate re-signs the attestation for. */\n verifyingContract: string\n intent: BuyerTeeIntent\n}\n\n/**\n * Pull a human-readable reason out of the gate's error body.\n *\n * The gate returns `{ success: false, error, message }` JSON, where `message`\n * is the enclave's own words (e.g. \"…payment not COMPLETE\"). Surface that. If\n * the body is not that shape — most notably a Cloudflare/Caddy HTML error page\n * when the gate is genuinely unreachable — fall back to a short marker rather\n * than dumping a page of markup into the thrown message.\n */\nfunction extractGateErrorMessage(detail: string): string {\n const trimmed = detail.trim()\n if (!trimmed) return ''\n try {\n const parsed = JSON.parse(trimmed) as { message?: unknown; error?: unknown }\n if (typeof parsed.message === 'string' && parsed.message) return parsed.message\n if (typeof parsed.error === 'string' && parsed.error) return parsed.error\n } catch {\n // Not JSON — an HTML gateway page (e.g. a raw Cloudflare 502) carries no\n // useful reason for the user, so collapse it instead of echoing markup.\n if (/^\\s*<(?:!doctype|html)/i.test(trimmed)) return 'the attestation gateway is unavailable'\n }\n return trimmed\n}\n\n/** The gate's error code for a payment that already settled an order. */\nconst PAYMENT_ALREADY_USED_CODE = 'NULLIFIER_ALREADY_USED'\n\n/**\n * The gate refused the attestation because this bank payment already\n * settled an order (its nullifier is used or reserved). Distinct from other\n * failures so a caller holding several candidate payments of the same amount\n * can move on to the next one instead of failing the order.\n */\nexport class PaymentAlreadyUsedError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'PaymentAlreadyUsedError'\n }\n}\n\n/** The gate's machine-readable `error` code from a JSON error body, if any. */\nfunction extractGateErrorCode(detail: string): string | undefined {\n try {\n const parsed = JSON.parse(detail) as { error?: unknown }\n return typeof parsed.error === 'string' ? parsed.error : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Verify a buyer payment via the gate. The gate proxies the encrypted capture\n * to zkp2p's enclave, then re-signs the resulting attestation for PulseChain\n * with our witness key — the enclave itself refuses to sign for 369.\n *\n * Throws with the gate's error text on a non-2xx response, as a\n * {@link PaymentAlreadyUsedError} when the payment already settled an order.\n */\nexport async function requestBuyerTeeAttestation(\n args: RequestBuyerTeeAttestationArgs,\n): Promise<V3AttestationResponseObject> {\n const url = `${args.attestationBaseUrl}/api/v0/attestation/buyer-verify/${encodeURIComponent(args.platform)}/${encodeURIComponent(args.actionType)}`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n encryptedSessionMaterial: args.encryptedSessionMaterial,\n params: args.params,\n verifyingContract: args.verifyingContract,\n intent: args.intent,\n }),\n })\n\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n const message = `Trusted-execution attestation failed (${response.status}): ${extractGateErrorMessage(detail) || response.statusText}`\n if (response.status === 409 && extractGateErrorCode(detail) === PAYMENT_ALREADY_USED_CODE) {\n throw new PaymentAlreadyUsedError(message)\n }\n throw new Error(message)\n }\n\n const json = (await response.json()) as\n | { responseObject?: V3AttestationResponseObject }\n | V3AttestationResponseObject\n const responseObject =\n (json as { responseObject?: V3AttestationResponseObject }).responseObject ??\n (json as V3AttestationResponseObject)\n\n if (!responseObject?.typedDataValue || !responseObject?.signature) {\n throw new Error('Trusted-execution attestation response missing typedDataValue/signature')\n }\n return responseObject\n}\n","/**\n * @fileoverview Seam contract + state types for the proving orchestrator.\n *\n * `CaptureAdapter` is the swappable capture backend (extension+enclave today,\n * TLSNotary tomorrow). `ProvingState` is the single discriminated-union\n * machine that subsumes the live proof-substep machine (ProofPanel) and the\n * attestation lifecycle (submitStepDerivations). Monetary values stay bigint\n * in base units; the only stringified amount is `ProvingIntent.amount`, which\n * mirrors the existing on-the-wire BuyerTeeIntent shape (buyerTeeAttestation).\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** Transaction candidate the adapter surfaces for manual selection. Mirrors\n * the `MetadataMessage` fields the live TransactionSelector consumes; kept\n * structural so the package needs no @provex/extension-react import. */\nexport interface CaptureCandidate {\n amount: number | string\n currency?: string\n date?: string | number | Date\n originalIndex: number\n /**\n * Provider attestation params for this row (zkp2p 0.6.0 spec §4). These ARE\n * the attestation query params the enclave verifies against — they must be\n * forwarded (merged with the metadata index) on the buyerTee proof, not\n * dropped. Optional because the display path doesn't read them.\n */\n params?: Record<string, string | number | boolean>\n}\n\n/** Identity the attestor binds the attestation to. Mirrors the live\n * attestationIntent built in SubmitStep.tsx:333-342 / BuyerTeeIntent. */\nexport interface ProvingIntent {\n intentHash: Hex\n /** bigint base units, stringified (matches BuyerTeeIntent.amount). */\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n /** payeeDetailsHash */\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\n/** What the orchestrator hands a capture adapter. */\nexport interface CaptureRequest {\n /** Parent provider key, e.g. 'zelle', 'revolut'. */\n provider: string\n /**\n * Extension platform, e.g. 'venmo', or 'zelle' for every Zelle bank. With\n * {@link actionType} it names the hosted provider template the extension\n * fetches: `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`.\n */\n platform: string\n /** Action type, e.g. 'transfer_venmo' or 'transfer_zelle_chase'. */\n actionType: string\n /**\n * Platform for the ATTESTATION hop, when it differs from {@link platform}.\n * No provider needs this today: generic Zelle captures and attests as\n * `zelle` + `transfer_zelle_{bank}`. Omit it; consumers fall back to\n * {@link platform}.\n */\n attestationPlatform?: string\n /**\n * Action type for the ATTESTATION hop, when it differs from\n * {@link actionType}. See {@link attestationPlatform}. Omit when they\n * coincide; consumers fall back to {@link actionType}.\n */\n attestationActionType?: string\n intent: ProvingIntent\n /** UnifiedPaymentVerifier the attestation is re-signed for. */\n verifyingContract: Hex\n chainId: number\n /**\n * Settlement token for this intent (e.g. USDC on the target chain).\n * Used by the adapter to compute the fiat currency amount via\n * convertTokenInputToCurrency so auto-match works correctly for non-1:1 rates.\n */\n token: TokenInfo\n}\n\n/** Opaque per-backend capture state; the orchestrator passes it back unmodified. */\nexport interface CaptureHandle {\n readonly kind: string\n readonly material: unknown\n}\n\n/** Uniform capture output. `verificationData` names the signing witness to\n * the on-chain verifier (enclave signer for buyerTee — SubmitStep.tsx:473-475). */\nexport interface VerifiedAttestation {\n attestation: V3AttestationResponseObject\n verificationData: Hex\n}\n\n/** Resolver the UI provides so the adapter can collect a manual transaction\n * pick when auto-match fails (GenerateProofStep.tsx:391-404). */\nexport type SelectTransaction = (candidates: CaptureCandidate[]) => Promise<number>\n\n/** A pluggable capture backend. One per capture mechanism. */\nexport interface CaptureAdapter {\n /** Stable id for telemetry, e.g. 'extension-enclave'. */\n readonly kind: string\n /** Phase 1: authenticate + capture, returning opaque material to attest. */\n authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle>\n /** Phase 2: produce a verified attestation from the captured material. */\n attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation>\n}\n\n/** Inputs the orchestrator hook needs to drive the machine. */\nexport interface ProvingRequest {\n request: CaptureRequest\n /** Optional manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n}\n\n/**\n * The proving state machine (spec lines 141-151). `attest` auto-runs on\n * entry; `readyToFulfill` waits for the user's explicit Complete Order\n * action; `error.isSubmissionError` routes retry to Retry Submission\n * (post-fulfilment) vs Retry Verification (pre-fulfilment) — exactly the\n * live SubmitStep.tsx:931-955 routing.\n */\nexport type ProvingState =\n | { status: 'authenticate' }\n | { status: 'capture' }\n | { status: 'attest' }\n | { status: 'readyToFulfill'; attestation: VerifiedAttestation }\n | { status: 'fulfillIntent'; attestation: VerifiedAttestation }\n | { status: 'confirm'; attestation: VerifiedAttestation }\n | { status: 'sync'; attestation: VerifiedAttestation }\n | { status: 'complete'; attestation: VerifiedAttestation }\n | {\n status: 'error'\n message: string\n /** true ⇒ failure occurred during/after on-chain submission. */\n isSubmissionError: boolean\n attestation?: VerifiedAttestation\n /** Typed reason, set for cases the consumer must branch on. Currently\n * 'enclave-unavailable' (non-migrated provider → caller falls back to\n * Reclaim) and 'manual-selection-required' (auto-match failed, no\n * resolver supplied). Absent for ordinary verification/submission errors. */\n code?: 'enclave-unavailable' | 'manual-selection-required'\n }\n\n/**\n * Thrown by a capture adapter when the provider returned no buyerTee capture\n * (a non-migrated / Reclaim-only provider). The caller (UnifiedBuy) catches\n * this to fall back to its retained Reclaim branch — the package itself does\n * not implement Reclaim (decision (a), spec line 256).\n */\nexport class EnclaveCaptureUnavailable extends Error {\n readonly platform: string\n constructor(platform: string) {\n super(`No enclave (buyerTee) capture available for platform \"${platform}\".`)\n this.name = 'EnclaveCaptureUnavailable'\n this.platform = platform\n }\n}\n\n/**\n * Thrown by a capture adapter when the enclave capture was *attempted* but the\n * extension surfaced a failure reason (an `errorMessage`) instead of a capture\n * — most commonly because the extension could not reach the attestation enclave\n * (a buyer-side network / firewall / privacy- or ad-blocker problem).\n *\n * Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the\n * provider has no enclave capture path at all (non-migrated). This one is an\n * operational failure the user can usually fix and retry. Keeping them separate\n * stops a transient network failure from being misread as \"this provider isn't\n * supported\", and — if the Reclaim fallback is ever re-enabled — from wrongly\n * dumping the user out of the enclave path (this error carries no\n * `enclave-unavailable` code, so it surfaces as an ordinary retryable error).\n *\n * `detail` carries the raw extension `errorMessage` for logs and support;\n * `message` is the user-facing, actionable copy.\n */\nexport class EnclaveCaptureFailed extends Error {\n readonly platform: string\n readonly detail: string\n constructor(platform: string, detail: string) {\n super(\n 'We could not reach the verification service needed to confirm this ' +\n 'payment. A firewall, or a privacy or ad-blocking tool, may be blocking ' +\n 'it — try disabling blockers or switching networks, then verify again.',\n )\n this.name = 'EnclaveCaptureFailed'\n this.platform = platform\n this.detail = detail\n }\n}\n\n/**\n * Thrown by a capture adapter when auto-match failed and no `selectTransaction`\n * resolver was provided (e.g. a headless wallet). Carries the candidates so a\n * UI caller can render the manual selector and retry (spec lines 263-267).\n */\nexport class ManualSelectionRequired extends Error {\n readonly candidates: CaptureCandidate[]\n constructor(candidates: CaptureCandidate[]) {\n super('Manual transaction selection is required but no resolver was provided.')\n this.name = 'ManualSelectionRequired'\n this.candidates = candidates\n }\n}\n","/**\n * @fileoverview Pure proving derivations moved from @provex/ui\n * (packages/ui/src/components/UnifiedBuy/ProofPanel/submitStepDerivations.ts).\n *\n * Bigint throughout — see the `money-values-stay-bigint-until-display` rule.\n * The only string conversions happen at the formatter boundary (formatUnits),\n * which is the display layer.\n */\n\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\nimport { formatUnits } from '@provex/utils/units'\nimport { convertTokenInputToCurrency } from '@provex/utils/conversionRates'\nimport { calculateFeeAmount, calculateNetAmount } from '@provex/utils/fees'\n\nexport interface PartialFulfillment {\n /** Gross USDC the gate authorized for release (typedData.releaseAmount). */\n grossReleased: bigint\n /** Net USDC the buyer actually receives (grossReleased − fee). */\n netToBuyer: bigint\n /** Protocol fee in USDC base units. 0n when no fee applies. */\n protocolFee: bigint\n /** Locked USDC returned to the maker (intent.amount − grossReleased). */\n returnedToMaker: bigint\n contractedFiatDisplay: string\n attestedFiatDisplay: string\n netToBuyerDisplay: string\n returnedToMakerDisplay: string\n /** null when protocolFee === 0n so the caller can skip rendering the row. */\n protocolFeeDisplay: string | null\n}\n\n/**\n * Partial-fulfilment breakdown derived from the verified attestation.\n *\n * Returns `null` when:\n * - attestation isn't present (nothing to compare)\n * - releaseAmount >= intent.amount (full settlement, no partial story)\n * - intent/tokenInfo missing (defensive)\n */\nexport function computePartialFulfillment({\n attestation,\n intentAmount,\n conversionRate,\n tokenInfo,\n feeBps,\n}: {\n attestation: V3AttestationResponseObject | null\n intentAmount: bigint | null | undefined\n conversionRate: bigint | null | undefined\n tokenInfo: TokenInfo | null\n feeBps: bigint | null | undefined\n}): PartialFulfillment | null {\n if (!attestation || !intentAmount || !conversionRate || !tokenInfo) return null\n const grossReleased = BigInt(attestation.typedDataValue.releaseAmount)\n if (grossReleased >= intentAmount) return null\n\n const returnedToMaker = intentAmount - grossReleased\n const effectiveFeeBps = feeBps ?? 0n\n const netToBuyer =\n effectiveFeeBps > 0n\n ? calculateNetAmount({ grossAmount: grossReleased, feeBps: effectiveFeeBps })\n : grossReleased\n const protocolFee =\n effectiveFeeBps > 0n\n ? calculateFeeAmount({ amount: grossReleased, feeBps: effectiveFeeBps })\n : 0n\n\n const contractedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: intentAmount,\n rate: conversionRate,\n })\n const attestedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: grossReleased,\n rate: conversionRate,\n })\n\n const formatTokenDisplay = (value: bigint): string =>\n formatUnits(value, {\n decimals: tokenInfo.decimals,\n truncateDecimals: 2,\n padDecimals: 2,\n })\n\n return {\n grossReleased,\n netToBuyer,\n protocolFee,\n returnedToMaker,\n contractedFiatDisplay: formatTokenDisplay(contractedFiatRaw),\n attestedFiatDisplay: formatTokenDisplay(attestedFiatRaw),\n netToBuyerDisplay: formatTokenDisplay(netToBuyer),\n returnedToMakerDisplay: formatTokenDisplay(returnedToMaker),\n protocolFeeDisplay: protocolFee > 0n ? formatTokenDisplay(protocolFee) : null,\n }\n}\n","/**\n * @fileoverview useProving — headless orchestrator for the enclave proving\n * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep\n * state machine (ProofPanel) with no presentation. Renders nothing; returns\n * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.\n *\n * Owns: the ProvingState machine, the nullifier double-spend check\n * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing\n * (SubmitStep.tsx:931-955), and the minimum-visible-state timings\n * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,\n * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { Hex } from 'viem'\nimport { extractNullifier, type V3AttestationResponseObject } from '@provex/utils'\nimport { sleep } from '@provex/utils/time'\nimport { EnclaveCaptureUnavailable, ManualSelectionRequired } from './types'\nimport type {\n CaptureAdapter,\n CaptureRequest,\n SelectTransaction,\n ProvingState,\n VerifiedAttestation,\n} from './types'\n\n/**\n * Minimal context object that buildWritableMethod passes to the per-call hooks.\n * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.\n * Only `hash` is consumed by this hook — the rest is opaque context.\n */\ninterface TxCallbackContext {\n chainId: number\n request: unknown\n}\n\n/** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +\n * WriteHookParams from @provex/react but declared here to keep @provex/proving\n * from depending on the React package's built dist (which the test environment\n * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.\n *\n * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call\n * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):\n * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void\n * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void\n */\ninterface FulfillParams {\n intentHash: Hex\n attestation: V3AttestationResponseObject\n verificationData: Hex\n postIntentHookData?: Hex\n onAwaitingSignature?: (info: TxCallbackContext) => void\n onTransactionHash?: (info: TxCallbackContext & { hash: Hex }) => void\n}\n\n/** The structural slice of ProveXClient the orchestrator depends on. Keeping\n * it structural lets tests pass a light fake and avoids importing the class. */\nexport interface ProvingClient {\n /**\n * The chain this rail operates on. Must equal request.chainId — useProving\n * refuses to check the nullifier or submit on any other chain. A nullifier is\n * only meaningful on its intended chain; checking it on (or submitting it to)\n * a different chain is a cross-chain double-spend / replay vector.\n */\n readonly chainId: number\n fulfillIntent: (params: FulfillParams) => Promise<{ hash: Hex; receipt: unknown }>\n checkNullifierUsed: (params: { paymentMethodId: Hex; nullifier: Hex }) => Promise<boolean | null>\n}\n\nexport interface UseProvingOptions {\n client: ProvingClient\n captureAdapter: CaptureAdapter\n request: CaptureRequest\n /** Payment method id for the nullifier registry lookup. */\n paymentMethodId: Hex\n /** Manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n /**\n * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.\n * Receives the raw TxContext — the hook does not inspect the argument.\n */\n onAwaitingSignature?: () => void\n /** Receives the raw tx hash extracted from the TxContext+hash payload. */\n onTransactionHash?: (hash: Hex) => void\n /** Called once when the flow reaches `complete`. */\n onComplete?: (attestation: V3AttestationResponseObject) => void\n}\n\nexport interface UseProvingReturn {\n state: ProvingState\n /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */\n authenticate: () => Promise<void>\n /** Phase 3: submit fulfillIntent → confirm → sync → complete. */\n fulfill: () => Promise<void>\n /** Re-run attestation after a pre-fulfilment error. */\n retryVerification: () => Promise<void>\n /** Re-run submission after a post-fulfilment error. */\n retrySubmission: () => Promise<void>\n}\n\n/** Minimum visible-state pacing from SubmitStep.tsx:404-417 — keep a state\n * on screen at least `floorMs` so the user perceives the step. */\nasync function withMinimumVisible(floorMs: number, started: number): Promise<void> {\n const elapsed = Date.now() - started\n if (elapsed < floorMs) await sleep(floorMs - elapsed)\n}\n\n/**\n * Detect a wallet-rejection error structurally (without importing ProveXError).\n * ProveXClient always re-wraps user rejections as ProveXError with\n * code === 'WALLET_REJECTED' before the error escapes fulfillIntent.\n */\nfunction isWalletRejection(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code: unknown }).code === 'WALLET_REJECTED'\n )\n}\n\nexport function useProving(options: UseProvingOptions): UseProvingReturn {\n const {\n client,\n captureAdapter,\n request,\n paymentMethodId,\n selectTransaction,\n onAwaitingSignature,\n onTransactionHash,\n onComplete,\n } = options\n\n const [state, setState] = useState<ProvingState>({ status: 'authenticate' })\n // Re-entry guard mirroring isRequestingAttestationRef (SubmitStep.tsx:199).\n const inFlightRef = useRef(false)\n // Separate in-flight guard for submit — prevents double on-chain spend from\n // two rapid fulfill() calls before a re-render.\n const submitInFlightRef = useRef(false)\n\n const runCaptureAndAttest = useCallback(async (): Promise<void> => {\n if (inFlightRef.current) return\n inFlightRef.current = true\n try {\n // Chain pinning: the nullifier must only ever be checked on the chain the\n // intent targets. A client wired to any other chain is refused before we\n // touch capture/attest/registry — never verify on the wrong chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n\n setState({ status: 'capture' })\n const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction })\n\n const attestStarted = Date.now()\n setState({ status: 'attest' })\n const verified = await captureAdapter.attest(handle, request)\n // SubmitStep.tsx:414-417: verifying state must be visible for ≥1000ms.\n await withMinimumVisible(1000, attestStarted)\n\n // Nullifier double-spend re-check — ADVISORY. The authoritative guards are\n // (1) the gate's buyerTee re-sign, which now verifies the nullifier is\n // unused and reserves it cross-chain before signing (buyer-tee.ts), and\n // (2) the on-chain contract, which reverts fulfillIntent on a used\n // nullifier. A valid attestation therefore already implies an unused\n // nullifier. So this browser re-check must NOT fail closed on a flaky\n // public RPC (which falsely blocked legitimate buys with \"registry\n // unreachable\"): block ONLY on a positive \"already used\"; pass otherwise.\n const nullifier = extractNullifier(verified.attestation)\n if (nullifier) {\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed === true) {\n throw new Error(\n 'This payment has already been verified. The nullifier has already been used onchain.',\n )\n }\n if (isUsed === null) {\n console.warn(\n '[useProving] nullifier re-check unavailable (browser RPC) — proceeding; the gate already verified and reserved it',\n )\n }\n } else {\n console.warn(\n '[useProving] could not extract nullifier in the browser — proceeding; the gate already verified it',\n )\n }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n const code =\n error instanceof EnclaveCaptureUnavailable\n ? ('enclave-unavailable' as const)\n : error instanceof ManualSelectionRequired\n ? ('manual-selection-required' as const)\n : undefined\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\n code,\n })\n } finally {\n inFlightRef.current = false\n }\n }, [captureAdapter, client, request, paymentMethodId, selectTransaction])\n\n const submit = useCallback(\n async (verified: VerifiedAttestation): Promise<void> => {\n // Re-entry guard: prevent two rapid submit calls from both calling\n // fulfillIntent (double on-chain spend). Mirrors inFlightRef pattern.\n if (submitInFlightRef.current) return\n submitInFlightRef.current = true\n\n const submitStarted = Date.now()\n setState({ status: 'fulfillIntent', attestation: verified })\n try {\n // Chain pinning (re-asserted at submit): the nullifier was verified on\n // request.chainId; it must only ever be submitted to that same chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n await client.fulfillIntent({\n intentHash: request.intent.intentHash,\n attestation: verified.attestation,\n verificationData: verified.verificationData,\n // Wrap consumer callbacks to match the real buildWritableMethod\n // call shapes (ProveXClient.ts ~890, ~903).\n onAwaitingSignature: onAwaitingSignature\n ? (_info: TxCallbackContext) => { onAwaitingSignature() }\n : undefined,\n // buildWritableMethod fires onTransactionHash with { ...ctx, hash }\n // (ProveXClient.ts ~903). Extract hash and forward to the consumer's\n // simpler (hash: Hex) => void API.\n onTransactionHash: onTransactionHash\n ? (info: TxCallbackContext & { hash: Hex }) => {\n // After the tx is broadcast (a separate async tick before the\n // receipt arrives), surface the confirm state so it is\n // observable by consumers.\n setState({ status: 'confirm', attestation: verified })\n onTransactionHash(info.hash)\n }\n : ((_info: TxCallbackContext & { hash: Hex }) => {\n setState({ status: 'confirm', attestation: verified })\n }),\n })\n await withMinimumVisible(500, submitStarted)\n\n // fulfillIntent already awaited receipt + indexer sync internally\n // (ProveXClient.ts:906-915). Surface sync as a real visible state\n // (confirm was set in onTransactionHash above); then await a\n // minimum-visible window before complete so sync is not batched away.\n setState({ status: 'sync', attestation: verified })\n await sleep(600)\n setState({ status: 'complete', attestation: verified })\n onComplete?.(verified.attestation)\n } catch (error) {\n // Wallet rejection: return to readyToFulfill so the user can retry\n // without losing the attestation (mirrors SubmitStep.tsx:571-573).\n if (isWalletRejection(error)) {\n setState({ status: 'readyToFulfill', attestation: verified })\n return\n }\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Submission failed',\n isSubmissionError: true,\n attestation: verified,\n })\n } finally {\n submitInFlightRef.current = false\n }\n },\n [client, request.intent.intentHash, onAwaitingSignature, onTransactionHash, onComplete],\n )\n\n const authenticate = useCallback(() => runCaptureAndAttest(), [runCaptureAndAttest])\n\n const fulfill = useCallback(async () => {\n if (state.status !== 'readyToFulfill') return\n await submit(state.attestation)\n }, [state, submit])\n\n const retryVerification = useCallback(async () => {\n setState({ status: 'authenticate' })\n await runCaptureAndAttest()\n }, [runCaptureAndAttest])\n\n const retrySubmission = useCallback(async () => {\n if (state.status !== 'error' || !state.attestation) return\n await submit(state.attestation)\n }, [state, submit])\n\n return { state, authenticate, fulfill, retryVerification, retrySubmission }\n}\n","import { useCallback, useRef, useState } from 'react'\nimport type { CaptureCandidate, SelectTransaction } from './types'\n\nexport interface ManualTransactionSelect {\n /**\n * Resolver injected into useProving — the enclave adapter calls this when\n * auto-match of the bank transaction fails (extensionEnclave.ts:189-190).\n */\n selectTransaction: SelectTransaction\n /**\n * Non-null while the adapter is awaiting a pick; drives the\n * TransactionSelector rendered inside GenerateProofStep.\n */\n candidates: CaptureCandidate[] | null\n /**\n * Called by TransactionSelector with the chosen candidate's originalIndex.\n * Resolves the pending promise and resets candidates to null.\n */\n pick: (originalIndex: number) => void\n}\n\n/**\n * Bridge the framework-agnostic {@link SelectTransaction} resolver the enclave\n * adapter expects (extensionEnclave.ts:189-190) to React state the\n * GenerateProofStep TransactionSelector can render.\n *\n * The adapter awaits the promise returned by `selectTransaction`; the user's\n * call to `pick` resolves it with the chosen `originalIndex`.\n *\n * `useRef` is justified here exclusively for holding the pending promise\n * resolver across renders without triggering a re-render cycle — the single\n * sanctioned use per the react-state-preferences memory.\n */\nexport function useManualTransactionSelect(): ManualTransactionSelect {\n const [candidates, setCandidates] = useState<CaptureCandidate[] | null>(null)\n const resolveRef = useRef<((originalIndex: number) => void) | null>(null)\n\n const selectTransaction = useCallback<SelectTransaction>((next) => {\n return new Promise<number>((resolve) => {\n resolveRef.current = resolve\n setCandidates(next)\n })\n }, [])\n\n const pick = useCallback((originalIndex: number) => {\n resolveRef.current?.(originalIndex)\n resolveRef.current = null\n setCandidates(null)\n }, [])\n\n return { selectTransaction, candidates, pick }\n}\n","/**\n * @fileoverview Pure builder that assembles a {@link CaptureRequest} from\n * resolved order data.\n *\n * Hoisted verbatim from the intent-assembly block in SubmitStep.tsx:312-342\n * so the mapping is unit-tested in isolation and reusable by the proving shell.\n *\n * Monetary values stay bigint in base units through the entire call stack;\n * this is the single boundary where they are stringified for the attestor\n * wire format.\n */\n\nimport type { Hex } from 'viem'\nimport type { CaptureRequest } from './types'\nimport { getExtensionPlatform } from '@provex/utils/payment'\nimport type { ProviderKey, SubProviderKey } from '@provex/utils/payment'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** All resolved order fields required to build a {@link CaptureRequest}. */\nexport interface BuildCaptureRequestInput {\n /** On-chain intent hash. */\n intentHash: Hex\n /** Chain the intent lives on. */\n chainId: number\n /** Bank-level sub-provider, e.g. `'zelle-chase'`, or `null` for top-level providers. */\n subProvider: SubProviderKey | null\n /** Provider key from the deposit. May itself be a sub-provider key (contains a dash). */\n providerKey: ProviderKey | SubProviderKey\n /** Address of the UnifiedPaymentVerifier contract for this chain. */\n verifyingContract: Hex\n /** payeeDetailsHash from the matched verifier record. */\n payeeDetailsHash: Hex\n /** Settlement token for this intent (used by the adapter for amount conversion). */\n token: TokenInfo\n /** Resolved intent fields, monetary values in bigint base units. */\n intent: {\n amount: bigint\n timestamp: bigint\n paymentMethod: Hex | undefined\n fiatCurrency: Hex\n conversionRate: bigint\n }\n}\n\n/**\n * The extension platform and action type a capture addresses.\n *\n * Generic Zelle uses zkp2p's hosted bank templates, named\n * `zelle/transfer_zelle_{bank}` (e.g. `zelle/transfer_zelle_chase`), the same\n * pair `@zkp2p/sdk` uses (`actionPlatform: zelle`,\n * `actionType: transfer_zelle_chase`). The extension always fetches its\n * template from `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`\n * and ignores a page-supplied template, so the pair must name a hosted file.\n * The older bank pair (`chase/transfer_zelle`) now 404s, and the capture dies\n * with \"Failed to fetch provider template: 404\".\n *\n * The same pair is the enclave's attestation route: zkp2p's\n * `resolveBuyerTeeAttestationRoute` accepts generic Zelle only as `zelle` plus\n * `transfer_zelle_{bofa,chase,citi}`, which matches our `zelle-{bofa,chase,citi}`\n * sub-provider keys one-for-one. Capture and attestation therefore coincide.\n *\n * Every other provider uses its extension platform and `transfer_<parent>`.\n *\n * @param providerKey - Sub-provider key when present, else the provider key.\n * @param parentProvider - The portion before the dash, e.g. 'zelle'.\n */\nfunction captureRouteFor({\n providerKey,\n parentProvider,\n}: {\n providerKey: ProviderKey | SubProviderKey\n parentProvider: string\n}): Pick<CaptureRequest, 'platform' | 'actionType'> {\n if (parentProvider === 'zelle' && providerKey.includes('-')) {\n const bank = providerKey.slice(parentProvider.length + 1)\n return { platform: parentProvider, actionType: `transfer_${parentProvider}_${bank}` }\n }\n return { platform: getExtensionPlatform(providerKey), actionType: `transfer_${parentProvider}` }\n}\n\n/**\n * Build the {@link CaptureRequest} from resolved order data.\n *\n * Pure hoist of the intent-assembly block in SubmitStep.tsx:312-342.\n * No side effects, no I/O, no React. Safe to call in any environment.\n *\n * @param input - All resolved order fields needed to build the request.\n * @returns A fully-formed {@link CaptureRequest} ready for a {@link CaptureAdapter}.\n */\nexport function buildCaptureRequest(input: BuildCaptureRequestInput): CaptureRequest {\n const providerKey = input.subProvider ?? input.providerKey\n const parentProvider = providerKey.includes('-')\n ? providerKey.split('-')[0]\n : providerKey\n\n return {\n provider: input.providerKey,\n ...captureRouteFor({ providerKey, parentProvider }),\n verifyingContract: input.verifyingContract,\n chainId: input.chainId,\n token: input.token,\n intent: {\n intentHash: input.intentHash,\n amount: input.intent.amount.toString(),\n timestampMs: (input.intent.timestamp * 1000n).toString(),\n paymentMethod: input.intent.paymentMethod ?? ('0x' as Hex),\n fiatCurrency: input.intent.fiatCurrency,\n conversionRate: input.intent.conversionRate.toString(),\n payeeDetails: input.payeeDetailsHash,\n timestampBufferMs: '300000',\n },\n }\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hex } from 'viem';
|
|
2
2
|
import { V3AttestationResponseObject } from '@provex/utils';
|
|
3
|
-
import { S as SelectTransaction, C as CaptureCandidate, a as CaptureRequest } from './useProving-
|
|
4
|
-
export { d as CaptureAdapter, c as CaptureHandle, b as EnclaveCaptureFailed, E as EnclaveCaptureUnavailable, M as ManualSelectionRequired, g as ProvingClient, P as ProvingIntent, e as ProvingRequest, f as ProvingState, U as UseProvingOptions, h as UseProvingReturn, V as VerifiedAttestation, u as useProving } from './useProving-
|
|
3
|
+
import { S as SelectTransaction, C as CaptureCandidate, a as CaptureRequest } from './useProving-B4UsP7ku.cjs';
|
|
4
|
+
export { d as CaptureAdapter, c as CaptureHandle, b as EnclaveCaptureFailed, E as EnclaveCaptureUnavailable, M as ManualSelectionRequired, g as ProvingClient, P as ProvingIntent, e as ProvingRequest, f as ProvingState, U as UseProvingOptions, h as UseProvingReturn, V as VerifiedAttestation, u as useProving } from './useProving-B4UsP7ku.cjs';
|
|
5
5
|
import { TokenInfo } from '@provex/utils/tokens';
|
|
6
6
|
import { SubProviderKey, ProviderKey } from '@provex/utils/payment';
|
|
7
7
|
|
|
@@ -58,16 +58,13 @@ interface RequestBuyerTeeAttestationArgs {
|
|
|
58
58
|
/** Gate/attestation origin (was `gateForwarderBaseUrl` in @provex/ui). */
|
|
59
59
|
attestationBaseUrl: string;
|
|
60
60
|
/**
|
|
61
|
-
* Platform for the ATTESTATION route
|
|
62
|
-
*
|
|
63
|
-
* (`chase`). See `CaptureRequest.attestationPlatform`.
|
|
61
|
+
* Platform for the ATTESTATION route, e.g. `zelle` for every Zelle bank.
|
|
62
|
+
* See `CaptureRequest.attestationPlatform`.
|
|
64
63
|
*/
|
|
65
64
|
platform: string;
|
|
66
65
|
/**
|
|
67
|
-
* Action type for the ATTESTATION route
|
|
68
|
-
*
|
|
69
|
-
* even though capture used `transfer_zelle`. See
|
|
70
|
-
* `CaptureRequest.attestationActionType`.
|
|
66
|
+
* Action type for the ATTESTATION route, e.g. `transfer_zelle_chase`.
|
|
67
|
+
* See `CaptureRequest.attestationActionType`.
|
|
71
68
|
*/
|
|
72
69
|
actionType: string;
|
|
73
70
|
/** Encrypted session material from the buyerTee capture. */
|
|
@@ -78,12 +75,22 @@ interface RequestBuyerTeeAttestationArgs {
|
|
|
78
75
|
verifyingContract: string;
|
|
79
76
|
intent: BuyerTeeIntent;
|
|
80
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The gate refused the attestation because this bank payment already
|
|
80
|
+
* settled an order (its nullifier is used or reserved). Distinct from other
|
|
81
|
+
* failures so a caller holding several candidate payments of the same amount
|
|
82
|
+
* can move on to the next one instead of failing the order.
|
|
83
|
+
*/
|
|
84
|
+
declare class PaymentAlreadyUsedError extends Error {
|
|
85
|
+
constructor(message: string);
|
|
86
|
+
}
|
|
81
87
|
/**
|
|
82
88
|
* Verify a buyer payment via the gate. The gate proxies the encrypted capture
|
|
83
89
|
* to zkp2p's enclave, then re-signs the resulting attestation for PulseChain
|
|
84
90
|
* with our witness key — the enclave itself refuses to sign for 369.
|
|
85
91
|
*
|
|
86
|
-
* Throws with the gate's error text on a non-2xx response
|
|
92
|
+
* Throws with the gate's error text on a non-2xx response, as a
|
|
93
|
+
* {@link PaymentAlreadyUsedError} when the payment already settled an order.
|
|
87
94
|
*/
|
|
88
95
|
declare function requestBuyerTeeAttestation(args: RequestBuyerTeeAttestationArgs): Promise<V3AttestationResponseObject>;
|
|
89
96
|
|
|
@@ -207,4 +214,4 @@ interface BuildCaptureRequestInput {
|
|
|
207
214
|
*/
|
|
208
215
|
declare function buildCaptureRequest(input: BuildCaptureRequestInput): CaptureRequest;
|
|
209
216
|
|
|
210
|
-
export { type BuildCaptureRequestInput, type BuyerTeeIntent, type BuyerTeeProof, CaptureCandidate, CaptureRequest, type ManualTransactionSelect, type PartialFulfillment, type RequestBuyerTeeAttestationArgs, SelectTransaction, asBuyerTeeProof, buildCaptureRequest, computePartialFulfillment, requestBuyerTeeAttestation, useManualTransactionSelect };
|
|
217
|
+
export { type BuildCaptureRequestInput, type BuyerTeeIntent, type BuyerTeeProof, CaptureCandidate, CaptureRequest, type ManualTransactionSelect, type PartialFulfillment, PaymentAlreadyUsedError, type RequestBuyerTeeAttestationArgs, SelectTransaction, asBuyerTeeProof, buildCaptureRequest, computePartialFulfillment, requestBuyerTeeAttestation, useManualTransactionSelect };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hex } from 'viem';
|
|
2
2
|
import { V3AttestationResponseObject } from '@provex/utils';
|
|
3
|
-
import { S as SelectTransaction, C as CaptureCandidate, a as CaptureRequest } from './useProving-
|
|
4
|
-
export { d as CaptureAdapter, c as CaptureHandle, b as EnclaveCaptureFailed, E as EnclaveCaptureUnavailable, M as ManualSelectionRequired, g as ProvingClient, P as ProvingIntent, e as ProvingRequest, f as ProvingState, U as UseProvingOptions, h as UseProvingReturn, V as VerifiedAttestation, u as useProving } from './useProving-
|
|
3
|
+
import { S as SelectTransaction, C as CaptureCandidate, a as CaptureRequest } from './useProving-B4UsP7ku.js';
|
|
4
|
+
export { d as CaptureAdapter, c as CaptureHandle, b as EnclaveCaptureFailed, E as EnclaveCaptureUnavailable, M as ManualSelectionRequired, g as ProvingClient, P as ProvingIntent, e as ProvingRequest, f as ProvingState, U as UseProvingOptions, h as UseProvingReturn, V as VerifiedAttestation, u as useProving } from './useProving-B4UsP7ku.js';
|
|
5
5
|
import { TokenInfo } from '@provex/utils/tokens';
|
|
6
6
|
import { SubProviderKey, ProviderKey } from '@provex/utils/payment';
|
|
7
7
|
|
|
@@ -58,16 +58,13 @@ interface RequestBuyerTeeAttestationArgs {
|
|
|
58
58
|
/** Gate/attestation origin (was `gateForwarderBaseUrl` in @provex/ui). */
|
|
59
59
|
attestationBaseUrl: string;
|
|
60
60
|
/**
|
|
61
|
-
* Platform for the ATTESTATION route
|
|
62
|
-
*
|
|
63
|
-
* (`chase`). See `CaptureRequest.attestationPlatform`.
|
|
61
|
+
* Platform for the ATTESTATION route, e.g. `zelle` for every Zelle bank.
|
|
62
|
+
* See `CaptureRequest.attestationPlatform`.
|
|
64
63
|
*/
|
|
65
64
|
platform: string;
|
|
66
65
|
/**
|
|
67
|
-
* Action type for the ATTESTATION route
|
|
68
|
-
*
|
|
69
|
-
* even though capture used `transfer_zelle`. See
|
|
70
|
-
* `CaptureRequest.attestationActionType`.
|
|
66
|
+
* Action type for the ATTESTATION route, e.g. `transfer_zelle_chase`.
|
|
67
|
+
* See `CaptureRequest.attestationActionType`.
|
|
71
68
|
*/
|
|
72
69
|
actionType: string;
|
|
73
70
|
/** Encrypted session material from the buyerTee capture. */
|
|
@@ -78,12 +75,22 @@ interface RequestBuyerTeeAttestationArgs {
|
|
|
78
75
|
verifyingContract: string;
|
|
79
76
|
intent: BuyerTeeIntent;
|
|
80
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The gate refused the attestation because this bank payment already
|
|
80
|
+
* settled an order (its nullifier is used or reserved). Distinct from other
|
|
81
|
+
* failures so a caller holding several candidate payments of the same amount
|
|
82
|
+
* can move on to the next one instead of failing the order.
|
|
83
|
+
*/
|
|
84
|
+
declare class PaymentAlreadyUsedError extends Error {
|
|
85
|
+
constructor(message: string);
|
|
86
|
+
}
|
|
81
87
|
/**
|
|
82
88
|
* Verify a buyer payment via the gate. The gate proxies the encrypted capture
|
|
83
89
|
* to zkp2p's enclave, then re-signs the resulting attestation for PulseChain
|
|
84
90
|
* with our witness key — the enclave itself refuses to sign for 369.
|
|
85
91
|
*
|
|
86
|
-
* Throws with the gate's error text on a non-2xx response
|
|
92
|
+
* Throws with the gate's error text on a non-2xx response, as a
|
|
93
|
+
* {@link PaymentAlreadyUsedError} when the payment already settled an order.
|
|
87
94
|
*/
|
|
88
95
|
declare function requestBuyerTeeAttestation(args: RequestBuyerTeeAttestationArgs): Promise<V3AttestationResponseObject>;
|
|
89
96
|
|
|
@@ -207,4 +214,4 @@ interface BuildCaptureRequestInput {
|
|
|
207
214
|
*/
|
|
208
215
|
declare function buildCaptureRequest(input: BuildCaptureRequestInput): CaptureRequest;
|
|
209
216
|
|
|
210
|
-
export { type BuildCaptureRequestInput, type BuyerTeeIntent, type BuyerTeeProof, CaptureCandidate, CaptureRequest, type ManualTransactionSelect, type PartialFulfillment, type RequestBuyerTeeAttestationArgs, SelectTransaction, asBuyerTeeProof, buildCaptureRequest, computePartialFulfillment, requestBuyerTeeAttestation, useManualTransactionSelect };
|
|
217
|
+
export { type BuildCaptureRequestInput, type BuyerTeeIntent, type BuyerTeeProof, CaptureCandidate, CaptureRequest, type ManualTransactionSelect, type PartialFulfillment, PaymentAlreadyUsedError, type RequestBuyerTeeAttestationArgs, SelectTransaction, asBuyerTeeProof, buildCaptureRequest, computePartialFulfillment, requestBuyerTeeAttestation, useManualTransactionSelect };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { EnclaveCaptureFailed, EnclaveCaptureUnavailable, ManualSelectionRequired, useProving } from './chunk-
|
|
1
|
+
export { EnclaveCaptureFailed, EnclaveCaptureUnavailable, ManualSelectionRequired, useProving } from './chunk-V2ESGZXE.js';
|
|
2
2
|
import { formatUnits } from '@provex/utils/units';
|
|
3
3
|
import { convertTokenInputToCurrency } from '@provex/utils/conversionRates';
|
|
4
4
|
import { calculateNetAmount, calculateFeeAmount } from '@provex/utils/fees';
|
|
@@ -25,6 +25,21 @@ function extractGateErrorMessage(detail) {
|
|
|
25
25
|
}
|
|
26
26
|
return trimmed;
|
|
27
27
|
}
|
|
28
|
+
var PAYMENT_ALREADY_USED_CODE = "NULLIFIER_ALREADY_USED";
|
|
29
|
+
var PaymentAlreadyUsedError = class extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "PaymentAlreadyUsedError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
function extractGateErrorCode(detail) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(detail);
|
|
38
|
+
return typeof parsed.error === "string" ? parsed.error : void 0;
|
|
39
|
+
} catch {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
28
43
|
async function requestBuyerTeeAttestation(args) {
|
|
29
44
|
const url = `${args.attestationBaseUrl}/api/v0/attestation/buyer-verify/${encodeURIComponent(args.platform)}/${encodeURIComponent(args.actionType)}`;
|
|
30
45
|
const response = await fetch(url, {
|
|
@@ -39,9 +54,11 @@ async function requestBuyerTeeAttestation(args) {
|
|
|
39
54
|
});
|
|
40
55
|
if (!response.ok) {
|
|
41
56
|
const detail = await response.text().catch(() => "");
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
const message = `Trusted-execution attestation failed (${response.status}): ${extractGateErrorMessage(detail) || response.statusText}`;
|
|
58
|
+
if (response.status === 409 && extractGateErrorCode(detail) === PAYMENT_ALREADY_USED_CODE) {
|
|
59
|
+
throw new PaymentAlreadyUsedError(message);
|
|
60
|
+
}
|
|
61
|
+
throw new Error(message);
|
|
45
62
|
}
|
|
46
63
|
const json = await response.json();
|
|
47
64
|
const responseObject = json.responseObject ?? json;
|
|
@@ -107,27 +124,22 @@ function useManualTransactionSelect() {
|
|
|
107
124
|
}, []);
|
|
108
125
|
return { selectTransaction, candidates, pick };
|
|
109
126
|
}
|
|
110
|
-
function
|
|
127
|
+
function captureRouteFor({
|
|
111
128
|
providerKey,
|
|
112
129
|
parentProvider
|
|
113
130
|
}) {
|
|
114
|
-
if (parentProvider
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
};
|
|
131
|
+
if (parentProvider === "zelle" && providerKey.includes("-")) {
|
|
132
|
+
const bank = providerKey.slice(parentProvider.length + 1);
|
|
133
|
+
return { platform: parentProvider, actionType: `transfer_${parentProvider}_${bank}` };
|
|
134
|
+
}
|
|
135
|
+
return { platform: getExtensionPlatform(providerKey), actionType: `transfer_${parentProvider}` };
|
|
120
136
|
}
|
|
121
137
|
function buildCaptureRequest(input) {
|
|
122
138
|
const providerKey = input.subProvider ?? input.providerKey;
|
|
123
139
|
const parentProvider = providerKey.includes("-") ? providerKey.split("-")[0] : providerKey;
|
|
124
|
-
const platform = getExtensionPlatform(providerKey);
|
|
125
|
-
const actionType = `transfer_${parentProvider}`;
|
|
126
140
|
return {
|
|
127
141
|
provider: input.providerKey,
|
|
128
|
-
|
|
129
|
-
actionType,
|
|
130
|
-
...attestationRouteFor({ providerKey, parentProvider }),
|
|
142
|
+
...captureRouteFor({ providerKey, parentProvider }),
|
|
131
143
|
verifyingContract: input.verifyingContract,
|
|
132
144
|
chainId: input.chainId,
|
|
133
145
|
token: input.token,
|
|
@@ -144,6 +156,6 @@ function buildCaptureRequest(input) {
|
|
|
144
156
|
};
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
export { asBuyerTeeProof, buildCaptureRequest, computePartialFulfillment, requestBuyerTeeAttestation, useManualTransactionSelect };
|
|
159
|
+
export { PaymentAlreadyUsedError, asBuyerTeeProof, buildCaptureRequest, computePartialFulfillment, requestBuyerTeeAttestation, useManualTransactionSelect };
|
|
148
160
|
//# sourceMappingURL=index.js.map
|
|
149
161
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buyerTeeAttestation.ts","../src/derivations.ts","../src/useManualTransactionSelect.ts","../src/buildCaptureRequest.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @fileoverview Buyer trusted-execution attestation client.\n *\n * In PeerAuth extension 0.5.2+, capture produces a buyerTee proof —\n * `{ proofType: 'buyerTee', encryptedSessionMaterial, params }` — where the\n * bank session is already encrypted toward the enclave (cookies never reach\n * our page in plaintext). Instead of re-signing a Reclaim proof through our\n * gate, we hand that material to zkp2p's trusted-execution attestation\n * service, which verifies the payment inside an Amazon Web Services Nitro\n * enclave and signs the attestation directly.\n *\n * The response `responseObject` is the same `V3AttestationResponseObject`\n * shape our gate returns, so it flows through the existing\n * `encodeV3PaymentProof` path unchanged.\n *\n * Moved verbatim from `@provex/ui` (packages/ui/src/lib/buyerTeeAttestation.ts)\n * with the base URL changed from a `@provex/ui` config import to an injected\n * `attestationBaseUrl` argument so this package carries no `@provex/ui` dep.\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\n\n/**\n * Runtime shape of a buyerTee proof emitted by the PeerAuth extension.\n * Narrow with {@link asBuyerTeeProof} before use.\n */\nexport interface BuyerTeeProof {\n proofType: 'buyerTee'\n /** Session material already encrypted toward the enclave (opaque to us). */\n encryptedSessionMaterial: string\n /** Platform-specific params, e.g. Revolut `{ index }`, Chase `{ index }`. */\n params: unknown\n actionType?: string\n actionPlatform?: string\n}\n\n/**\n * Narrow an extension proof to a buyerTee proof, or null when it is the\n * legacy Reclaim shape (or malformed).\n */\nexport function asBuyerTeeProof(proof: unknown): BuyerTeeProof | null {\n if (!proof || typeof proof !== 'object') return null\n const candidate = proof as Record<string, unknown>\n if (candidate.proofType !== 'buyerTee') return null\n if (typeof candidate.encryptedSessionMaterial !== 'string') return null\n return candidate as unknown as BuyerTeeProof\n}\n\n/** Intent details the enclave binds the attestation to. */\nexport interface BuyerTeeIntent {\n intentHash: Hex\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\nexport interface RequestBuyerTeeAttestationArgs {\n /** Gate/attestation origin (was `gateForwarderBaseUrl` in @provex/ui). */\n attestationBaseUrl: string\n /**\n * Platform for the ATTESTATION route — NOT necessarily the capture platform.\n * For generic Zelle this is `zelle`, even though capture used the bank\n * (`chase`). See `CaptureRequest.attestationPlatform`.\n */\n platform: string\n /**\n * Action type for the ATTESTATION route — NOT necessarily the capture action\n * type. For generic Zelle this is bank-specific (`transfer_zelle_chase`),\n * even though capture used `transfer_zelle`. See\n * `CaptureRequest.attestationActionType`.\n */\n actionType: string\n /** Encrypted session material from the buyerTee capture. */\n encryptedSessionMaterial: string\n /** Platform params from the capture (e.g. `{ index }`). */\n params: unknown\n /** PulseChain UnifiedPaymentVerifier the gate re-signs the attestation for. */\n verifyingContract: string\n intent: BuyerTeeIntent\n}\n\n/**\n * Pull a human-readable reason out of the gate's error body.\n *\n * The gate returns `{ success: false, error, message }` JSON, where `message`\n * is the enclave's own words (e.g. \"…payment not COMPLETE\"). Surface that. If\n * the body is not that shape — most notably a Cloudflare/Caddy HTML error page\n * when the gate is genuinely unreachable — fall back to a short marker rather\n * than dumping a page of markup into the thrown message.\n */\nfunction extractGateErrorMessage(detail: string): string {\n const trimmed = detail.trim()\n if (!trimmed) return ''\n try {\n const parsed = JSON.parse(trimmed) as { message?: unknown; error?: unknown }\n if (typeof parsed.message === 'string' && parsed.message) return parsed.message\n if (typeof parsed.error === 'string' && parsed.error) return parsed.error\n } catch {\n // Not JSON — an HTML gateway page (e.g. a raw Cloudflare 502) carries no\n // useful reason for the user, so collapse it instead of echoing markup.\n if (/^\\s*<(?:!doctype|html)/i.test(trimmed)) return 'the attestation gateway is unavailable'\n }\n return trimmed\n}\n\n/**\n * Verify a buyer payment via the gate. The gate proxies the encrypted capture\n * to zkp2p's enclave, then re-signs the resulting attestation for PulseChain\n * with our witness key — the enclave itself refuses to sign for 369.\n *\n * Throws with the gate's error text on a non-2xx response.\n */\nexport async function requestBuyerTeeAttestation(\n args: RequestBuyerTeeAttestationArgs,\n): Promise<V3AttestationResponseObject> {\n const url = `${args.attestationBaseUrl}/api/v0/attestation/buyer-verify/${encodeURIComponent(args.platform)}/${encodeURIComponent(args.actionType)}`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n encryptedSessionMaterial: args.encryptedSessionMaterial,\n params: args.params,\n verifyingContract: args.verifyingContract,\n intent: args.intent,\n }),\n })\n\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `Trusted-execution attestation failed (${response.status}): ${extractGateErrorMessage(detail) || response.statusText}`,\n )\n }\n\n const json = (await response.json()) as\n | { responseObject?: V3AttestationResponseObject }\n | V3AttestationResponseObject\n const responseObject =\n (json as { responseObject?: V3AttestationResponseObject }).responseObject ??\n (json as V3AttestationResponseObject)\n\n if (!responseObject?.typedDataValue || !responseObject?.signature) {\n throw new Error('Trusted-execution attestation response missing typedDataValue/signature')\n }\n return responseObject\n}\n","/**\n * @fileoverview Pure proving derivations moved from @provex/ui\n * (packages/ui/src/components/UnifiedBuy/ProofPanel/submitStepDerivations.ts).\n *\n * Bigint throughout — see the `money-values-stay-bigint-until-display` rule.\n * The only string conversions happen at the formatter boundary (formatUnits),\n * which is the display layer.\n */\n\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\nimport { formatUnits } from '@provex/utils/units'\nimport { convertTokenInputToCurrency } from '@provex/utils/conversionRates'\nimport { calculateFeeAmount, calculateNetAmount } from '@provex/utils/fees'\n\nexport interface PartialFulfillment {\n /** Gross USDC the gate authorized for release (typedData.releaseAmount). */\n grossReleased: bigint\n /** Net USDC the buyer actually receives (grossReleased − fee). */\n netToBuyer: bigint\n /** Protocol fee in USDC base units. 0n when no fee applies. */\n protocolFee: bigint\n /** Locked USDC returned to the maker (intent.amount − grossReleased). */\n returnedToMaker: bigint\n contractedFiatDisplay: string\n attestedFiatDisplay: string\n netToBuyerDisplay: string\n returnedToMakerDisplay: string\n /** null when protocolFee === 0n so the caller can skip rendering the row. */\n protocolFeeDisplay: string | null\n}\n\n/**\n * Partial-fulfilment breakdown derived from the verified attestation.\n *\n * Returns `null` when:\n * - attestation isn't present (nothing to compare)\n * - releaseAmount >= intent.amount (full settlement, no partial story)\n * - intent/tokenInfo missing (defensive)\n */\nexport function computePartialFulfillment({\n attestation,\n intentAmount,\n conversionRate,\n tokenInfo,\n feeBps,\n}: {\n attestation: V3AttestationResponseObject | null\n intentAmount: bigint | null | undefined\n conversionRate: bigint | null | undefined\n tokenInfo: TokenInfo | null\n feeBps: bigint | null | undefined\n}): PartialFulfillment | null {\n if (!attestation || !intentAmount || !conversionRate || !tokenInfo) return null\n const grossReleased = BigInt(attestation.typedDataValue.releaseAmount)\n if (grossReleased >= intentAmount) return null\n\n const returnedToMaker = intentAmount - grossReleased\n const effectiveFeeBps = feeBps ?? 0n\n const netToBuyer =\n effectiveFeeBps > 0n\n ? calculateNetAmount({ grossAmount: grossReleased, feeBps: effectiveFeeBps })\n : grossReleased\n const protocolFee =\n effectiveFeeBps > 0n\n ? calculateFeeAmount({ amount: grossReleased, feeBps: effectiveFeeBps })\n : 0n\n\n const contractedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: intentAmount,\n rate: conversionRate,\n })\n const attestedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: grossReleased,\n rate: conversionRate,\n })\n\n const formatTokenDisplay = (value: bigint): string =>\n formatUnits(value, {\n decimals: tokenInfo.decimals,\n truncateDecimals: 2,\n padDecimals: 2,\n })\n\n return {\n grossReleased,\n netToBuyer,\n protocolFee,\n returnedToMaker,\n contractedFiatDisplay: formatTokenDisplay(contractedFiatRaw),\n attestedFiatDisplay: formatTokenDisplay(attestedFiatRaw),\n netToBuyerDisplay: formatTokenDisplay(netToBuyer),\n returnedToMakerDisplay: formatTokenDisplay(returnedToMaker),\n protocolFeeDisplay: protocolFee > 0n ? formatTokenDisplay(protocolFee) : null,\n }\n}\n","import { useCallback, useRef, useState } from 'react'\nimport type { CaptureCandidate, SelectTransaction } from './types'\n\nexport interface ManualTransactionSelect {\n /**\n * Resolver injected into useProving — the enclave adapter calls this when\n * auto-match of the bank transaction fails (extensionEnclave.ts:189-190).\n */\n selectTransaction: SelectTransaction\n /**\n * Non-null while the adapter is awaiting a pick; drives the\n * TransactionSelector rendered inside GenerateProofStep.\n */\n candidates: CaptureCandidate[] | null\n /**\n * Called by TransactionSelector with the chosen candidate's originalIndex.\n * Resolves the pending promise and resets candidates to null.\n */\n pick: (originalIndex: number) => void\n}\n\n/**\n * Bridge the framework-agnostic {@link SelectTransaction} resolver the enclave\n * adapter expects (extensionEnclave.ts:189-190) to React state the\n * GenerateProofStep TransactionSelector can render.\n *\n * The adapter awaits the promise returned by `selectTransaction`; the user's\n * call to `pick` resolves it with the chosen `originalIndex`.\n *\n * `useRef` is justified here exclusively for holding the pending promise\n * resolver across renders without triggering a re-render cycle — the single\n * sanctioned use per the react-state-preferences memory.\n */\nexport function useManualTransactionSelect(): ManualTransactionSelect {\n const [candidates, setCandidates] = useState<CaptureCandidate[] | null>(null)\n const resolveRef = useRef<((originalIndex: number) => void) | null>(null)\n\n const selectTransaction = useCallback<SelectTransaction>((next) => {\n return new Promise<number>((resolve) => {\n resolveRef.current = resolve\n setCandidates(next)\n })\n }, [])\n\n const pick = useCallback((originalIndex: number) => {\n resolveRef.current?.(originalIndex)\n resolveRef.current = null\n setCandidates(null)\n }, [])\n\n return { selectTransaction, candidates, pick }\n}\n","/**\n * @fileoverview Pure builder that assembles a {@link CaptureRequest} from\n * resolved order data.\n *\n * Hoisted verbatim from the intent-assembly block in SubmitStep.tsx:312-342\n * so the mapping is unit-tested in isolation and reusable by the proving shell.\n *\n * Monetary values stay bigint in base units through the entire call stack;\n * this is the single boundary where they are stringified for the attestor\n * wire format.\n */\n\nimport type { Hex } from 'viem'\nimport type { CaptureRequest } from './types'\nimport { getExtensionPlatform } from '@provex/utils/payment'\nimport type { ProviderKey, SubProviderKey } from '@provex/utils/payment'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** All resolved order fields required to build a {@link CaptureRequest}. */\nexport interface BuildCaptureRequestInput {\n /** On-chain intent hash. */\n intentHash: Hex\n /** Chain the intent lives on. */\n chainId: number\n /** Bank-level sub-provider, e.g. `'zelle-chase'`, or `null` for top-level providers. */\n subProvider: SubProviderKey | null\n /** Provider key from the deposit. May itself be a sub-provider key (contains a dash). */\n providerKey: ProviderKey | SubProviderKey\n /** Address of the UnifiedPaymentVerifier contract for this chain. */\n verifyingContract: Hex\n /** payeeDetailsHash from the matched verifier record. */\n payeeDetailsHash: Hex\n /** Settlement token for this intent (used by the adapter for amount conversion). */\n token: TokenInfo\n /** Resolved intent fields, monetary values in bigint base units. */\n intent: {\n amount: bigint\n timestamp: bigint\n paymentMethod: Hex | undefined\n fiatCurrency: Hex\n conversionRate: bigint\n }\n}\n\n/**\n * Attestation-route overrides for providers whose attestation namespace differs\n * from their capture namespace.\n *\n * Only generic-Zelle does today: capture addresses the bank's template\n * (`chase` + `transfer_zelle`) while the enclave's verify route demands the\n * generic platform plus a bank-specific action type (`zelle` +\n * `transfer_zelle_chase`). zkp2p's `resolveBuyerTeeAttestationRoute`\n * (@zkp2p/sdk 0.9.0) hard-fails anything else for Zelle, and its accepted set\n * is exactly `transfer_zelle_{bofa,chase,citi}` — which matches our\n * `zelle-{bofa,chase,citi}` sub-provider keys one-for-one.\n *\n * Returns an empty object for every other provider, where the two namespaces\n * coincide and the fallbacks in {@link CaptureRequest} already do the right\n * thing. Keeping the override absent (rather than echoing platform/actionType)\n * means \"no override needed\" stays distinguishable from \"overridden\".\n *\n * @param providerKey - Sub-provider key when present, else the provider key.\n * @param parentProvider - The portion before the dash, e.g. 'zelle'.\n */\nfunction attestationRouteFor({\n providerKey,\n parentProvider,\n}: {\n providerKey: string\n parentProvider: string\n}): Pick<CaptureRequest, 'attestationPlatform' | 'attestationActionType'> {\n if (parentProvider !== 'zelle' || !providerKey.includes('-')) return {}\n const bank = providerKey.slice(parentProvider.length + 1)\n return {\n attestationPlatform: parentProvider,\n attestationActionType: `transfer_${parentProvider}_${bank}`,\n }\n}\n\n/**\n * Build the {@link CaptureRequest} from resolved order data.\n *\n * Pure hoist of the intent-assembly block in SubmitStep.tsx:312-342.\n * No side effects, no I/O, no React. Safe to call in any environment.\n *\n * @param input - All resolved order fields needed to build the request.\n * @returns A fully-formed {@link CaptureRequest} ready for a {@link CaptureAdapter}.\n */\nexport function buildCaptureRequest(input: BuildCaptureRequestInput): CaptureRequest {\n const providerKey = input.subProvider ?? input.providerKey\n const parentProvider = providerKey.includes('-')\n ? providerKey.split('-')[0]\n : providerKey\n\n const platform = getExtensionPlatform(providerKey)\n const actionType = `transfer_${parentProvider}`\n\n return {\n provider: input.providerKey,\n platform,\n actionType,\n ...attestationRouteFor({ providerKey, parentProvider }),\n verifyingContract: input.verifyingContract,\n chainId: input.chainId,\n token: input.token,\n intent: {\n intentHash: input.intentHash,\n amount: input.intent.amount.toString(),\n timestampMs: (input.intent.timestamp * 1000n).toString(),\n paymentMethod: input.intent.paymentMethod ?? ('0x' as Hex),\n fiatCurrency: input.intent.fiatCurrency,\n conversionRate: input.intent.conversionRate.toString(),\n payeeDetails: input.payeeDetailsHash,\n timestampBufferMs: '300000',\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/buyerTeeAttestation.ts","../src/derivations.ts","../src/useManualTransactionSelect.ts","../src/buildCaptureRequest.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @fileoverview Buyer trusted-execution attestation client.\n *\n * In PeerAuth extension 0.5.2+, capture produces a buyerTee proof —\n * `{ proofType: 'buyerTee', encryptedSessionMaterial, params }` — where the\n * bank session is already encrypted toward the enclave (cookies never reach\n * our page in plaintext). Instead of re-signing a Reclaim proof through our\n * gate, we hand that material to zkp2p's trusted-execution attestation\n * service, which verifies the payment inside an Amazon Web Services Nitro\n * enclave and signs the attestation directly.\n *\n * The response `responseObject` is the same `V3AttestationResponseObject`\n * shape our gate returns, so it flows through the existing\n * `encodeV3PaymentProof` path unchanged.\n *\n * Moved verbatim from `@provex/ui` (packages/ui/src/lib/buyerTeeAttestation.ts)\n * with the base URL changed from a `@provex/ui` config import to an injected\n * `attestationBaseUrl` argument so this package carries no `@provex/ui` dep.\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\n\n/**\n * Runtime shape of a buyerTee proof emitted by the PeerAuth extension.\n * Narrow with {@link asBuyerTeeProof} before use.\n */\nexport interface BuyerTeeProof {\n proofType: 'buyerTee'\n /** Session material already encrypted toward the enclave (opaque to us). */\n encryptedSessionMaterial: string\n /** Platform-specific params, e.g. Revolut `{ index }`, Chase `{ index }`. */\n params: unknown\n actionType?: string\n actionPlatform?: string\n}\n\n/**\n * Narrow an extension proof to a buyerTee proof, or null when it is the\n * legacy Reclaim shape (or malformed).\n */\nexport function asBuyerTeeProof(proof: unknown): BuyerTeeProof | null {\n if (!proof || typeof proof !== 'object') return null\n const candidate = proof as Record<string, unknown>\n if (candidate.proofType !== 'buyerTee') return null\n if (typeof candidate.encryptedSessionMaterial !== 'string') return null\n return candidate as unknown as BuyerTeeProof\n}\n\n/** Intent details the enclave binds the attestation to. */\nexport interface BuyerTeeIntent {\n intentHash: Hex\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\nexport interface RequestBuyerTeeAttestationArgs {\n /** Gate/attestation origin (was `gateForwarderBaseUrl` in @provex/ui). */\n attestationBaseUrl: string\n /**\n * Platform for the ATTESTATION route, e.g. `zelle` for every Zelle bank.\n * See `CaptureRequest.attestationPlatform`.\n */\n platform: string\n /**\n * Action type for the ATTESTATION route, e.g. `transfer_zelle_chase`.\n * See `CaptureRequest.attestationActionType`.\n */\n actionType: string\n /** Encrypted session material from the buyerTee capture. */\n encryptedSessionMaterial: string\n /** Platform params from the capture (e.g. `{ index }`). */\n params: unknown\n /** PulseChain UnifiedPaymentVerifier the gate re-signs the attestation for. */\n verifyingContract: string\n intent: BuyerTeeIntent\n}\n\n/**\n * Pull a human-readable reason out of the gate's error body.\n *\n * The gate returns `{ success: false, error, message }` JSON, where `message`\n * is the enclave's own words (e.g. \"…payment not COMPLETE\"). Surface that. If\n * the body is not that shape — most notably a Cloudflare/Caddy HTML error page\n * when the gate is genuinely unreachable — fall back to a short marker rather\n * than dumping a page of markup into the thrown message.\n */\nfunction extractGateErrorMessage(detail: string): string {\n const trimmed = detail.trim()\n if (!trimmed) return ''\n try {\n const parsed = JSON.parse(trimmed) as { message?: unknown; error?: unknown }\n if (typeof parsed.message === 'string' && parsed.message) return parsed.message\n if (typeof parsed.error === 'string' && parsed.error) return parsed.error\n } catch {\n // Not JSON — an HTML gateway page (e.g. a raw Cloudflare 502) carries no\n // useful reason for the user, so collapse it instead of echoing markup.\n if (/^\\s*<(?:!doctype|html)/i.test(trimmed)) return 'the attestation gateway is unavailable'\n }\n return trimmed\n}\n\n/** The gate's error code for a payment that already settled an order. */\nconst PAYMENT_ALREADY_USED_CODE = 'NULLIFIER_ALREADY_USED'\n\n/**\n * The gate refused the attestation because this bank payment already\n * settled an order (its nullifier is used or reserved). Distinct from other\n * failures so a caller holding several candidate payments of the same amount\n * can move on to the next one instead of failing the order.\n */\nexport class PaymentAlreadyUsedError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'PaymentAlreadyUsedError'\n }\n}\n\n/** The gate's machine-readable `error` code from a JSON error body, if any. */\nfunction extractGateErrorCode(detail: string): string | undefined {\n try {\n const parsed = JSON.parse(detail) as { error?: unknown }\n return typeof parsed.error === 'string' ? parsed.error : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Verify a buyer payment via the gate. The gate proxies the encrypted capture\n * to zkp2p's enclave, then re-signs the resulting attestation for PulseChain\n * with our witness key — the enclave itself refuses to sign for 369.\n *\n * Throws with the gate's error text on a non-2xx response, as a\n * {@link PaymentAlreadyUsedError} when the payment already settled an order.\n */\nexport async function requestBuyerTeeAttestation(\n args: RequestBuyerTeeAttestationArgs,\n): Promise<V3AttestationResponseObject> {\n const url = `${args.attestationBaseUrl}/api/v0/attestation/buyer-verify/${encodeURIComponent(args.platform)}/${encodeURIComponent(args.actionType)}`\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n encryptedSessionMaterial: args.encryptedSessionMaterial,\n params: args.params,\n verifyingContract: args.verifyingContract,\n intent: args.intent,\n }),\n })\n\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n const message = `Trusted-execution attestation failed (${response.status}): ${extractGateErrorMessage(detail) || response.statusText}`\n if (response.status === 409 && extractGateErrorCode(detail) === PAYMENT_ALREADY_USED_CODE) {\n throw new PaymentAlreadyUsedError(message)\n }\n throw new Error(message)\n }\n\n const json = (await response.json()) as\n | { responseObject?: V3AttestationResponseObject }\n | V3AttestationResponseObject\n const responseObject =\n (json as { responseObject?: V3AttestationResponseObject }).responseObject ??\n (json as V3AttestationResponseObject)\n\n if (!responseObject?.typedDataValue || !responseObject?.signature) {\n throw new Error('Trusted-execution attestation response missing typedDataValue/signature')\n }\n return responseObject\n}\n","/**\n * @fileoverview Pure proving derivations moved from @provex/ui\n * (packages/ui/src/components/UnifiedBuy/ProofPanel/submitStepDerivations.ts).\n *\n * Bigint throughout — see the `money-values-stay-bigint-until-display` rule.\n * The only string conversions happen at the formatter boundary (formatUnits),\n * which is the display layer.\n */\n\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\nimport { formatUnits } from '@provex/utils/units'\nimport { convertTokenInputToCurrency } from '@provex/utils/conversionRates'\nimport { calculateFeeAmount, calculateNetAmount } from '@provex/utils/fees'\n\nexport interface PartialFulfillment {\n /** Gross USDC the gate authorized for release (typedData.releaseAmount). */\n grossReleased: bigint\n /** Net USDC the buyer actually receives (grossReleased − fee). */\n netToBuyer: bigint\n /** Protocol fee in USDC base units. 0n when no fee applies. */\n protocolFee: bigint\n /** Locked USDC returned to the maker (intent.amount − grossReleased). */\n returnedToMaker: bigint\n contractedFiatDisplay: string\n attestedFiatDisplay: string\n netToBuyerDisplay: string\n returnedToMakerDisplay: string\n /** null when protocolFee === 0n so the caller can skip rendering the row. */\n protocolFeeDisplay: string | null\n}\n\n/**\n * Partial-fulfilment breakdown derived from the verified attestation.\n *\n * Returns `null` when:\n * - attestation isn't present (nothing to compare)\n * - releaseAmount >= intent.amount (full settlement, no partial story)\n * - intent/tokenInfo missing (defensive)\n */\nexport function computePartialFulfillment({\n attestation,\n intentAmount,\n conversionRate,\n tokenInfo,\n feeBps,\n}: {\n attestation: V3AttestationResponseObject | null\n intentAmount: bigint | null | undefined\n conversionRate: bigint | null | undefined\n tokenInfo: TokenInfo | null\n feeBps: bigint | null | undefined\n}): PartialFulfillment | null {\n if (!attestation || !intentAmount || !conversionRate || !tokenInfo) return null\n const grossReleased = BigInt(attestation.typedDataValue.releaseAmount)\n if (grossReleased >= intentAmount) return null\n\n const returnedToMaker = intentAmount - grossReleased\n const effectiveFeeBps = feeBps ?? 0n\n const netToBuyer =\n effectiveFeeBps > 0n\n ? calculateNetAmount({ grossAmount: grossReleased, feeBps: effectiveFeeBps })\n : grossReleased\n const protocolFee =\n effectiveFeeBps > 0n\n ? calculateFeeAmount({ amount: grossReleased, feeBps: effectiveFeeBps })\n : 0n\n\n const contractedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: intentAmount,\n rate: conversionRate,\n })\n const attestedFiatRaw = convertTokenInputToCurrency({\n token: tokenInfo,\n amountOutInt: grossReleased,\n rate: conversionRate,\n })\n\n const formatTokenDisplay = (value: bigint): string =>\n formatUnits(value, {\n decimals: tokenInfo.decimals,\n truncateDecimals: 2,\n padDecimals: 2,\n })\n\n return {\n grossReleased,\n netToBuyer,\n protocolFee,\n returnedToMaker,\n contractedFiatDisplay: formatTokenDisplay(contractedFiatRaw),\n attestedFiatDisplay: formatTokenDisplay(attestedFiatRaw),\n netToBuyerDisplay: formatTokenDisplay(netToBuyer),\n returnedToMakerDisplay: formatTokenDisplay(returnedToMaker),\n protocolFeeDisplay: protocolFee > 0n ? formatTokenDisplay(protocolFee) : null,\n }\n}\n","import { useCallback, useRef, useState } from 'react'\nimport type { CaptureCandidate, SelectTransaction } from './types'\n\nexport interface ManualTransactionSelect {\n /**\n * Resolver injected into useProving — the enclave adapter calls this when\n * auto-match of the bank transaction fails (extensionEnclave.ts:189-190).\n */\n selectTransaction: SelectTransaction\n /**\n * Non-null while the adapter is awaiting a pick; drives the\n * TransactionSelector rendered inside GenerateProofStep.\n */\n candidates: CaptureCandidate[] | null\n /**\n * Called by TransactionSelector with the chosen candidate's originalIndex.\n * Resolves the pending promise and resets candidates to null.\n */\n pick: (originalIndex: number) => void\n}\n\n/**\n * Bridge the framework-agnostic {@link SelectTransaction} resolver the enclave\n * adapter expects (extensionEnclave.ts:189-190) to React state the\n * GenerateProofStep TransactionSelector can render.\n *\n * The adapter awaits the promise returned by `selectTransaction`; the user's\n * call to `pick` resolves it with the chosen `originalIndex`.\n *\n * `useRef` is justified here exclusively for holding the pending promise\n * resolver across renders without triggering a re-render cycle — the single\n * sanctioned use per the react-state-preferences memory.\n */\nexport function useManualTransactionSelect(): ManualTransactionSelect {\n const [candidates, setCandidates] = useState<CaptureCandidate[] | null>(null)\n const resolveRef = useRef<((originalIndex: number) => void) | null>(null)\n\n const selectTransaction = useCallback<SelectTransaction>((next) => {\n return new Promise<number>((resolve) => {\n resolveRef.current = resolve\n setCandidates(next)\n })\n }, [])\n\n const pick = useCallback((originalIndex: number) => {\n resolveRef.current?.(originalIndex)\n resolveRef.current = null\n setCandidates(null)\n }, [])\n\n return { selectTransaction, candidates, pick }\n}\n","/**\n * @fileoverview Pure builder that assembles a {@link CaptureRequest} from\n * resolved order data.\n *\n * Hoisted verbatim from the intent-assembly block in SubmitStep.tsx:312-342\n * so the mapping is unit-tested in isolation and reusable by the proving shell.\n *\n * Monetary values stay bigint in base units through the entire call stack;\n * this is the single boundary where they are stringified for the attestor\n * wire format.\n */\n\nimport type { Hex } from 'viem'\nimport type { CaptureRequest } from './types'\nimport { getExtensionPlatform } from '@provex/utils/payment'\nimport type { ProviderKey, SubProviderKey } from '@provex/utils/payment'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** All resolved order fields required to build a {@link CaptureRequest}. */\nexport interface BuildCaptureRequestInput {\n /** On-chain intent hash. */\n intentHash: Hex\n /** Chain the intent lives on. */\n chainId: number\n /** Bank-level sub-provider, e.g. `'zelle-chase'`, or `null` for top-level providers. */\n subProvider: SubProviderKey | null\n /** Provider key from the deposit. May itself be a sub-provider key (contains a dash). */\n providerKey: ProviderKey | SubProviderKey\n /** Address of the UnifiedPaymentVerifier contract for this chain. */\n verifyingContract: Hex\n /** payeeDetailsHash from the matched verifier record. */\n payeeDetailsHash: Hex\n /** Settlement token for this intent (used by the adapter for amount conversion). */\n token: TokenInfo\n /** Resolved intent fields, monetary values in bigint base units. */\n intent: {\n amount: bigint\n timestamp: bigint\n paymentMethod: Hex | undefined\n fiatCurrency: Hex\n conversionRate: bigint\n }\n}\n\n/**\n * The extension platform and action type a capture addresses.\n *\n * Generic Zelle uses zkp2p's hosted bank templates, named\n * `zelle/transfer_zelle_{bank}` (e.g. `zelle/transfer_zelle_chase`), the same\n * pair `@zkp2p/sdk` uses (`actionPlatform: zelle`,\n * `actionType: transfer_zelle_chase`). The extension always fetches its\n * template from `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`\n * and ignores a page-supplied template, so the pair must name a hosted file.\n * The older bank pair (`chase/transfer_zelle`) now 404s, and the capture dies\n * with \"Failed to fetch provider template: 404\".\n *\n * The same pair is the enclave's attestation route: zkp2p's\n * `resolveBuyerTeeAttestationRoute` accepts generic Zelle only as `zelle` plus\n * `transfer_zelle_{bofa,chase,citi}`, which matches our `zelle-{bofa,chase,citi}`\n * sub-provider keys one-for-one. Capture and attestation therefore coincide.\n *\n * Every other provider uses its extension platform and `transfer_<parent>`.\n *\n * @param providerKey - Sub-provider key when present, else the provider key.\n * @param parentProvider - The portion before the dash, e.g. 'zelle'.\n */\nfunction captureRouteFor({\n providerKey,\n parentProvider,\n}: {\n providerKey: ProviderKey | SubProviderKey\n parentProvider: string\n}): Pick<CaptureRequest, 'platform' | 'actionType'> {\n if (parentProvider === 'zelle' && providerKey.includes('-')) {\n const bank = providerKey.slice(parentProvider.length + 1)\n return { platform: parentProvider, actionType: `transfer_${parentProvider}_${bank}` }\n }\n return { platform: getExtensionPlatform(providerKey), actionType: `transfer_${parentProvider}` }\n}\n\n/**\n * Build the {@link CaptureRequest} from resolved order data.\n *\n * Pure hoist of the intent-assembly block in SubmitStep.tsx:312-342.\n * No side effects, no I/O, no React. Safe to call in any environment.\n *\n * @param input - All resolved order fields needed to build the request.\n * @returns A fully-formed {@link CaptureRequest} ready for a {@link CaptureAdapter}.\n */\nexport function buildCaptureRequest(input: BuildCaptureRequestInput): CaptureRequest {\n const providerKey = input.subProvider ?? input.providerKey\n const parentProvider = providerKey.includes('-')\n ? providerKey.split('-')[0]\n : providerKey\n\n return {\n provider: input.providerKey,\n ...captureRouteFor({ providerKey, parentProvider }),\n verifyingContract: input.verifyingContract,\n chainId: input.chainId,\n token: input.token,\n intent: {\n intentHash: input.intentHash,\n amount: input.intent.amount.toString(),\n timestampMs: (input.intent.timestamp * 1000n).toString(),\n paymentMethod: input.intent.paymentMethod ?? ('0x' as Hex),\n fiatCurrency: input.intent.fiatCurrency,\n conversionRate: input.intent.conversionRate.toString(),\n payeeDetails: input.payeeDetailsHash,\n timestampBufferMs: '300000',\n },\n }\n}\n"]}
|
|
@@ -47,26 +47,19 @@ interface ProvingIntent {
|
|
|
47
47
|
interface CaptureRequest {
|
|
48
48
|
/** Parent provider key, e.g. 'zelle', 'revolut'. */
|
|
49
49
|
provider: string;
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Extension platform, e.g. 'venmo', or 'zelle' for every Zelle bank. With
|
|
52
|
+
* {@link actionType} it names the hosted provider template the extension
|
|
53
|
+
* fetches: `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`.
|
|
54
|
+
*/
|
|
51
55
|
platform: string;
|
|
52
|
-
/** Action type, e.g. '
|
|
56
|
+
/** Action type, e.g. 'transfer_venmo' or 'transfer_zelle_chase'. */
|
|
53
57
|
actionType: string;
|
|
54
58
|
/**
|
|
55
59
|
* Platform for the ATTESTATION hop, when it differs from {@link platform}.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* `chase` + `transfer_zelle`. Attestation addresses the enclave's verify
|
|
60
|
-
* route, which for Zelle demands the GENERIC platform plus a BANK-SPECIFIC
|
|
61
|
-
* action type — `zelle` + `transfer_zelle_chase`.
|
|
62
|
-
*
|
|
63
|
-
* Ground truth is zkp2p's own `resolveBuyerTeeAttestationRoute`
|
|
64
|
-
* (@zkp2p/sdk 0.9.0), which throws otherwise:
|
|
65
|
-
* 'Generic Zelle buyer TEE fulfillment requires actionPlatform "zelle"
|
|
66
|
-
* and a bank-specific transfer_zelle_* actionType.'
|
|
67
|
-
*
|
|
68
|
-
* Omit for providers where the two namespaces coincide (venmo, cashapp, …);
|
|
69
|
-
* consumers fall back to {@link platform}.
|
|
60
|
+
* No provider needs this today: generic Zelle captures and attests as
|
|
61
|
+
* `zelle` + `transfer_zelle_{bank}`. Omit it; consumers fall back to
|
|
62
|
+
* {@link platform}.
|
|
70
63
|
*/
|
|
71
64
|
attestationPlatform?: string;
|
|
72
65
|
/**
|
|
@@ -47,26 +47,19 @@ interface ProvingIntent {
|
|
|
47
47
|
interface CaptureRequest {
|
|
48
48
|
/** Parent provider key, e.g. 'zelle', 'revolut'. */
|
|
49
49
|
provider: string;
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Extension platform, e.g. 'venmo', or 'zelle' for every Zelle bank. With
|
|
52
|
+
* {@link actionType} it names the hosted provider template the extension
|
|
53
|
+
* fetches: `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`.
|
|
54
|
+
*/
|
|
51
55
|
platform: string;
|
|
52
|
-
/** Action type, e.g. '
|
|
56
|
+
/** Action type, e.g. 'transfer_venmo' or 'transfer_zelle_chase'. */
|
|
53
57
|
actionType: string;
|
|
54
58
|
/**
|
|
55
59
|
* Platform for the ATTESTATION hop, when it differs from {@link platform}.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* `chase` + `transfer_zelle`. Attestation addresses the enclave's verify
|
|
60
|
-
* route, which for Zelle demands the GENERIC platform plus a BANK-SPECIFIC
|
|
61
|
-
* action type — `zelle` + `transfer_zelle_chase`.
|
|
62
|
-
*
|
|
63
|
-
* Ground truth is zkp2p's own `resolveBuyerTeeAttestationRoute`
|
|
64
|
-
* (@zkp2p/sdk 0.9.0), which throws otherwise:
|
|
65
|
-
* 'Generic Zelle buyer TEE fulfillment requires actionPlatform "zelle"
|
|
66
|
-
* and a bank-specific transfer_zelle_* actionType.'
|
|
67
|
-
*
|
|
68
|
-
* Omit for providers where the two namespaces coincide (venmo, cashapp, …);
|
|
69
|
-
* consumers fall back to {@link platform}.
|
|
60
|
+
* No provider needs this today: generic Zelle captures and attests as
|
|
61
|
+
* `zelle` + `transfer_zelle_{bank}`. Omit it; consumers fall back to
|
|
62
|
+
* {@link platform}.
|
|
70
63
|
*/
|
|
71
64
|
attestationPlatform?: string;
|
|
72
65
|
/**
|
package/dist/useProving.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/useProving.ts"],"names":["sleep","useState","useRef","useCallback","extractNullifier"],"mappings":"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","file":"useProving.cjs","sourcesContent":["/**\n * @fileoverview Seam contract + state types for the proving orchestrator.\n *\n * `CaptureAdapter` is the swappable capture backend (extension+enclave today,\n * TLSNotary tomorrow). `ProvingState` is the single discriminated-union\n * machine that subsumes the live proof-substep machine (ProofPanel) and the\n * attestation lifecycle (submitStepDerivations). Monetary values stay bigint\n * in base units; the only stringified amount is `ProvingIntent.amount`, which\n * mirrors the existing on-the-wire BuyerTeeIntent shape (buyerTeeAttestation).\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** Transaction candidate the adapter surfaces for manual selection. Mirrors\n * the `MetadataMessage` fields the live TransactionSelector consumes; kept\n * structural so the package needs no @provex/extension-react import. */\nexport interface CaptureCandidate {\n amount: number | string\n currency?: string\n date?: string | number | Date\n originalIndex: number\n /**\n * Provider attestation params for this row (zkp2p 0.6.0 spec §4). These ARE\n * the attestation query params the enclave verifies against — they must be\n * forwarded (merged with the metadata index) on the buyerTee proof, not\n * dropped. Optional because the display path doesn't read them.\n */\n params?: Record<string, string | number | boolean>\n}\n\n/** Identity the attestor binds the attestation to. Mirrors the live\n * attestationIntent built in SubmitStep.tsx:333-342 / BuyerTeeIntent. */\nexport interface ProvingIntent {\n intentHash: Hex\n /** bigint base units, stringified (matches BuyerTeeIntent.amount). */\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n /** payeeDetailsHash */\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\n/** What the orchestrator hands a capture adapter. */\nexport interface CaptureRequest {\n /** Parent provider key, e.g. 'zelle', 'revolut'. */\n provider: string\n /** Extension platform slug, e.g. 'chase'. */\n platform: string\n /** Action type, e.g. 'transfer_zelle'. */\n actionType: string\n /**\n * Platform for the ATTESTATION hop, when it differs from {@link platform}.\n *\n * Capture and attestation use two different namespaces. Capture addresses a\n * provider template (`{platform}/{actionType}.json`), so a Zelle bank is\n * `chase` + `transfer_zelle`. Attestation addresses the enclave's verify\n * route, which for Zelle demands the GENERIC platform plus a BANK-SPECIFIC\n * action type — `zelle` + `transfer_zelle_chase`.\n *\n * Ground truth is zkp2p's own `resolveBuyerTeeAttestationRoute`\n * (@zkp2p/sdk 0.9.0), which throws otherwise:\n * 'Generic Zelle buyer TEE fulfillment requires actionPlatform \"zelle\"\n * and a bank-specific transfer_zelle_* actionType.'\n *\n * Omit for providers where the two namespaces coincide (venmo, cashapp, …);\n * consumers fall back to {@link platform}.\n */\n attestationPlatform?: string\n /**\n * Action type for the ATTESTATION hop, when it differs from\n * {@link actionType}. See {@link attestationPlatform}. Omit when they\n * coincide; consumers fall back to {@link actionType}.\n */\n attestationActionType?: string\n intent: ProvingIntent\n /** UnifiedPaymentVerifier the attestation is re-signed for. */\n verifyingContract: Hex\n chainId: number\n /**\n * Settlement token for this intent (e.g. USDC on the target chain).\n * Used by the adapter to compute the fiat currency amount via\n * convertTokenInputToCurrency so auto-match works correctly for non-1:1 rates.\n */\n token: TokenInfo\n}\n\n/** Opaque per-backend capture state; the orchestrator passes it back unmodified. */\nexport interface CaptureHandle {\n readonly kind: string\n readonly material: unknown\n}\n\n/** Uniform capture output. `verificationData` names the signing witness to\n * the on-chain verifier (enclave signer for buyerTee — SubmitStep.tsx:473-475). */\nexport interface VerifiedAttestation {\n attestation: V3AttestationResponseObject\n verificationData: Hex\n}\n\n/** Resolver the UI provides so the adapter can collect a manual transaction\n * pick when auto-match fails (GenerateProofStep.tsx:391-404). */\nexport type SelectTransaction = (candidates: CaptureCandidate[]) => Promise<number>\n\n/** A pluggable capture backend. One per capture mechanism. */\nexport interface CaptureAdapter {\n /** Stable id for telemetry, e.g. 'extension-enclave'. */\n readonly kind: string\n /** Phase 1: authenticate + capture, returning opaque material to attest. */\n authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle>\n /** Phase 2: produce a verified attestation from the captured material. */\n attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation>\n}\n\n/** Inputs the orchestrator hook needs to drive the machine. */\nexport interface ProvingRequest {\n request: CaptureRequest\n /** Optional manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n}\n\n/**\n * The proving state machine (spec lines 141-151). `attest` auto-runs on\n * entry; `readyToFulfill` waits for the user's explicit Complete Order\n * action; `error.isSubmissionError` routes retry to Retry Submission\n * (post-fulfilment) vs Retry Verification (pre-fulfilment) — exactly the\n * live SubmitStep.tsx:931-955 routing.\n */\nexport type ProvingState =\n | { status: 'authenticate' }\n | { status: 'capture' }\n | { status: 'attest' }\n | { status: 'readyToFulfill'; attestation: VerifiedAttestation }\n | { status: 'fulfillIntent'; attestation: VerifiedAttestation }\n | { status: 'confirm'; attestation: VerifiedAttestation }\n | { status: 'sync'; attestation: VerifiedAttestation }\n | { status: 'complete'; attestation: VerifiedAttestation }\n | {\n status: 'error'\n message: string\n /** true ⇒ failure occurred during/after on-chain submission. */\n isSubmissionError: boolean\n attestation?: VerifiedAttestation\n /** Typed reason, set for cases the consumer must branch on. Currently\n * 'enclave-unavailable' (non-migrated provider → caller falls back to\n * Reclaim) and 'manual-selection-required' (auto-match failed, no\n * resolver supplied). Absent for ordinary verification/submission errors. */\n code?: 'enclave-unavailable' | 'manual-selection-required'\n }\n\n/**\n * Thrown by a capture adapter when the provider returned no buyerTee capture\n * (a non-migrated / Reclaim-only provider). The caller (UnifiedBuy) catches\n * this to fall back to its retained Reclaim branch — the package itself does\n * not implement Reclaim (decision (a), spec line 256).\n */\nexport class EnclaveCaptureUnavailable extends Error {\n readonly platform: string\n constructor(platform: string) {\n super(`No enclave (buyerTee) capture available for platform \"${platform}\".`)\n this.name = 'EnclaveCaptureUnavailable'\n this.platform = platform\n }\n}\n\n/**\n * Thrown by a capture adapter when the enclave capture was *attempted* but the\n * extension surfaced a failure reason (an `errorMessage`) instead of a capture\n * — most commonly because the extension could not reach the attestation enclave\n * (a buyer-side network / firewall / privacy- or ad-blocker problem).\n *\n * Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the\n * provider has no enclave capture path at all (non-migrated). This one is an\n * operational failure the user can usually fix and retry. Keeping them separate\n * stops a transient network failure from being misread as \"this provider isn't\n * supported\", and — if the Reclaim fallback is ever re-enabled — from wrongly\n * dumping the user out of the enclave path (this error carries no\n * `enclave-unavailable` code, so it surfaces as an ordinary retryable error).\n *\n * `detail` carries the raw extension `errorMessage` for logs and support;\n * `message` is the user-facing, actionable copy.\n */\nexport class EnclaveCaptureFailed extends Error {\n readonly platform: string\n readonly detail: string\n constructor(platform: string, detail: string) {\n super(\n 'We could not reach the verification service needed to confirm this ' +\n 'payment. A firewall, or a privacy or ad-blocking tool, may be blocking ' +\n 'it — try disabling blockers or switching networks, then verify again.',\n )\n this.name = 'EnclaveCaptureFailed'\n this.platform = platform\n this.detail = detail\n }\n}\n\n/**\n * Thrown by a capture adapter when auto-match failed and no `selectTransaction`\n * resolver was provided (e.g. a headless wallet). Carries the candidates so a\n * UI caller can render the manual selector and retry (spec lines 263-267).\n */\nexport class ManualSelectionRequired extends Error {\n readonly candidates: CaptureCandidate[]\n constructor(candidates: CaptureCandidate[]) {\n super('Manual transaction selection is required but no resolver was provided.')\n this.name = 'ManualSelectionRequired'\n this.candidates = candidates\n }\n}\n","/**\n * @fileoverview useProving — headless orchestrator for the enclave proving\n * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep\n * state machine (ProofPanel) with no presentation. Renders nothing; returns\n * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.\n *\n * Owns: the ProvingState machine, the nullifier double-spend check\n * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing\n * (SubmitStep.tsx:931-955), and the minimum-visible-state timings\n * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,\n * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { Hex } from 'viem'\nimport { extractNullifier, type V3AttestationResponseObject } from '@provex/utils'\nimport { sleep } from '@provex/utils/time'\nimport { EnclaveCaptureUnavailable, ManualSelectionRequired } from './types'\nimport type {\n CaptureAdapter,\n CaptureRequest,\n SelectTransaction,\n ProvingState,\n VerifiedAttestation,\n} from './types'\n\n/**\n * Minimal context object that buildWritableMethod passes to the per-call hooks.\n * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.\n * Only `hash` is consumed by this hook — the rest is opaque context.\n */\ninterface TxCallbackContext {\n chainId: number\n request: unknown\n}\n\n/** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +\n * WriteHookParams from @provex/react but declared here to keep @provex/proving\n * from depending on the React package's built dist (which the test environment\n * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.\n *\n * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call\n * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):\n * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void\n * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void\n */\ninterface FulfillParams {\n intentHash: Hex\n attestation: V3AttestationResponseObject\n verificationData: Hex\n postIntentHookData?: Hex\n onAwaitingSignature?: (info: TxCallbackContext) => void\n onTransactionHash?: (info: TxCallbackContext & { hash: Hex }) => void\n}\n\n/** The structural slice of ProveXClient the orchestrator depends on. Keeping\n * it structural lets tests pass a light fake and avoids importing the class. */\nexport interface ProvingClient {\n /**\n * The chain this rail operates on. Must equal request.chainId — useProving\n * refuses to check the nullifier or submit on any other chain. A nullifier is\n * only meaningful on its intended chain; checking it on (or submitting it to)\n * a different chain is a cross-chain double-spend / replay vector.\n */\n readonly chainId: number\n fulfillIntent: (params: FulfillParams) => Promise<{ hash: Hex; receipt: unknown }>\n checkNullifierUsed: (params: { paymentMethodId: Hex; nullifier: Hex }) => Promise<boolean | null>\n}\n\nexport interface UseProvingOptions {\n client: ProvingClient\n captureAdapter: CaptureAdapter\n request: CaptureRequest\n /** Payment method id for the nullifier registry lookup. */\n paymentMethodId: Hex\n /** Manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n /**\n * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.\n * Receives the raw TxContext — the hook does not inspect the argument.\n */\n onAwaitingSignature?: () => void\n /** Receives the raw tx hash extracted from the TxContext+hash payload. */\n onTransactionHash?: (hash: Hex) => void\n /** Called once when the flow reaches `complete`. */\n onComplete?: (attestation: V3AttestationResponseObject) => void\n}\n\nexport interface UseProvingReturn {\n state: ProvingState\n /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */\n authenticate: () => Promise<void>\n /** Phase 3: submit fulfillIntent → confirm → sync → complete. */\n fulfill: () => Promise<void>\n /** Re-run attestation after a pre-fulfilment error. */\n retryVerification: () => Promise<void>\n /** Re-run submission after a post-fulfilment error. */\n retrySubmission: () => Promise<void>\n}\n\n/** Minimum visible-state pacing from SubmitStep.tsx:404-417 — keep a state\n * on screen at least `floorMs` so the user perceives the step. */\nasync function withMinimumVisible(floorMs: number, started: number): Promise<void> {\n const elapsed = Date.now() - started\n if (elapsed < floorMs) await sleep(floorMs - elapsed)\n}\n\n/**\n * Detect a wallet-rejection error structurally (without importing ProveXError).\n * ProveXClient always re-wraps user rejections as ProveXError with\n * code === 'WALLET_REJECTED' before the error escapes fulfillIntent.\n */\nfunction isWalletRejection(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code: unknown }).code === 'WALLET_REJECTED'\n )\n}\n\nexport function useProving(options: UseProvingOptions): UseProvingReturn {\n const {\n client,\n captureAdapter,\n request,\n paymentMethodId,\n selectTransaction,\n onAwaitingSignature,\n onTransactionHash,\n onComplete,\n } = options\n\n const [state, setState] = useState<ProvingState>({ status: 'authenticate' })\n // Re-entry guard mirroring isRequestingAttestationRef (SubmitStep.tsx:199).\n const inFlightRef = useRef(false)\n // Separate in-flight guard for submit — prevents double on-chain spend from\n // two rapid fulfill() calls before a re-render.\n const submitInFlightRef = useRef(false)\n\n const runCaptureAndAttest = useCallback(async (): Promise<void> => {\n if (inFlightRef.current) return\n inFlightRef.current = true\n try {\n // Chain pinning: the nullifier must only ever be checked on the chain the\n // intent targets. A client wired to any other chain is refused before we\n // touch capture/attest/registry — never verify on the wrong chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n\n setState({ status: 'capture' })\n const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction })\n\n const attestStarted = Date.now()\n setState({ status: 'attest' })\n const verified = await captureAdapter.attest(handle, request)\n // SubmitStep.tsx:414-417: verifying state must be visible for ≥1000ms.\n await withMinimumVisible(1000, attestStarted)\n\n // Nullifier double-spend re-check — ADVISORY. The authoritative guards are\n // (1) the gate's buyerTee re-sign, which now verifies the nullifier is\n // unused and reserves it cross-chain before signing (buyer-tee.ts), and\n // (2) the on-chain contract, which reverts fulfillIntent on a used\n // nullifier. A valid attestation therefore already implies an unused\n // nullifier. So this browser re-check must NOT fail closed on a flaky\n // public RPC (which falsely blocked legitimate buys with \"registry\n // unreachable\"): block ONLY on a positive \"already used\"; pass otherwise.\n const nullifier = extractNullifier(verified.attestation)\n if (nullifier) {\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed === true) {\n throw new Error(\n 'This payment has already been verified. The nullifier has already been used onchain.',\n )\n }\n if (isUsed === null) {\n console.warn(\n '[useProving] nullifier re-check unavailable (browser RPC) — proceeding; the gate already verified and reserved it',\n )\n }\n } else {\n console.warn(\n '[useProving] could not extract nullifier in the browser — proceeding; the gate already verified it',\n )\n }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n const code =\n error instanceof EnclaveCaptureUnavailable\n ? ('enclave-unavailable' as const)\n : error instanceof ManualSelectionRequired\n ? ('manual-selection-required' as const)\n : undefined\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\n code,\n })\n } finally {\n inFlightRef.current = false\n }\n }, [captureAdapter, client, request, paymentMethodId, selectTransaction])\n\n const submit = useCallback(\n async (verified: VerifiedAttestation): Promise<void> => {\n // Re-entry guard: prevent two rapid submit calls from both calling\n // fulfillIntent (double on-chain spend). Mirrors inFlightRef pattern.\n if (submitInFlightRef.current) return\n submitInFlightRef.current = true\n\n const submitStarted = Date.now()\n setState({ status: 'fulfillIntent', attestation: verified })\n try {\n // Chain pinning (re-asserted at submit): the nullifier was verified on\n // request.chainId; it must only ever be submitted to that same chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n await client.fulfillIntent({\n intentHash: request.intent.intentHash,\n attestation: verified.attestation,\n verificationData: verified.verificationData,\n // Wrap consumer callbacks to match the real buildWritableMethod\n // call shapes (ProveXClient.ts ~890, ~903).\n onAwaitingSignature: onAwaitingSignature\n ? (_info: TxCallbackContext) => { onAwaitingSignature() }\n : undefined,\n // buildWritableMethod fires onTransactionHash with { ...ctx, hash }\n // (ProveXClient.ts ~903). Extract hash and forward to the consumer's\n // simpler (hash: Hex) => void API.\n onTransactionHash: onTransactionHash\n ? (info: TxCallbackContext & { hash: Hex }) => {\n // After the tx is broadcast (a separate async tick before the\n // receipt arrives), surface the confirm state so it is\n // observable by consumers.\n setState({ status: 'confirm', attestation: verified })\n onTransactionHash(info.hash)\n }\n : ((_info: TxCallbackContext & { hash: Hex }) => {\n setState({ status: 'confirm', attestation: verified })\n }),\n })\n await withMinimumVisible(500, submitStarted)\n\n // fulfillIntent already awaited receipt + indexer sync internally\n // (ProveXClient.ts:906-915). Surface sync as a real visible state\n // (confirm was set in onTransactionHash above); then await a\n // minimum-visible window before complete so sync is not batched away.\n setState({ status: 'sync', attestation: verified })\n await sleep(600)\n setState({ status: 'complete', attestation: verified })\n onComplete?.(verified.attestation)\n } catch (error) {\n // Wallet rejection: return to readyToFulfill so the user can retry\n // without losing the attestation (mirrors SubmitStep.tsx:571-573).\n if (isWalletRejection(error)) {\n setState({ status: 'readyToFulfill', attestation: verified })\n return\n }\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Submission failed',\n isSubmissionError: true,\n attestation: verified,\n })\n } finally {\n submitInFlightRef.current = false\n }\n },\n [client, request.intent.intentHash, onAwaitingSignature, onTransactionHash, onComplete],\n )\n\n const authenticate = useCallback(() => runCaptureAndAttest(), [runCaptureAndAttest])\n\n const fulfill = useCallback(async () => {\n if (state.status !== 'readyToFulfill') return\n await submit(state.attestation)\n }, [state, submit])\n\n const retryVerification = useCallback(async () => {\n setState({ status: 'authenticate' })\n await runCaptureAndAttest()\n }, [runCaptureAndAttest])\n\n const retrySubmission = useCallback(async () => {\n if (state.status !== 'error' || !state.attestation) return\n await submit(state.attestation)\n }, [state, submit])\n\n return { state, authenticate, fulfill, retryVerification, retrySubmission }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/useProving.ts"],"names":["sleep","useState","useRef","useCallback","extractNullifier"],"mappings":";;;;;;;;;AA4JO,IAAM,yBAAA,GAAN,cAAwC,KAAA,CAAM;AAAA,EAC1C,QAAA;AAAA,EACT,YAAY,QAAA,EAAkB;AAC5B,IAAA,KAAA,CAAM,CAAA,sDAAA,EAAyD,QAAQ,CAAA,EAAA,CAAI,CAAA;AAC3E,IAAA,IAAA,CAAK,IAAA,GAAO,2BAAA;AACZ,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EAClB;AACF,CAAA;AAuCO,IAAM,uBAAA,GAAN,cAAsC,KAAA,CAAM;AAAA,EACxC,UAAA;AAAA,EACT,YAAY,UAAA,EAAgC;AAC1C,IAAA,KAAA,CAAM,wEAAwE,CAAA;AAC9E,IAAA,IAAA,CAAK,IAAA,GAAO,yBAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAAA,EACpB;AACF,CAAA;;;AC3GA,eAAe,kBAAA,CAAmB,SAAiB,OAAA,EAAgC;AACjF,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI,GAAI,OAAA;AAC7B,EAAA,IAAI,OAAA,GAAU,OAAA,EAAS,MAAMA,UAAA,CAAM,UAAU,OAAO,CAAA;AACtD;AAOA,SAAS,kBAAkB,KAAA,EAAyB;AAClD,EAAA,OACE,OAAO,UAAU,QAAA,IACjB,KAAA,KAAU,QACV,MAAA,IAAU,KAAA,IACT,MAA4B,IAAA,KAAS,iBAAA;AAE1C;AAEO,SAAS,WAAW,OAAA,EAA8C;AACvE,EAAA,MAAM;AAAA,IACJ,MAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA;AAAA,IACA,iBAAA;AAAA,IACA,mBAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAEJ,EAAA,MAAM,CAAC,OAAO,QAAQ,CAAA,GAAIC,eAAuB,EAAE,MAAA,EAAQ,gBAAgB,CAAA;AAE3E,EAAA,MAAM,WAAA,GAAcC,aAAO,KAAK,CAAA;AAGhC,EAAA,MAAM,iBAAA,GAAoBA,aAAO,KAAK,CAAA;AAEtC,EAAA,MAAM,mBAAA,GAAsBC,kBAAY,YAA2B;AACjE,IAAA,IAAI,YAAY,OAAA,EAAS;AACzB,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,IAAI;AAIF,MAAA,IAAI,MAAA,CAAO,OAAA,KAAY,OAAA,CAAQ,OAAA,EAAS;AACtC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,gDAAA,EAAmD,MAAA,CAAO,OAAO,CAAA,8BAAA,EAAiC,QAAQ,OAAO,CAAA,wEAAA;AAAA,SACnH;AAAA,MACF;AAEA,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,CAAA;AAC9B,MAAA,MAAM,SAAS,MAAM,cAAA,CAAe,uBAAuB,OAAA,EAAS,EAAE,mBAAmB,CAAA;AAEzF,MAAA,MAAM,aAAA,GAAgB,KAAK,GAAA,EAAI;AAC/B,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,QAAA,EAAU,CAAA;AAC7B,MAAA,MAAM,QAAA,GAAW,MAAM,cAAA,CAAe,MAAA,CAAO,QAAQ,OAAO,CAAA;AAE5D,MAAA,MAAM,kBAAA,CAAmB,KAAM,aAAa,CAAA;AAU5C,MAAA,MAAM,SAAA,GAAYC,sBAAA,CAAiB,QAAA,CAAS,WAAW,CAAA;AACvD,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,MAAM,SAAS,MAAM,MAAA,CAAO,mBAAmB,EAAE,eAAA,EAAiB,WAAW,CAAA;AAC7E,QAAA,IAAI,WAAW,IAAA,EAAM;AACnB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,IAAI,WAAW,IAAA,EAAM;AACnB,UAAA,OAAA,CAAQ,IAAA;AAAA,YACN;AAAA,WACF;AAAA,QACF;AAAA,MACF,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN;AAAA,SACF;AAAA,MACF;AAEA,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,gBAAA,EAAkB,WAAA,EAAa,UAAU,CAAA;AAAA,IAC9D,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,OACJ,KAAA,YAAiB,yBAAA,GACZ,qBAAA,GACD,KAAA,YAAiB,0BACd,2BAAA,GACD,MAAA;AACR,MAAA,QAAA,CAAS;AAAA,QACP,MAAA,EAAQ,OAAA;AAAA,QACR,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,qBAAA;AAAA,QAClD,iBAAA,EAAmB,KAAA;AAAA,QACnB;AAAA,OACD,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,WAAA,CAAY,OAAA,GAAU,KAAA;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,cAAA,EAAgB,QAAQ,OAAA,EAAS,eAAA,EAAiB,iBAAiB,CAAC,CAAA;AAExE,EAAA,MAAM,MAAA,GAASD,iBAAA;AAAA,IACb,OAAO,QAAA,KAAiD;AAGtD,MAAA,IAAI,kBAAkB,OAAA,EAAS;AAC/B,MAAA,iBAAA,CAAkB,OAAA,GAAU,IAAA;AAE5B,MAAA,MAAM,aAAA,GAAgB,KAAK,GAAA,EAAI;AAC/B,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,eAAA,EAAiB,WAAA,EAAa,UAAU,CAAA;AAC3D,MAAA,IAAI;AAGF,QAAA,IAAI,MAAA,CAAO,OAAA,KAAY,OAAA,CAAQ,OAAA,EAAS;AACtC,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,gDAAA,EAAmD,MAAA,CAAO,OAAO,CAAA,8BAAA,EAAiC,QAAQ,OAAO,CAAA,wEAAA;AAAA,WACnH;AAAA,QACF;AACA,QAAA,MAAM,OAAO,aAAA,CAAc;AAAA,UACzB,UAAA,EAAY,QAAQ,MAAA,CAAO,UAAA;AAAA,UAC3B,aAAa,QAAA,CAAS,WAAA;AAAA,UACtB,kBAAkB,QAAA,CAAS,gBAAA;AAAA;AAAA;AAAA,UAG3B,mBAAA,EAAqB,mBAAA,GACjB,CAAC,KAAA,KAA6B;AAAE,YAAA,mBAAA,EAAoB;AAAA,UAAE,CAAA,GACtD,KAAA,CAAA;AAAA;AAAA;AAAA;AAAA,UAIJ,iBAAA,EAAmB,iBAAA,GACf,CAAC,IAAA,KAA4C;AAI3C,YAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,UAAU,CAAA;AACrD,YAAA,iBAAA,CAAkB,KAAK,IAAI,CAAA;AAAA,UAC7B,CAAA,GACC,CAAC,KAAA,KAA6C;AAC7C,YAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,UAAU,CAAA;AAAA,UACvD;AAAA,SACL,CAAA;AACD,QAAA,MAAM,kBAAA,CAAmB,KAAK,aAAa,CAAA;AAM3C,QAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,MAAA,EAAQ,WAAA,EAAa,UAAU,CAAA;AAClD,QAAA,MAAMH,WAAM,GAAG,CAAA;AACf,QAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,UAAA,EAAY,WAAA,EAAa,UAAU,CAAA;AACtD,QAAA,UAAA,GAAa,SAAS,WAAW,CAAA;AAAA,MACnC,SAAS,KAAA,EAAO;AAGd,QAAA,IAAI,iBAAA,CAAkB,KAAK,CAAA,EAAG;AAC5B,UAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,gBAAA,EAAkB,WAAA,EAAa,UAAU,CAAA;AAC5D,UAAA;AAAA,QACF;AACA,QAAA,QAAA,CAAS;AAAA,UACP,MAAA,EAAQ,OAAA;AAAA,UACR,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,mBAAA;AAAA,UAClD,iBAAA,EAAmB,IAAA;AAAA,UACnB,WAAA,EAAa;AAAA,SACd,CAAA;AAAA,MACH,CAAA,SAAE;AACA,QAAA,iBAAA,CAAkB,OAAA,GAAU,KAAA;AAAA,MAC9B;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,OAAA,CAAQ,OAAO,UAAA,EAAY,mBAAA,EAAqB,mBAAmB,UAAU;AAAA,GACxF;AAEA,EAAA,MAAM,eAAeG,iBAAA,CAAY,MAAM,qBAAoB,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAEnF,EAAA,MAAM,OAAA,GAAUA,kBAAY,YAAY;AACtC,IAAA,IAAI,KAAA,CAAM,WAAW,gBAAA,EAAkB;AACvC,IAAA,MAAM,MAAA,CAAO,MAAM,WAAW,CAAA;AAAA,EAChC,CAAA,EAAG,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAElB,EAAA,MAAM,iBAAA,GAAoBA,kBAAY,YAAY;AAChD,IAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,cAAA,EAAgB,CAAA;AACnC,IAAA,MAAM,mBAAA,EAAoB;AAAA,EAC5B,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,MAAM,eAAA,GAAkBA,kBAAY,YAAY;AAC9C,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,OAAA,IAAW,CAAC,MAAM,WAAA,EAAa;AACpD,IAAA,MAAM,MAAA,CAAO,MAAM,WAAW,CAAA;AAAA,EAChC,CAAA,EAAG,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAElB,EAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,OAAA,EAAS,mBAAmB,eAAA,EAAgB;AAC5E","file":"useProving.cjs","sourcesContent":["/**\n * @fileoverview Seam contract + state types for the proving orchestrator.\n *\n * `CaptureAdapter` is the swappable capture backend (extension+enclave today,\n * TLSNotary tomorrow). `ProvingState` is the single discriminated-union\n * machine that subsumes the live proof-substep machine (ProofPanel) and the\n * attestation lifecycle (submitStepDerivations). Monetary values stay bigint\n * in base units; the only stringified amount is `ProvingIntent.amount`, which\n * mirrors the existing on-the-wire BuyerTeeIntent shape (buyerTeeAttestation).\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** Transaction candidate the adapter surfaces for manual selection. Mirrors\n * the `MetadataMessage` fields the live TransactionSelector consumes; kept\n * structural so the package needs no @provex/extension-react import. */\nexport interface CaptureCandidate {\n amount: number | string\n currency?: string\n date?: string | number | Date\n originalIndex: number\n /**\n * Provider attestation params for this row (zkp2p 0.6.0 spec §4). These ARE\n * the attestation query params the enclave verifies against — they must be\n * forwarded (merged with the metadata index) on the buyerTee proof, not\n * dropped. Optional because the display path doesn't read them.\n */\n params?: Record<string, string | number | boolean>\n}\n\n/** Identity the attestor binds the attestation to. Mirrors the live\n * attestationIntent built in SubmitStep.tsx:333-342 / BuyerTeeIntent. */\nexport interface ProvingIntent {\n intentHash: Hex\n /** bigint base units, stringified (matches BuyerTeeIntent.amount). */\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n /** payeeDetailsHash */\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\n/** What the orchestrator hands a capture adapter. */\nexport interface CaptureRequest {\n /** Parent provider key, e.g. 'zelle', 'revolut'. */\n provider: string\n /**\n * Extension platform, e.g. 'venmo', or 'zelle' for every Zelle bank. With\n * {@link actionType} it names the hosted provider template the extension\n * fetches: `https://api.zkp2p.xyz/providers/<platform>/<actionType>.json`.\n */\n platform: string\n /** Action type, e.g. 'transfer_venmo' or 'transfer_zelle_chase'. */\n actionType: string\n /**\n * Platform for the ATTESTATION hop, when it differs from {@link platform}.\n * No provider needs this today: generic Zelle captures and attests as\n * `zelle` + `transfer_zelle_{bank}`. Omit it; consumers fall back to\n * {@link platform}.\n */\n attestationPlatform?: string\n /**\n * Action type for the ATTESTATION hop, when it differs from\n * {@link actionType}. See {@link attestationPlatform}. Omit when they\n * coincide; consumers fall back to {@link actionType}.\n */\n attestationActionType?: string\n intent: ProvingIntent\n /** UnifiedPaymentVerifier the attestation is re-signed for. */\n verifyingContract: Hex\n chainId: number\n /**\n * Settlement token for this intent (e.g. USDC on the target chain).\n * Used by the adapter to compute the fiat currency amount via\n * convertTokenInputToCurrency so auto-match works correctly for non-1:1 rates.\n */\n token: TokenInfo\n}\n\n/** Opaque per-backend capture state; the orchestrator passes it back unmodified. */\nexport interface CaptureHandle {\n readonly kind: string\n readonly material: unknown\n}\n\n/** Uniform capture output. `verificationData` names the signing witness to\n * the on-chain verifier (enclave signer for buyerTee — SubmitStep.tsx:473-475). */\nexport interface VerifiedAttestation {\n attestation: V3AttestationResponseObject\n verificationData: Hex\n}\n\n/** Resolver the UI provides so the adapter can collect a manual transaction\n * pick when auto-match fails (GenerateProofStep.tsx:391-404). */\nexport type SelectTransaction = (candidates: CaptureCandidate[]) => Promise<number>\n\n/** A pluggable capture backend. One per capture mechanism. */\nexport interface CaptureAdapter {\n /** Stable id for telemetry, e.g. 'extension-enclave'. */\n readonly kind: string\n /** Phase 1: authenticate + capture, returning opaque material to attest. */\n authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle>\n /** Phase 2: produce a verified attestation from the captured material. */\n attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation>\n}\n\n/** Inputs the orchestrator hook needs to drive the machine. */\nexport interface ProvingRequest {\n request: CaptureRequest\n /** Optional manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n}\n\n/**\n * The proving state machine (spec lines 141-151). `attest` auto-runs on\n * entry; `readyToFulfill` waits for the user's explicit Complete Order\n * action; `error.isSubmissionError` routes retry to Retry Submission\n * (post-fulfilment) vs Retry Verification (pre-fulfilment) — exactly the\n * live SubmitStep.tsx:931-955 routing.\n */\nexport type ProvingState =\n | { status: 'authenticate' }\n | { status: 'capture' }\n | { status: 'attest' }\n | { status: 'readyToFulfill'; attestation: VerifiedAttestation }\n | { status: 'fulfillIntent'; attestation: VerifiedAttestation }\n | { status: 'confirm'; attestation: VerifiedAttestation }\n | { status: 'sync'; attestation: VerifiedAttestation }\n | { status: 'complete'; attestation: VerifiedAttestation }\n | {\n status: 'error'\n message: string\n /** true ⇒ failure occurred during/after on-chain submission. */\n isSubmissionError: boolean\n attestation?: VerifiedAttestation\n /** Typed reason, set for cases the consumer must branch on. Currently\n * 'enclave-unavailable' (non-migrated provider → caller falls back to\n * Reclaim) and 'manual-selection-required' (auto-match failed, no\n * resolver supplied). Absent for ordinary verification/submission errors. */\n code?: 'enclave-unavailable' | 'manual-selection-required'\n }\n\n/**\n * Thrown by a capture adapter when the provider returned no buyerTee capture\n * (a non-migrated / Reclaim-only provider). The caller (UnifiedBuy) catches\n * this to fall back to its retained Reclaim branch — the package itself does\n * not implement Reclaim (decision (a), spec line 256).\n */\nexport class EnclaveCaptureUnavailable extends Error {\n readonly platform: string\n constructor(platform: string) {\n super(`No enclave (buyerTee) capture available for platform \"${platform}\".`)\n this.name = 'EnclaveCaptureUnavailable'\n this.platform = platform\n }\n}\n\n/**\n * Thrown by a capture adapter when the enclave capture was *attempted* but the\n * extension surfaced a failure reason (an `errorMessage`) instead of a capture\n * — most commonly because the extension could not reach the attestation enclave\n * (a buyer-side network / firewall / privacy- or ad-blocker problem).\n *\n * Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the\n * provider has no enclave capture path at all (non-migrated). This one is an\n * operational failure the user can usually fix and retry. Keeping them separate\n * stops a transient network failure from being misread as \"this provider isn't\n * supported\", and — if the Reclaim fallback is ever re-enabled — from wrongly\n * dumping the user out of the enclave path (this error carries no\n * `enclave-unavailable` code, so it surfaces as an ordinary retryable error).\n *\n * `detail` carries the raw extension `errorMessage` for logs and support;\n * `message` is the user-facing, actionable copy.\n */\nexport class EnclaveCaptureFailed extends Error {\n readonly platform: string\n readonly detail: string\n constructor(platform: string, detail: string) {\n super(\n 'We could not reach the verification service needed to confirm this ' +\n 'payment. A firewall, or a privacy or ad-blocking tool, may be blocking ' +\n 'it — try disabling blockers or switching networks, then verify again.',\n )\n this.name = 'EnclaveCaptureFailed'\n this.platform = platform\n this.detail = detail\n }\n}\n\n/**\n * Thrown by a capture adapter when auto-match failed and no `selectTransaction`\n * resolver was provided (e.g. a headless wallet). Carries the candidates so a\n * UI caller can render the manual selector and retry (spec lines 263-267).\n */\nexport class ManualSelectionRequired extends Error {\n readonly candidates: CaptureCandidate[]\n constructor(candidates: CaptureCandidate[]) {\n super('Manual transaction selection is required but no resolver was provided.')\n this.name = 'ManualSelectionRequired'\n this.candidates = candidates\n }\n}\n","/**\n * @fileoverview useProving — headless orchestrator for the enclave proving\n * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep\n * state machine (ProofPanel) with no presentation. Renders nothing; returns\n * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.\n *\n * Owns: the ProvingState machine, the nullifier double-spend check\n * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing\n * (SubmitStep.tsx:931-955), and the minimum-visible-state timings\n * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,\n * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { Hex } from 'viem'\nimport { extractNullifier, type V3AttestationResponseObject } from '@provex/utils'\nimport { sleep } from '@provex/utils/time'\nimport { EnclaveCaptureUnavailable, ManualSelectionRequired } from './types'\nimport type {\n CaptureAdapter,\n CaptureRequest,\n SelectTransaction,\n ProvingState,\n VerifiedAttestation,\n} from './types'\n\n/**\n * Minimal context object that buildWritableMethod passes to the per-call hooks.\n * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.\n * Only `hash` is consumed by this hook — the rest is opaque context.\n */\ninterface TxCallbackContext {\n chainId: number\n request: unknown\n}\n\n/** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +\n * WriteHookParams from @provex/react but declared here to keep @provex/proving\n * from depending on the React package's built dist (which the test environment\n * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.\n *\n * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call\n * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):\n * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void\n * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void\n */\ninterface FulfillParams {\n intentHash: Hex\n attestation: V3AttestationResponseObject\n verificationData: Hex\n postIntentHookData?: Hex\n onAwaitingSignature?: (info: TxCallbackContext) => void\n onTransactionHash?: (info: TxCallbackContext & { hash: Hex }) => void\n}\n\n/** The structural slice of ProveXClient the orchestrator depends on. Keeping\n * it structural lets tests pass a light fake and avoids importing the class. */\nexport interface ProvingClient {\n /**\n * The chain this rail operates on. Must equal request.chainId — useProving\n * refuses to check the nullifier or submit on any other chain. A nullifier is\n * only meaningful on its intended chain; checking it on (or submitting it to)\n * a different chain is a cross-chain double-spend / replay vector.\n */\n readonly chainId: number\n fulfillIntent: (params: FulfillParams) => Promise<{ hash: Hex; receipt: unknown }>\n checkNullifierUsed: (params: { paymentMethodId: Hex; nullifier: Hex }) => Promise<boolean | null>\n}\n\nexport interface UseProvingOptions {\n client: ProvingClient\n captureAdapter: CaptureAdapter\n request: CaptureRequest\n /** Payment method id for the nullifier registry lookup. */\n paymentMethodId: Hex\n /** Manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n /**\n * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.\n * Receives the raw TxContext — the hook does not inspect the argument.\n */\n onAwaitingSignature?: () => void\n /** Receives the raw tx hash extracted from the TxContext+hash payload. */\n onTransactionHash?: (hash: Hex) => void\n /** Called once when the flow reaches `complete`. */\n onComplete?: (attestation: V3AttestationResponseObject) => void\n}\n\nexport interface UseProvingReturn {\n state: ProvingState\n /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */\n authenticate: () => Promise<void>\n /** Phase 3: submit fulfillIntent → confirm → sync → complete. */\n fulfill: () => Promise<void>\n /** Re-run attestation after a pre-fulfilment error. */\n retryVerification: () => Promise<void>\n /** Re-run submission after a post-fulfilment error. */\n retrySubmission: () => Promise<void>\n}\n\n/** Minimum visible-state pacing from SubmitStep.tsx:404-417 — keep a state\n * on screen at least `floorMs` so the user perceives the step. */\nasync function withMinimumVisible(floorMs: number, started: number): Promise<void> {\n const elapsed = Date.now() - started\n if (elapsed < floorMs) await sleep(floorMs - elapsed)\n}\n\n/**\n * Detect a wallet-rejection error structurally (without importing ProveXError).\n * ProveXClient always re-wraps user rejections as ProveXError with\n * code === 'WALLET_REJECTED' before the error escapes fulfillIntent.\n */\nfunction isWalletRejection(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code: unknown }).code === 'WALLET_REJECTED'\n )\n}\n\nexport function useProving(options: UseProvingOptions): UseProvingReturn {\n const {\n client,\n captureAdapter,\n request,\n paymentMethodId,\n selectTransaction,\n onAwaitingSignature,\n onTransactionHash,\n onComplete,\n } = options\n\n const [state, setState] = useState<ProvingState>({ status: 'authenticate' })\n // Re-entry guard mirroring isRequestingAttestationRef (SubmitStep.tsx:199).\n const inFlightRef = useRef(false)\n // Separate in-flight guard for submit — prevents double on-chain spend from\n // two rapid fulfill() calls before a re-render.\n const submitInFlightRef = useRef(false)\n\n const runCaptureAndAttest = useCallback(async (): Promise<void> => {\n if (inFlightRef.current) return\n inFlightRef.current = true\n try {\n // Chain pinning: the nullifier must only ever be checked on the chain the\n // intent targets. A client wired to any other chain is refused before we\n // touch capture/attest/registry — never verify on the wrong chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n\n setState({ status: 'capture' })\n const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction })\n\n const attestStarted = Date.now()\n setState({ status: 'attest' })\n const verified = await captureAdapter.attest(handle, request)\n // SubmitStep.tsx:414-417: verifying state must be visible for ≥1000ms.\n await withMinimumVisible(1000, attestStarted)\n\n // Nullifier double-spend re-check — ADVISORY. The authoritative guards are\n // (1) the gate's buyerTee re-sign, which now verifies the nullifier is\n // unused and reserves it cross-chain before signing (buyer-tee.ts), and\n // (2) the on-chain contract, which reverts fulfillIntent on a used\n // nullifier. A valid attestation therefore already implies an unused\n // nullifier. So this browser re-check must NOT fail closed on a flaky\n // public RPC (which falsely blocked legitimate buys with \"registry\n // unreachable\"): block ONLY on a positive \"already used\"; pass otherwise.\n const nullifier = extractNullifier(verified.attestation)\n if (nullifier) {\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed === true) {\n throw new Error(\n 'This payment has already been verified. The nullifier has already been used onchain.',\n )\n }\n if (isUsed === null) {\n console.warn(\n '[useProving] nullifier re-check unavailable (browser RPC) — proceeding; the gate already verified and reserved it',\n )\n }\n } else {\n console.warn(\n '[useProving] could not extract nullifier in the browser — proceeding; the gate already verified it',\n )\n }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n const code =\n error instanceof EnclaveCaptureUnavailable\n ? ('enclave-unavailable' as const)\n : error instanceof ManualSelectionRequired\n ? ('manual-selection-required' as const)\n : undefined\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\n code,\n })\n } finally {\n inFlightRef.current = false\n }\n }, [captureAdapter, client, request, paymentMethodId, selectTransaction])\n\n const submit = useCallback(\n async (verified: VerifiedAttestation): Promise<void> => {\n // Re-entry guard: prevent two rapid submit calls from both calling\n // fulfillIntent (double on-chain spend). Mirrors inFlightRef pattern.\n if (submitInFlightRef.current) return\n submitInFlightRef.current = true\n\n const submitStarted = Date.now()\n setState({ status: 'fulfillIntent', attestation: verified })\n try {\n // Chain pinning (re-asserted at submit): the nullifier was verified on\n // request.chainId; it must only ever be submitted to that same chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n await client.fulfillIntent({\n intentHash: request.intent.intentHash,\n attestation: verified.attestation,\n verificationData: verified.verificationData,\n // Wrap consumer callbacks to match the real buildWritableMethod\n // call shapes (ProveXClient.ts ~890, ~903).\n onAwaitingSignature: onAwaitingSignature\n ? (_info: TxCallbackContext) => { onAwaitingSignature() }\n : undefined,\n // buildWritableMethod fires onTransactionHash with { ...ctx, hash }\n // (ProveXClient.ts ~903). Extract hash and forward to the consumer's\n // simpler (hash: Hex) => void API.\n onTransactionHash: onTransactionHash\n ? (info: TxCallbackContext & { hash: Hex }) => {\n // After the tx is broadcast (a separate async tick before the\n // receipt arrives), surface the confirm state so it is\n // observable by consumers.\n setState({ status: 'confirm', attestation: verified })\n onTransactionHash(info.hash)\n }\n : ((_info: TxCallbackContext & { hash: Hex }) => {\n setState({ status: 'confirm', attestation: verified })\n }),\n })\n await withMinimumVisible(500, submitStarted)\n\n // fulfillIntent already awaited receipt + indexer sync internally\n // (ProveXClient.ts:906-915). Surface sync as a real visible state\n // (confirm was set in onTransactionHash above); then await a\n // minimum-visible window before complete so sync is not batched away.\n setState({ status: 'sync', attestation: verified })\n await sleep(600)\n setState({ status: 'complete', attestation: verified })\n onComplete?.(verified.attestation)\n } catch (error) {\n // Wallet rejection: return to readyToFulfill so the user can retry\n // without losing the attestation (mirrors SubmitStep.tsx:571-573).\n if (isWalletRejection(error)) {\n setState({ status: 'readyToFulfill', attestation: verified })\n return\n }\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Submission failed',\n isSubmissionError: true,\n attestation: verified,\n })\n } finally {\n submitInFlightRef.current = false\n }\n },\n [client, request.intent.intentHash, onAwaitingSignature, onTransactionHash, onComplete],\n )\n\n const authenticate = useCallback(() => runCaptureAndAttest(), [runCaptureAndAttest])\n\n const fulfill = useCallback(async () => {\n if (state.status !== 'readyToFulfill') return\n await submit(state.attestation)\n }, [state, submit])\n\n const retryVerification = useCallback(async () => {\n setState({ status: 'authenticate' })\n await runCaptureAndAttest()\n }, [runCaptureAndAttest])\n\n const retrySubmission = useCallback(async () => {\n if (state.status !== 'error' || !state.attestation) return\n await submit(state.attestation)\n }, [state, submit])\n\n return { state, authenticate, fulfill, retryVerification, retrySubmission }\n}\n"]}
|
package/dist/useProving.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'viem';
|
|
2
2
|
import '@provex/utils';
|
|
3
|
-
export { g as ProvingClient, U as UseProvingOptions, h as UseProvingReturn, u as useProving } from './useProving-
|
|
3
|
+
export { g as ProvingClient, U as UseProvingOptions, h as UseProvingReturn, u as useProving } from './useProving-B4UsP7ku.cjs';
|
|
4
4
|
import '@provex/utils/tokens';
|
package/dist/useProving.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'viem';
|
|
2
2
|
import '@provex/utils';
|
|
3
|
-
export { g as ProvingClient, U as UseProvingOptions, h as UseProvingReturn, u as useProving } from './useProving-
|
|
3
|
+
export { g as ProvingClient, U as UseProvingOptions, h as UseProvingReturn, u as useProving } from './useProving-B4UsP7ku.js';
|
|
4
4
|
import '@provex/utils/tokens';
|
package/dist/useProving.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@provex/proving",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0-rc.20261018212258.bdc024c",
|
|
4
4
|
"description": "Headless enclave proving orchestrator for the Provex buy flow",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"test:watch": "vitest"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@provex/utils": "1.
|
|
42
|
+
"@provex/utils": "1.11.0-rc.20261018212258.bdc024c"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/useProving.ts"],"names":[],"mappings":"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","file":"chunk-SQ3PVZQH.js","sourcesContent":["/**\n * @fileoverview Seam contract + state types for the proving orchestrator.\n *\n * `CaptureAdapter` is the swappable capture backend (extension+enclave today,\n * TLSNotary tomorrow). `ProvingState` is the single discriminated-union\n * machine that subsumes the live proof-substep machine (ProofPanel) and the\n * attestation lifecycle (submitStepDerivations). Monetary values stay bigint\n * in base units; the only stringified amount is `ProvingIntent.amount`, which\n * mirrors the existing on-the-wire BuyerTeeIntent shape (buyerTeeAttestation).\n */\n\nimport type { Hex } from 'viem'\nimport type { V3AttestationResponseObject } from '@provex/utils'\nimport type { TokenInfo } from '@provex/utils/tokens'\n\n/** Transaction candidate the adapter surfaces for manual selection. Mirrors\n * the `MetadataMessage` fields the live TransactionSelector consumes; kept\n * structural so the package needs no @provex/extension-react import. */\nexport interface CaptureCandidate {\n amount: number | string\n currency?: string\n date?: string | number | Date\n originalIndex: number\n /**\n * Provider attestation params for this row (zkp2p 0.6.0 spec §4). These ARE\n * the attestation query params the enclave verifies against — they must be\n * forwarded (merged with the metadata index) on the buyerTee proof, not\n * dropped. Optional because the display path doesn't read them.\n */\n params?: Record<string, string | number | boolean>\n}\n\n/** Identity the attestor binds the attestation to. Mirrors the live\n * attestationIntent built in SubmitStep.tsx:333-342 / BuyerTeeIntent. */\nexport interface ProvingIntent {\n intentHash: Hex\n /** bigint base units, stringified (matches BuyerTeeIntent.amount). */\n amount: string\n timestampMs: string\n paymentMethod: Hex\n fiatCurrency: Hex\n conversionRate: string\n /** payeeDetailsHash */\n payeeDetails: Hex\n timestampBufferMs?: string\n}\n\n/** What the orchestrator hands a capture adapter. */\nexport interface CaptureRequest {\n /** Parent provider key, e.g. 'zelle', 'revolut'. */\n provider: string\n /** Extension platform slug, e.g. 'chase'. */\n platform: string\n /** Action type, e.g. 'transfer_zelle'. */\n actionType: string\n /**\n * Platform for the ATTESTATION hop, when it differs from {@link platform}.\n *\n * Capture and attestation use two different namespaces. Capture addresses a\n * provider template (`{platform}/{actionType}.json`), so a Zelle bank is\n * `chase` + `transfer_zelle`. Attestation addresses the enclave's verify\n * route, which for Zelle demands the GENERIC platform plus a BANK-SPECIFIC\n * action type — `zelle` + `transfer_zelle_chase`.\n *\n * Ground truth is zkp2p's own `resolveBuyerTeeAttestationRoute`\n * (@zkp2p/sdk 0.9.0), which throws otherwise:\n * 'Generic Zelle buyer TEE fulfillment requires actionPlatform \"zelle\"\n * and a bank-specific transfer_zelle_* actionType.'\n *\n * Omit for providers where the two namespaces coincide (venmo, cashapp, …);\n * consumers fall back to {@link platform}.\n */\n attestationPlatform?: string\n /**\n * Action type for the ATTESTATION hop, when it differs from\n * {@link actionType}. See {@link attestationPlatform}. Omit when they\n * coincide; consumers fall back to {@link actionType}.\n */\n attestationActionType?: string\n intent: ProvingIntent\n /** UnifiedPaymentVerifier the attestation is re-signed for. */\n verifyingContract: Hex\n chainId: number\n /**\n * Settlement token for this intent (e.g. USDC on the target chain).\n * Used by the adapter to compute the fiat currency amount via\n * convertTokenInputToCurrency so auto-match works correctly for non-1:1 rates.\n */\n token: TokenInfo\n}\n\n/** Opaque per-backend capture state; the orchestrator passes it back unmodified. */\nexport interface CaptureHandle {\n readonly kind: string\n readonly material: unknown\n}\n\n/** Uniform capture output. `verificationData` names the signing witness to\n * the on-chain verifier (enclave signer for buyerTee — SubmitStep.tsx:473-475). */\nexport interface VerifiedAttestation {\n attestation: V3AttestationResponseObject\n verificationData: Hex\n}\n\n/** Resolver the UI provides so the adapter can collect a manual transaction\n * pick when auto-match fails (GenerateProofStep.tsx:391-404). */\nexport type SelectTransaction = (candidates: CaptureCandidate[]) => Promise<number>\n\n/** A pluggable capture backend. One per capture mechanism. */\nexport interface CaptureAdapter {\n /** Stable id for telemetry, e.g. 'extension-enclave'. */\n readonly kind: string\n /** Phase 1: authenticate + capture, returning opaque material to attest. */\n authenticateAndCapture(\n request: CaptureRequest,\n options?: { selectTransaction?: SelectTransaction },\n ): Promise<CaptureHandle>\n /** Phase 2: produce a verified attestation from the captured material. */\n attest(handle: CaptureHandle, request: CaptureRequest): Promise<VerifiedAttestation>\n}\n\n/** Inputs the orchestrator hook needs to drive the machine. */\nexport interface ProvingRequest {\n request: CaptureRequest\n /** Optional manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n}\n\n/**\n * The proving state machine (spec lines 141-151). `attest` auto-runs on\n * entry; `readyToFulfill` waits for the user's explicit Complete Order\n * action; `error.isSubmissionError` routes retry to Retry Submission\n * (post-fulfilment) vs Retry Verification (pre-fulfilment) — exactly the\n * live SubmitStep.tsx:931-955 routing.\n */\nexport type ProvingState =\n | { status: 'authenticate' }\n | { status: 'capture' }\n | { status: 'attest' }\n | { status: 'readyToFulfill'; attestation: VerifiedAttestation }\n | { status: 'fulfillIntent'; attestation: VerifiedAttestation }\n | { status: 'confirm'; attestation: VerifiedAttestation }\n | { status: 'sync'; attestation: VerifiedAttestation }\n | { status: 'complete'; attestation: VerifiedAttestation }\n | {\n status: 'error'\n message: string\n /** true ⇒ failure occurred during/after on-chain submission. */\n isSubmissionError: boolean\n attestation?: VerifiedAttestation\n /** Typed reason, set for cases the consumer must branch on. Currently\n * 'enclave-unavailable' (non-migrated provider → caller falls back to\n * Reclaim) and 'manual-selection-required' (auto-match failed, no\n * resolver supplied). Absent for ordinary verification/submission errors. */\n code?: 'enclave-unavailable' | 'manual-selection-required'\n }\n\n/**\n * Thrown by a capture adapter when the provider returned no buyerTee capture\n * (a non-migrated / Reclaim-only provider). The caller (UnifiedBuy) catches\n * this to fall back to its retained Reclaim branch — the package itself does\n * not implement Reclaim (decision (a), spec line 256).\n */\nexport class EnclaveCaptureUnavailable extends Error {\n readonly platform: string\n constructor(platform: string) {\n super(`No enclave (buyerTee) capture available for platform \"${platform}\".`)\n this.name = 'EnclaveCaptureUnavailable'\n this.platform = platform\n }\n}\n\n/**\n * Thrown by a capture adapter when the enclave capture was *attempted* but the\n * extension surfaced a failure reason (an `errorMessage`) instead of a capture\n * — most commonly because the extension could not reach the attestation enclave\n * (a buyer-side network / firewall / privacy- or ad-blocker problem).\n *\n * Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the\n * provider has no enclave capture path at all (non-migrated). This one is an\n * operational failure the user can usually fix and retry. Keeping them separate\n * stops a transient network failure from being misread as \"this provider isn't\n * supported\", and — if the Reclaim fallback is ever re-enabled — from wrongly\n * dumping the user out of the enclave path (this error carries no\n * `enclave-unavailable` code, so it surfaces as an ordinary retryable error).\n *\n * `detail` carries the raw extension `errorMessage` for logs and support;\n * `message` is the user-facing, actionable copy.\n */\nexport class EnclaveCaptureFailed extends Error {\n readonly platform: string\n readonly detail: string\n constructor(platform: string, detail: string) {\n super(\n 'We could not reach the verification service needed to confirm this ' +\n 'payment. A firewall, or a privacy or ad-blocking tool, may be blocking ' +\n 'it — try disabling blockers or switching networks, then verify again.',\n )\n this.name = 'EnclaveCaptureFailed'\n this.platform = platform\n this.detail = detail\n }\n}\n\n/**\n * Thrown by a capture adapter when auto-match failed and no `selectTransaction`\n * resolver was provided (e.g. a headless wallet). Carries the candidates so a\n * UI caller can render the manual selector and retry (spec lines 263-267).\n */\nexport class ManualSelectionRequired extends Error {\n readonly candidates: CaptureCandidate[]\n constructor(candidates: CaptureCandidate[]) {\n super('Manual transaction selection is required but no resolver was provided.')\n this.name = 'ManualSelectionRequired'\n this.candidates = candidates\n }\n}\n","/**\n * @fileoverview useProving — headless orchestrator for the enclave proving\n * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep\n * state machine (ProofPanel) with no presentation. Renders nothing; returns\n * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.\n *\n * Owns: the ProvingState machine, the nullifier double-spend check\n * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing\n * (SubmitStep.tsx:931-955), and the minimum-visible-state timings\n * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,\n * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { Hex } from 'viem'\nimport { extractNullifier, type V3AttestationResponseObject } from '@provex/utils'\nimport { sleep } from '@provex/utils/time'\nimport { EnclaveCaptureUnavailable, ManualSelectionRequired } from './types'\nimport type {\n CaptureAdapter,\n CaptureRequest,\n SelectTransaction,\n ProvingState,\n VerifiedAttestation,\n} from './types'\n\n/**\n * Minimal context object that buildWritableMethod passes to the per-call hooks.\n * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.\n * Only `hash` is consumed by this hook — the rest is opaque context.\n */\ninterface TxCallbackContext {\n chainId: number\n request: unknown\n}\n\n/** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +\n * WriteHookParams from @provex/react but declared here to keep @provex/proving\n * from depending on the React package's built dist (which the test environment\n * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.\n *\n * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call\n * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):\n * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void\n * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void\n */\ninterface FulfillParams {\n intentHash: Hex\n attestation: V3AttestationResponseObject\n verificationData: Hex\n postIntentHookData?: Hex\n onAwaitingSignature?: (info: TxCallbackContext) => void\n onTransactionHash?: (info: TxCallbackContext & { hash: Hex }) => void\n}\n\n/** The structural slice of ProveXClient the orchestrator depends on. Keeping\n * it structural lets tests pass a light fake and avoids importing the class. */\nexport interface ProvingClient {\n /**\n * The chain this rail operates on. Must equal request.chainId — useProving\n * refuses to check the nullifier or submit on any other chain. A nullifier is\n * only meaningful on its intended chain; checking it on (or submitting it to)\n * a different chain is a cross-chain double-spend / replay vector.\n */\n readonly chainId: number\n fulfillIntent: (params: FulfillParams) => Promise<{ hash: Hex; receipt: unknown }>\n checkNullifierUsed: (params: { paymentMethodId: Hex; nullifier: Hex }) => Promise<boolean | null>\n}\n\nexport interface UseProvingOptions {\n client: ProvingClient\n captureAdapter: CaptureAdapter\n request: CaptureRequest\n /** Payment method id for the nullifier registry lookup. */\n paymentMethodId: Hex\n /** Manual-select resolver forwarded to the adapter. */\n selectTransaction?: SelectTransaction\n /**\n * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.\n * Receives the raw TxContext — the hook does not inspect the argument.\n */\n onAwaitingSignature?: () => void\n /** Receives the raw tx hash extracted from the TxContext+hash payload. */\n onTransactionHash?: (hash: Hex) => void\n /** Called once when the flow reaches `complete`. */\n onComplete?: (attestation: V3AttestationResponseObject) => void\n}\n\nexport interface UseProvingReturn {\n state: ProvingState\n /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */\n authenticate: () => Promise<void>\n /** Phase 3: submit fulfillIntent → confirm → sync → complete. */\n fulfill: () => Promise<void>\n /** Re-run attestation after a pre-fulfilment error. */\n retryVerification: () => Promise<void>\n /** Re-run submission after a post-fulfilment error. */\n retrySubmission: () => Promise<void>\n}\n\n/** Minimum visible-state pacing from SubmitStep.tsx:404-417 — keep a state\n * on screen at least `floorMs` so the user perceives the step. */\nasync function withMinimumVisible(floorMs: number, started: number): Promise<void> {\n const elapsed = Date.now() - started\n if (elapsed < floorMs) await sleep(floorMs - elapsed)\n}\n\n/**\n * Detect a wallet-rejection error structurally (without importing ProveXError).\n * ProveXClient always re-wraps user rejections as ProveXError with\n * code === 'WALLET_REJECTED' before the error escapes fulfillIntent.\n */\nfunction isWalletRejection(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { code: unknown }).code === 'WALLET_REJECTED'\n )\n}\n\nexport function useProving(options: UseProvingOptions): UseProvingReturn {\n const {\n client,\n captureAdapter,\n request,\n paymentMethodId,\n selectTransaction,\n onAwaitingSignature,\n onTransactionHash,\n onComplete,\n } = options\n\n const [state, setState] = useState<ProvingState>({ status: 'authenticate' })\n // Re-entry guard mirroring isRequestingAttestationRef (SubmitStep.tsx:199).\n const inFlightRef = useRef(false)\n // Separate in-flight guard for submit — prevents double on-chain spend from\n // two rapid fulfill() calls before a re-render.\n const submitInFlightRef = useRef(false)\n\n const runCaptureAndAttest = useCallback(async (): Promise<void> => {\n if (inFlightRef.current) return\n inFlightRef.current = true\n try {\n // Chain pinning: the nullifier must only ever be checked on the chain the\n // intent targets. A client wired to any other chain is refused before we\n // touch capture/attest/registry — never verify on the wrong chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n\n setState({ status: 'capture' })\n const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction })\n\n const attestStarted = Date.now()\n setState({ status: 'attest' })\n const verified = await captureAdapter.attest(handle, request)\n // SubmitStep.tsx:414-417: verifying state must be visible for ≥1000ms.\n await withMinimumVisible(1000, attestStarted)\n\n // Nullifier double-spend re-check — ADVISORY. The authoritative guards are\n // (1) the gate's buyerTee re-sign, which now verifies the nullifier is\n // unused and reserves it cross-chain before signing (buyer-tee.ts), and\n // (2) the on-chain contract, which reverts fulfillIntent on a used\n // nullifier. A valid attestation therefore already implies an unused\n // nullifier. So this browser re-check must NOT fail closed on a flaky\n // public RPC (which falsely blocked legitimate buys with \"registry\n // unreachable\"): block ONLY on a positive \"already used\"; pass otherwise.\n const nullifier = extractNullifier(verified.attestation)\n if (nullifier) {\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed === true) {\n throw new Error(\n 'This payment has already been verified. The nullifier has already been used onchain.',\n )\n }\n if (isUsed === null) {\n console.warn(\n '[useProving] nullifier re-check unavailable (browser RPC) — proceeding; the gate already verified and reserved it',\n )\n }\n } else {\n console.warn(\n '[useProving] could not extract nullifier in the browser — proceeding; the gate already verified it',\n )\n }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n const code =\n error instanceof EnclaveCaptureUnavailable\n ? ('enclave-unavailable' as const)\n : error instanceof ManualSelectionRequired\n ? ('manual-selection-required' as const)\n : undefined\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\n code,\n })\n } finally {\n inFlightRef.current = false\n }\n }, [captureAdapter, client, request, paymentMethodId, selectTransaction])\n\n const submit = useCallback(\n async (verified: VerifiedAttestation): Promise<void> => {\n // Re-entry guard: prevent two rapid submit calls from both calling\n // fulfillIntent (double on-chain spend). Mirrors inFlightRef pattern.\n if (submitInFlightRef.current) return\n submitInFlightRef.current = true\n\n const submitStarted = Date.now()\n setState({ status: 'fulfillIntent', attestation: verified })\n try {\n // Chain pinning (re-asserted at submit): the nullifier was verified on\n // request.chainId; it must only ever be submitted to that same chain.\n if (client.chainId !== request.chainId) {\n throw new Error(\n `Refusing to proceed: proving client is on chain ${client.chainId} but the intent targets chain ${request.chainId}. A nullifier must only be verified and fulfilled on its intended chain.`,\n )\n }\n await client.fulfillIntent({\n intentHash: request.intent.intentHash,\n attestation: verified.attestation,\n verificationData: verified.verificationData,\n // Wrap consumer callbacks to match the real buildWritableMethod\n // call shapes (ProveXClient.ts ~890, ~903).\n onAwaitingSignature: onAwaitingSignature\n ? (_info: TxCallbackContext) => { onAwaitingSignature() }\n : undefined,\n // buildWritableMethod fires onTransactionHash with { ...ctx, hash }\n // (ProveXClient.ts ~903). Extract hash and forward to the consumer's\n // simpler (hash: Hex) => void API.\n onTransactionHash: onTransactionHash\n ? (info: TxCallbackContext & { hash: Hex }) => {\n // After the tx is broadcast (a separate async tick before the\n // receipt arrives), surface the confirm state so it is\n // observable by consumers.\n setState({ status: 'confirm', attestation: verified })\n onTransactionHash(info.hash)\n }\n : ((_info: TxCallbackContext & { hash: Hex }) => {\n setState({ status: 'confirm', attestation: verified })\n }),\n })\n await withMinimumVisible(500, submitStarted)\n\n // fulfillIntent already awaited receipt + indexer sync internally\n // (ProveXClient.ts:906-915). Surface sync as a real visible state\n // (confirm was set in onTransactionHash above); then await a\n // minimum-visible window before complete so sync is not batched away.\n setState({ status: 'sync', attestation: verified })\n await sleep(600)\n setState({ status: 'complete', attestation: verified })\n onComplete?.(verified.attestation)\n } catch (error) {\n // Wallet rejection: return to readyToFulfill so the user can retry\n // without losing the attestation (mirrors SubmitStep.tsx:571-573).\n if (isWalletRejection(error)) {\n setState({ status: 'readyToFulfill', attestation: verified })\n return\n }\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Submission failed',\n isSubmissionError: true,\n attestation: verified,\n })\n } finally {\n submitInFlightRef.current = false\n }\n },\n [client, request.intent.intentHash, onAwaitingSignature, onTransactionHash, onComplete],\n )\n\n const authenticate = useCallback(() => runCaptureAndAttest(), [runCaptureAndAttest])\n\n const fulfill = useCallback(async () => {\n if (state.status !== 'readyToFulfill') return\n await submit(state.attestation)\n }, [state, submit])\n\n const retryVerification = useCallback(async () => {\n setState({ status: 'authenticate' })\n await runCaptureAndAttest()\n }, [runCaptureAndAttest])\n\n const retrySubmission = useCallback(async () => {\n if (state.status !== 'error' || !state.attestation) return\n await submit(state.attestation)\n }, [state, submit])\n\n return { state, authenticate, fulfill, retryVerification, retrySubmission }\n}\n"]}
|