@provex/proving 1.9.1 → 1.9.2-rc.20260701200216.78137f4
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-P74DHNBB.js → chunk-NWQHGMZR.js} +15 -3
- package/dist/chunk-NWQHGMZR.js.map +1 -0
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{useProving-CS_B8rOD.d.cts → useProving-Cyr8Ftqy.d.cts} +23 -1
- package/dist/{useProving-CS_B8rOD.d.ts → useProving-Cyr8Ftqy.d.ts} +23 -1
- 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-P74DHNBB.js.map +0 -1
|
@@ -13,6 +13,18 @@ var EnclaveCaptureUnavailable = class extends Error {
|
|
|
13
13
|
this.platform = platform;
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var EnclaveCaptureFailed = class extends Error {
|
|
17
|
+
platform;
|
|
18
|
+
detail;
|
|
19
|
+
constructor(platform, detail) {
|
|
20
|
+
super(
|
|
21
|
+
"We could not reach the verification service needed to confirm this payment. A firewall, or a privacy or ad-blocking tool, may be blocking it \u2014 try disabling blockers or switching networks, then verify again."
|
|
22
|
+
);
|
|
23
|
+
this.name = "EnclaveCaptureFailed";
|
|
24
|
+
this.platform = platform;
|
|
25
|
+
this.detail = detail;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
16
28
|
var ManualSelectionRequired = class extends Error {
|
|
17
29
|
candidates;
|
|
18
30
|
constructor(candidates) {
|
|
@@ -159,6 +171,6 @@ function useProving(options) {
|
|
|
159
171
|
return { state, authenticate, fulfill, retryVerification, retrySubmission };
|
|
160
172
|
}
|
|
161
173
|
|
|
162
|
-
export { EnclaveCaptureUnavailable, ManualSelectionRequired, useProving };
|
|
163
|
-
//# sourceMappingURL=chunk-
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
174
|
+
export { EnclaveCaptureFailed, EnclaveCaptureUnavailable, ManualSelectionRequired, useProving };
|
|
175
|
+
//# sourceMappingURL=chunk-NWQHGMZR.js.map
|
|
176
|
+
//# sourceMappingURL=chunk-NWQHGMZR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/useProving.ts"],"names":[],"mappings":"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","file":"chunk-NWQHGMZR.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 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
|
@@ -51,6 +51,18 @@ var EnclaveCaptureUnavailable = class extends Error {
|
|
|
51
51
|
this.platform = platform;
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
+
var EnclaveCaptureFailed = class extends Error {
|
|
55
|
+
platform;
|
|
56
|
+
detail;
|
|
57
|
+
constructor(platform, detail) {
|
|
58
|
+
super(
|
|
59
|
+
"We could not reach the verification service needed to confirm this payment. A firewall, or a privacy or ad-blocking tool, may be blocking it \u2014 try disabling blockers or switching networks, then verify again."
|
|
60
|
+
);
|
|
61
|
+
this.name = "EnclaveCaptureFailed";
|
|
62
|
+
this.platform = platform;
|
|
63
|
+
this.detail = detail;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
54
66
|
var ManualSelectionRequired = class extends Error {
|
|
55
67
|
candidates;
|
|
56
68
|
constructor(candidates) {
|
|
@@ -274,6 +286,7 @@ function buildCaptureRequest(input) {
|
|
|
274
286
|
};
|
|
275
287
|
}
|
|
276
288
|
|
|
289
|
+
exports.EnclaveCaptureFailed = EnclaveCaptureFailed;
|
|
277
290
|
exports.EnclaveCaptureUnavailable = EnclaveCaptureUnavailable;
|
|
278
291
|
exports.ManualSelectionRequired = ManualSelectionRequired;
|
|
279
292
|
exports.asBuyerTeeProof = asBuyerTeeProof;
|
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 /** Extension platform / bank key, e.g. `revolut`, `chase`. */\n platform: string\n /** Action type, e.g. `transfer_revolut`, `transfer_zelle`. */\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 * 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}): ${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 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 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 * 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 parentProvider = input.providerKey.includes('-')\n ? input.providerKey.split('-')[0]\n : input.providerKey\n\n const platform = getExtensionPlatform(input.subProvider ?? input.providerKey)\n\n return {\n provider: input.providerKey,\n platform,\n actionType: `transfer_${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 /** Extension platform / bank key, e.g. `revolut`, `chase`. */\n platform: string\n /** Action type, e.g. `transfer_revolut`, `transfer_zelle`. */\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 * 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}): ${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 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 * 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 parentProvider = input.providerKey.includes('-')\n ? input.providerKey.split('-')[0]\n : input.providerKey\n\n const platform = getExtensionPlatform(input.subProvider ?? input.providerKey)\n\n return {\n provider: input.providerKey,\n platform,\n actionType: `transfer_${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 {
|
|
3
|
+
import { S as SelectTransaction, C as CaptureCandidate, a as CaptureRequest } from './useProving-Cyr8Ftqy.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-Cyr8Ftqy.cjs';
|
|
5
5
|
import { TokenInfo } from '@provex/utils/tokens';
|
|
6
6
|
import { SubProviderKey, ProviderKey } from '@provex/utils/payment';
|
|
7
7
|
|
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 {
|
|
3
|
+
import { S as SelectTransaction, C as CaptureCandidate, a as CaptureRequest } from './useProving-Cyr8Ftqy.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-Cyr8Ftqy.js';
|
|
5
5
|
import { TokenInfo } from '@provex/utils/tokens';
|
|
6
6
|
import { SubProviderKey, ProviderKey } from '@provex/utils/payment';
|
|
7
7
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { EnclaveCaptureUnavailable, ManualSelectionRequired, useProving } from './chunk-
|
|
1
|
+
export { EnclaveCaptureFailed, EnclaveCaptureUnavailable, ManualSelectionRequired, useProving } from './chunk-NWQHGMZR.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';
|
|
@@ -143,6 +143,28 @@ declare class EnclaveCaptureUnavailable extends Error {
|
|
|
143
143
|
readonly platform: string;
|
|
144
144
|
constructor(platform: string);
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Thrown by a capture adapter when the enclave capture was *attempted* but the
|
|
148
|
+
* extension surfaced a failure reason (an `errorMessage`) instead of a capture
|
|
149
|
+
* — most commonly because the extension could not reach the attestation enclave
|
|
150
|
+
* (a buyer-side network / firewall / privacy- or ad-blocker problem).
|
|
151
|
+
*
|
|
152
|
+
* Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the
|
|
153
|
+
* provider has no enclave capture path at all (non-migrated). This one is an
|
|
154
|
+
* operational failure the user can usually fix and retry. Keeping them separate
|
|
155
|
+
* stops a transient network failure from being misread as "this provider isn't
|
|
156
|
+
* supported", and — if the Reclaim fallback is ever re-enabled — from wrongly
|
|
157
|
+
* dumping the user out of the enclave path (this error carries no
|
|
158
|
+
* `enclave-unavailable` code, so it surfaces as an ordinary retryable error).
|
|
159
|
+
*
|
|
160
|
+
* `detail` carries the raw extension `errorMessage` for logs and support;
|
|
161
|
+
* `message` is the user-facing, actionable copy.
|
|
162
|
+
*/
|
|
163
|
+
declare class EnclaveCaptureFailed extends Error {
|
|
164
|
+
readonly platform: string;
|
|
165
|
+
readonly detail: string;
|
|
166
|
+
constructor(platform: string, detail: string);
|
|
167
|
+
}
|
|
146
168
|
/**
|
|
147
169
|
* Thrown by a capture adapter when auto-match failed and no `selectTransaction`
|
|
148
170
|
* resolver was provided (e.g. a headless wallet). Carries the candidates so a
|
|
@@ -245,4 +267,4 @@ interface UseProvingReturn {
|
|
|
245
267
|
}
|
|
246
268
|
declare function useProving(options: UseProvingOptions): UseProvingReturn;
|
|
247
269
|
|
|
248
|
-
export { type CaptureCandidate as C, EnclaveCaptureUnavailable as E, ManualSelectionRequired as M, type ProvingIntent as P, type SelectTransaction as S, type UseProvingOptions as U, type VerifiedAttestation as V, type CaptureRequest as a, type CaptureHandle as
|
|
270
|
+
export { type CaptureCandidate as C, EnclaveCaptureUnavailable as E, ManualSelectionRequired as M, type ProvingIntent as P, type SelectTransaction as S, type UseProvingOptions as U, type VerifiedAttestation as V, type CaptureRequest as a, EnclaveCaptureFailed as b, type CaptureHandle as c, type CaptureAdapter as d, type ProvingRequest as e, type ProvingState as f, type ProvingClient as g, type UseProvingReturn as h, useProving as u };
|
|
@@ -143,6 +143,28 @@ declare class EnclaveCaptureUnavailable extends Error {
|
|
|
143
143
|
readonly platform: string;
|
|
144
144
|
constructor(platform: string);
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Thrown by a capture adapter when the enclave capture was *attempted* but the
|
|
148
|
+
* extension surfaced a failure reason (an `errorMessage`) instead of a capture
|
|
149
|
+
* — most commonly because the extension could not reach the attestation enclave
|
|
150
|
+
* (a buyer-side network / firewall / privacy- or ad-blocker problem).
|
|
151
|
+
*
|
|
152
|
+
* Deliberately distinct from {@link EnclaveCaptureUnavailable}, which means the
|
|
153
|
+
* provider has no enclave capture path at all (non-migrated). This one is an
|
|
154
|
+
* operational failure the user can usually fix and retry. Keeping them separate
|
|
155
|
+
* stops a transient network failure from being misread as "this provider isn't
|
|
156
|
+
* supported", and — if the Reclaim fallback is ever re-enabled — from wrongly
|
|
157
|
+
* dumping the user out of the enclave path (this error carries no
|
|
158
|
+
* `enclave-unavailable` code, so it surfaces as an ordinary retryable error).
|
|
159
|
+
*
|
|
160
|
+
* `detail` carries the raw extension `errorMessage` for logs and support;
|
|
161
|
+
* `message` is the user-facing, actionable copy.
|
|
162
|
+
*/
|
|
163
|
+
declare class EnclaveCaptureFailed extends Error {
|
|
164
|
+
readonly platform: string;
|
|
165
|
+
readonly detail: string;
|
|
166
|
+
constructor(platform: string, detail: string);
|
|
167
|
+
}
|
|
146
168
|
/**
|
|
147
169
|
* Thrown by a capture adapter when auto-match failed and no `selectTransaction`
|
|
148
170
|
* resolver was provided (e.g. a headless wallet). Carries the candidates so a
|
|
@@ -245,4 +267,4 @@ interface UseProvingReturn {
|
|
|
245
267
|
}
|
|
246
268
|
declare function useProving(options: UseProvingOptions): UseProvingReturn;
|
|
247
269
|
|
|
248
|
-
export { type CaptureCandidate as C, EnclaveCaptureUnavailable as E, ManualSelectionRequired as M, type ProvingIntent as P, type SelectTransaction as S, type UseProvingOptions as U, type VerifiedAttestation as V, type CaptureRequest as a, type CaptureHandle as
|
|
270
|
+
export { type CaptureCandidate as C, EnclaveCaptureUnavailable as E, ManualSelectionRequired as M, type ProvingIntent as P, type SelectTransaction as S, type UseProvingOptions as U, type VerifiedAttestation as V, type CaptureRequest as a, EnclaveCaptureFailed as b, type CaptureHandle as c, type CaptureAdapter as d, type ProvingRequest as e, type ProvingState as f, type ProvingClient as g, type UseProvingReturn as h, useProving as u };
|
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 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 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":"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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 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 {
|
|
3
|
+
export { g as ProvingClient, U as UseProvingOptions, h as UseProvingReturn, u as useProving } from './useProving-Cyr8Ftqy.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 {
|
|
3
|
+
export { g as ProvingClient, U as UseProvingOptions, h as UseProvingReturn, u as useProving } from './useProving-Cyr8Ftqy.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.9.
|
|
3
|
+
"version": "1.9.2-rc.20260701200216.78137f4",
|
|
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.9.
|
|
42
|
+
"@provex/utils": "1.9.2-rc.20260701200216.78137f4"
|
|
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-P74DHNBB.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 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 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"]}
|