@provex/proving 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -120,6 +120,11 @@ type ProvingState = {
120
120
  /** true ⇒ failure occurred during/after on-chain submission. */
121
121
  isSubmissionError: boolean;
122
122
  attestation?: VerifiedAttestation;
123
+ /** Typed reason, set for cases the consumer must branch on. Currently
124
+ * 'enclave-unavailable' (non-migrated provider → caller falls back to
125
+ * Reclaim) and 'manual-selection-required' (auto-match failed, no
126
+ * resolver supplied). Absent for ordinary verification/submission errors. */
127
+ code?: 'enclave-unavailable' | 'manual-selection-required';
123
128
  };
124
129
  /**
125
130
  * Thrown by a capture adapter when the provider returned no buyerTee capture
@@ -141,4 +146,96 @@ declare class ManualSelectionRequired extends Error {
141
146
  constructor(candidates: CaptureCandidate[]);
142
147
  }
143
148
 
144
- export { type CaptureCandidate as C, EnclaveCaptureUnavailable as E, ManualSelectionRequired as M, type ProvingIntent as P, type SelectTransaction as S, type VerifiedAttestation as V, type CaptureRequest as a, type CaptureHandle as b, type CaptureAdapter as c, type ProvingRequest as d, type ProvingState as e };
149
+ /**
150
+ * @fileoverview useProving — headless orchestrator for the enclave proving
151
+ * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep
152
+ * state machine (ProofPanel) with no presentation. Renders nothing; returns
153
+ * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.
154
+ *
155
+ * Owns: the ProvingState machine, the nullifier double-spend check
156
+ * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing
157
+ * (SubmitStep.tsx:931-955), and the minimum-visible-state timings
158
+ * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,
159
+ * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.
160
+ */
161
+
162
+ /**
163
+ * Minimal context object that buildWritableMethod passes to the per-call hooks.
164
+ * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.
165
+ * Only `hash` is consumed by this hook — the rest is opaque context.
166
+ */
167
+ interface TxCallbackContext {
168
+ chainId: number;
169
+ request: unknown;
170
+ }
171
+ /** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +
172
+ * WriteHookParams from @provex/react but declared here to keep @provex/proving
173
+ * from depending on the React package's built dist (which the test environment
174
+ * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.
175
+ *
176
+ * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call
177
+ * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):
178
+ * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void
179
+ * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void
180
+ */
181
+ interface FulfillParams {
182
+ intentHash: Hex;
183
+ attestation: V3AttestationResponseObject;
184
+ verificationData: Hex;
185
+ postIntentHookData?: Hex;
186
+ onAwaitingSignature?: (info: TxCallbackContext) => void;
187
+ onTransactionHash?: (info: TxCallbackContext & {
188
+ hash: Hex;
189
+ }) => void;
190
+ }
191
+ /** The structural slice of ProveXClient the orchestrator depends on. Keeping
192
+ * it structural lets tests pass a light fake and avoids importing the class. */
193
+ interface ProvingClient {
194
+ /**
195
+ * The chain this rail operates on. Must equal request.chainId — useProving
196
+ * refuses to check the nullifier or submit on any other chain. A nullifier is
197
+ * only meaningful on its intended chain; checking it on (or submitting it to)
198
+ * a different chain is a cross-chain double-spend / replay vector.
199
+ */
200
+ readonly chainId: number;
201
+ fulfillIntent: (params: FulfillParams) => Promise<{
202
+ hash: Hex;
203
+ receipt: unknown;
204
+ }>;
205
+ checkNullifierUsed: (params: {
206
+ paymentMethodId: Hex;
207
+ nullifier: Hex;
208
+ }) => Promise<boolean | null>;
209
+ }
210
+ interface UseProvingOptions {
211
+ client: ProvingClient;
212
+ captureAdapter: CaptureAdapter;
213
+ request: CaptureRequest;
214
+ /** Payment method id for the nullifier registry lookup. */
215
+ paymentMethodId: Hex;
216
+ /** Manual-select resolver forwarded to the adapter. */
217
+ selectTransaction?: SelectTransaction;
218
+ /**
219
+ * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.
220
+ * Receives the raw TxContext — the hook does not inspect the argument.
221
+ */
222
+ onAwaitingSignature?: () => void;
223
+ /** Receives the raw tx hash extracted from the TxContext+hash payload. */
224
+ onTransactionHash?: (hash: Hex) => void;
225
+ /** Called once when the flow reaches `complete`. */
226
+ onComplete?: (attestation: V3AttestationResponseObject) => void;
227
+ }
228
+ interface UseProvingReturn {
229
+ state: ProvingState;
230
+ /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */
231
+ authenticate: () => Promise<void>;
232
+ /** Phase 3: submit fulfillIntent → confirm → sync → complete. */
233
+ fulfill: () => Promise<void>;
234
+ /** Re-run attestation after a pre-fulfilment error. */
235
+ retryVerification: () => Promise<void>;
236
+ /** Re-run submission after a post-fulfilment error. */
237
+ retrySubmission: () => Promise<void>;
238
+ }
239
+ declare function useProving(options: UseProvingOptions): UseProvingReturn;
240
+
241
+ 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 b, type CaptureAdapter as c, type ProvingRequest as d, type ProvingState as e, type ProvingClient as f, type UseProvingReturn as g, useProving as u };
@@ -4,6 +4,26 @@ var react = require('react');
4
4
  var utils = require('@provex/utils');
5
5
  var time = require('@provex/utils/time');
6
6
 
7
+ // src/useProving.ts
8
+
9
+ // src/types.ts
10
+ var EnclaveCaptureUnavailable = class extends Error {
11
+ platform;
12
+ constructor(platform) {
13
+ super(`No enclave (buyerTee) capture available for platform "${platform}".`);
14
+ this.name = "EnclaveCaptureUnavailable";
15
+ this.platform = platform;
16
+ }
17
+ };
18
+ var ManualSelectionRequired = class extends Error {
19
+ candidates;
20
+ constructor(candidates) {
21
+ super("Manual transaction selection is required but no resolver was provided.");
22
+ this.name = "ManualSelectionRequired";
23
+ this.candidates = candidates;
24
+ }
25
+ };
26
+
7
27
  // src/useProving.ts
8
28
  async function withMinimumVisible(floorMs, started) {
9
29
  const elapsed = Date.now() - started;
@@ -30,6 +50,11 @@ function useProving(options) {
30
50
  if (inFlightRef.current) return;
31
51
  inFlightRef.current = true;
32
52
  try {
53
+ if (client.chainId !== request.chainId) {
54
+ throw new Error(
55
+ `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.`
56
+ );
57
+ }
33
58
  setState({ status: "capture" });
34
59
  const handle = await captureAdapter.authenticateAndCapture(request, { selectTransaction });
35
60
  const attestStarted = Date.now();
@@ -37,20 +62,25 @@ function useProving(options) {
37
62
  const verified = await captureAdapter.attest(handle, request);
38
63
  await withMinimumVisible(1e3, attestStarted);
39
64
  const nullifier = utils.extractNullifier(verified.attestation);
40
- if (nullifier) {
41
- const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier });
42
- if (isUsed === true) {
43
- throw new Error(
44
- "This payment has already been verified. The nullifier has already been used onchain."
45
- );
46
- }
65
+ if (!nullifier) {
66
+ throw new Error(
67
+ "Could not extract a nullifier from the attestation; cannot confirm it is unused. Refusing to proceed."
68
+ );
69
+ }
70
+ const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier });
71
+ if (isUsed !== false) {
72
+ throw new Error(
73
+ isUsed === true ? "This payment has already been verified. The nullifier has already been used onchain." : "Could not verify the nullifier is unused (registry unreachable). Refusing to proceed \u2014 please retry."
74
+ );
47
75
  }
48
76
  setState({ status: "readyToFulfill", attestation: verified });
49
77
  } catch (error) {
78
+ const code = error instanceof EnclaveCaptureUnavailable ? "enclave-unavailable" : error instanceof ManualSelectionRequired ? "manual-selection-required" : void 0;
50
79
  setState({
51
80
  status: "error",
52
81
  message: error instanceof Error ? error.message : "Verification failed",
53
- isSubmissionError: false
82
+ isSubmissionError: false,
83
+ code
54
84
  });
55
85
  } finally {
56
86
  inFlightRef.current = false;
@@ -63,6 +93,11 @@ function useProving(options) {
63
93
  const submitStarted = Date.now();
64
94
  setState({ status: "fulfillIntent", attestation: verified });
65
95
  try {
96
+ if (client.chainId !== request.chainId) {
97
+ throw new Error(
98
+ `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.`
99
+ );
100
+ }
66
101
  await client.fulfillIntent({
67
102
  intentHash: request.intent.intentHash,
68
103
  attestation: verified.attestation,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useProving.ts"],"names":["sleep","useState","useRef","useCallback","extractNullifier"],"mappings":";;;;;;;AA8FA,eAAe,kBAAA,CAAmB,SAAiB,OAAA,EAAgC;AACjF,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI,GAAI,OAAA;AAC7B,EAAA,IAAI,OAAA,GAAU,OAAA,EAAS,MAAMA,UAAA,CAAM,UAAU,OAAO,CAAA;AACtD;AAOA,SAAS,kBAAkB,KAAA,EAAyB;AAClD,EAAA,OACE,OAAO,UAAU,QAAA,IACjB,KAAA,KAAU,QACV,MAAA,IAAU,KAAA,IACT,MAA4B,IAAA,KAAS,iBAAA;AAE1C;AAEO,SAAS,WAAW,OAAA,EAA8C;AACvE,EAAA,MAAM;AAAA,IACJ,MAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA;AAAA,IACA,iBAAA;AAAA,IACA,mBAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAEJ,EAAA,MAAM,CAAC,OAAO,QAAQ,CAAA,GAAIC,eAAuB,EAAE,MAAA,EAAQ,gBAAgB,CAAA;AAE3E,EAAA,MAAM,WAAA,GAAcC,aAAO,KAAK,CAAA;AAGhC,EAAA,MAAM,iBAAA,GAAoBA,aAAO,KAAK,CAAA;AAEtC,EAAA,MAAM,mBAAA,GAAsBC,kBAAY,YAA2B;AACjE,IAAA,IAAI,YAAY,OAAA,EAAS;AACzB,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,IAAI;AACF,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,CAAA;AAC9B,MAAA,MAAM,SAAS,MAAM,cAAA,CAAe,uBAAuB,OAAA,EAAS,EAAE,mBAAmB,CAAA;AAEzF,MAAA,MAAM,aAAA,GAAgB,KAAK,GAAA,EAAI;AAC/B,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,QAAA,EAAU,CAAA;AAC7B,MAAA,MAAM,QAAA,GAAW,MAAM,cAAA,CAAe,MAAA,CAAO,QAAQ,OAAO,CAAA;AAE5D,MAAA,MAAM,kBAAA,CAAmB,KAAM,aAAa,CAAA;AAG5C,MAAA,MAAM,SAAA,GAAYC,sBAAA,CAAiB,QAAA,CAAS,WAAW,CAAA;AACvD,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,MAAM,SAAS,MAAM,MAAA,CAAO,mBAAmB,EAAE,eAAA,EAAiB,WAAW,CAAA;AAC7E,QAAA,IAAI,WAAW,IAAA,EAAM;AACnB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AAAA,MACF;AAEA,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,gBAAA,EAAkB,WAAA,EAAa,UAAU,CAAA;AAAA,IAC9D,SAAS,KAAA,EAAO;AACd,MAAA,QAAA,CAAS;AAAA,QACP,MAAA,EAAQ,OAAA;AAAA,QACR,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,qBAAA;AAAA,QAClD,iBAAA,EAAmB;AAAA,OACpB,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,WAAA,CAAY,OAAA,GAAU,KAAA;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,cAAA,EAAgB,QAAQ,OAAA,EAAS,eAAA,EAAiB,iBAAiB,CAAC,CAAA;AAExE,EAAA,MAAM,MAAA,GAASD,iBAAA;AAAA,IACb,OAAO,QAAA,KAAiD;AAGtD,MAAA,IAAI,kBAAkB,OAAA,EAAS;AAC/B,MAAA,iBAAA,CAAkB,OAAA,GAAU,IAAA;AAE5B,MAAA,MAAM,aAAA,GAAgB,KAAK,GAAA,EAAI;AAC/B,MAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,eAAA,EAAiB,WAAA,EAAa,UAAU,CAAA;AAC3D,MAAA,IAAI;AACF,QAAA,MAAM,OAAO,aAAA,CAAc;AAAA,UACzB,UAAA,EAAY,QAAQ,MAAA,CAAO,UAAA;AAAA,UAC3B,aAAa,QAAA,CAAS,WAAA;AAAA,UACtB,kBAAkB,QAAA,CAAS,gBAAA;AAAA;AAAA;AAAA,UAG3B,mBAAA,EAAqB,mBAAA,GACjB,CAAC,KAAA,KAA6B;AAAE,YAAA,mBAAA,EAAoB;AAAA,UAAE,CAAA,GACtD,KAAA,CAAA;AAAA;AAAA;AAAA;AAAA,UAIJ,iBAAA,EAAmB,iBAAA,GACf,CAAC,IAAA,KAA4C;AAI3C,YAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,UAAU,CAAA;AACrD,YAAA,iBAAA,CAAkB,KAAK,IAAI,CAAA;AAAA,UAC7B,CAAA,GACC,CAAC,KAAA,KAA6C;AAC7C,YAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,SAAA,EAAW,WAAA,EAAa,UAAU,CAAA;AAAA,UACvD;AAAA,SACL,CAAA;AACD,QAAA,MAAM,kBAAA,CAAmB,KAAK,aAAa,CAAA;AAM3C,QAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,MAAA,EAAQ,WAAA,EAAa,UAAU,CAAA;AAClD,QAAA,MAAMH,WAAM,GAAG,CAAA;AACf,QAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,UAAA,EAAY,WAAA,EAAa,UAAU,CAAA;AACtD,QAAA,UAAA,GAAa,SAAS,WAAW,CAAA;AAAA,MACnC,SAAS,KAAA,EAAO;AAGd,QAAA,IAAI,iBAAA,CAAkB,KAAK,CAAA,EAAG;AAC5B,UAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,gBAAA,EAAkB,WAAA,EAAa,UAAU,CAAA;AAC5D,UAAA;AAAA,QACF;AACA,QAAA,QAAA,CAAS;AAAA,UACP,MAAA,EAAQ,OAAA;AAAA,UACR,OAAA,EAAS,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,mBAAA;AAAA,UAClD,iBAAA,EAAmB,IAAA;AAAA,UACnB,WAAA,EAAa;AAAA,SACd,CAAA;AAAA,MACH,CAAA,SAAE;AACA,QAAA,iBAAA,CAAkB,OAAA,GAAU,KAAA;AAAA,MAC9B;AAAA,IACF,CAAA;AAAA,IACA,CAAC,MAAA,EAAQ,OAAA,CAAQ,OAAO,UAAA,EAAY,mBAAA,EAAqB,mBAAmB,UAAU;AAAA,GACxF;AAEA,EAAA,MAAM,eAAeG,iBAAA,CAAY,MAAM,qBAAoB,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAEnF,EAAA,MAAM,OAAA,GAAUA,kBAAY,YAAY;AACtC,IAAA,IAAI,KAAA,CAAM,WAAW,gBAAA,EAAkB;AACvC,IAAA,MAAM,MAAA,CAAO,MAAM,WAAW,CAAA;AAAA,EAChC,CAAA,EAAG,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAElB,EAAA,MAAM,iBAAA,GAAoBA,kBAAY,YAAY;AAChD,IAAA,QAAA,CAAS,EAAE,MAAA,EAAQ,cAAA,EAAgB,CAAA;AACnC,IAAA,MAAM,mBAAA,EAAoB;AAAA,EAC5B,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,MAAM,eAAA,GAAkBA,kBAAY,YAAY;AAC9C,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,OAAA,IAAW,CAAC,MAAM,WAAA,EAAa;AACpD,IAAA,MAAM,MAAA,CAAO,MAAM,WAAW,CAAA;AAAA,EAChC,CAAA,EAAG,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAElB,EAAA,OAAO,EAAE,KAAA,EAAO,YAAA,EAAc,OAAA,EAAS,mBAAmB,eAAA,EAAgB;AAC5E","file":"useProving.cjs","sourcesContent":["/**\n * @fileoverview 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 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 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 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 check (SubmitStep.tsx:419-426).\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 }\n\n setState({ status: 'readyToFulfill', attestation: verified })\n } catch (error) {\n setState({\n status: 'error',\n message: error instanceof Error ? error.message : 'Verification failed',\n isSubmissionError: false,\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 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\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 pre-check — FAIL CLOSED. We must positively\n // confirm the nullifier is unused on the target chain before allowing\n // fulfillment. Any inability to verify (cannot extract, registry\n // unreachable/errored) blocks. \"If we can't verify the nullifier has not\n // been used, we cannot use it.\"\n const nullifier = extractNullifier(verified.attestation)\n if (!nullifier) {\n throw new Error(\n 'Could not extract a nullifier from the attestation; cannot confirm it is unused. Refusing to proceed.',\n )\n }\n const isUsed = await client.checkNullifierUsed({ paymentMethodId, nullifier })\n if (isUsed !== false) {\n throw new Error(\n isUsed === true\n ? 'This payment has already been verified. The nullifier has already been used onchain.'\n : 'Could not verify the nullifier is unused (registry unreachable). Refusing to proceed — please retry.',\n )\n }\n\n // only an explicit `false` reaches here\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,91 +1,4 @@
1
- import { Hex } from 'viem';
2
- import { V3AttestationResponseObject } from '@provex/utils';
3
- import { c as CaptureAdapter, a as CaptureRequest, S as SelectTransaction, e as ProvingState } from './types-ByiBb7AN.cjs';
1
+ import 'viem';
2
+ import '@provex/utils';
3
+ export { f as ProvingClient, U as UseProvingOptions, g as UseProvingReturn, u as useProving } from './useProving-h4RtlSEU.cjs';
4
4
  import '@provex/utils/tokens';
5
-
6
- /**
7
- * @fileoverview useProving — headless orchestrator for the enclave proving
8
- * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep
9
- * state machine (ProofPanel) with no presentation. Renders nothing; returns
10
- * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.
11
- *
12
- * Owns: the ProvingState machine, the nullifier double-spend check
13
- * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing
14
- * (SubmitStep.tsx:931-955), and the minimum-visible-state timings
15
- * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,
16
- * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.
17
- */
18
-
19
- /**
20
- * Minimal context object that buildWritableMethod passes to the per-call hooks.
21
- * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.
22
- * Only `hash` is consumed by this hook — the rest is opaque context.
23
- */
24
- interface TxCallbackContext {
25
- chainId: number;
26
- request: unknown;
27
- }
28
- /** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +
29
- * WriteHookParams from @provex/react but declared here to keep @provex/proving
30
- * from depending on the React package's built dist (which the test environment
31
- * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.
32
- *
33
- * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call
34
- * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):
35
- * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void
36
- * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void
37
- */
38
- interface FulfillParams {
39
- intentHash: Hex;
40
- attestation: V3AttestationResponseObject;
41
- verificationData: Hex;
42
- postIntentHookData?: Hex;
43
- onAwaitingSignature?: (info: TxCallbackContext) => void;
44
- onTransactionHash?: (info: TxCallbackContext & {
45
- hash: Hex;
46
- }) => void;
47
- }
48
- /** The structural slice of ProveXClient the orchestrator depends on. Keeping
49
- * it structural lets tests pass a light fake and avoids importing the class. */
50
- interface ProvingClient {
51
- fulfillIntent: (params: FulfillParams) => Promise<{
52
- hash: Hex;
53
- receipt: unknown;
54
- }>;
55
- checkNullifierUsed: (params: {
56
- paymentMethodId: Hex;
57
- nullifier: Hex;
58
- }) => Promise<boolean | null>;
59
- }
60
- interface UseProvingOptions {
61
- client: ProvingClient;
62
- captureAdapter: CaptureAdapter;
63
- request: CaptureRequest;
64
- /** Payment method id for the nullifier registry lookup. */
65
- paymentMethodId: Hex;
66
- /** Manual-select resolver forwarded to the adapter. */
67
- selectTransaction?: SelectTransaction;
68
- /**
69
- * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.
70
- * Receives the raw TxContext — the hook does not inspect the argument.
71
- */
72
- onAwaitingSignature?: () => void;
73
- /** Receives the raw tx hash extracted from the TxContext+hash payload. */
74
- onTransactionHash?: (hash: Hex) => void;
75
- /** Called once when the flow reaches `complete`. */
76
- onComplete?: (attestation: V3AttestationResponseObject) => void;
77
- }
78
- interface UseProvingReturn {
79
- state: ProvingState;
80
- /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */
81
- authenticate: () => Promise<void>;
82
- /** Phase 3: submit fulfillIntent → confirm → sync → complete. */
83
- fulfill: () => Promise<void>;
84
- /** Re-run attestation after a pre-fulfilment error. */
85
- retryVerification: () => Promise<void>;
86
- /** Re-run submission after a post-fulfilment error. */
87
- retrySubmission: () => Promise<void>;
88
- }
89
- declare function useProving(options: UseProvingOptions): UseProvingReturn;
90
-
91
- export { type ProvingClient, type UseProvingOptions, type UseProvingReturn, useProving };
@@ -1,91 +1,4 @@
1
- import { Hex } from 'viem';
2
- import { V3AttestationResponseObject } from '@provex/utils';
3
- import { c as CaptureAdapter, a as CaptureRequest, S as SelectTransaction, e as ProvingState } from './types-ByiBb7AN.js';
1
+ import 'viem';
2
+ import '@provex/utils';
3
+ export { f as ProvingClient, U as UseProvingOptions, g as UseProvingReturn, u as useProving } from './useProving-h4RtlSEU.js';
4
4
  import '@provex/utils/tokens';
5
-
6
- /**
7
- * @fileoverview useProving — headless orchestrator for the enclave proving
8
- * pipeline. Replicates the live AuthenticateStep/GenerateProofStep/SubmitStep
9
- * state machine (ProofPanel) with no presentation. Renders nothing; returns
10
- * state + actions for a UI (UnifiedBuy ProofPanel or a wallet) to bind to.
11
- *
12
- * Owns: the ProvingState machine, the nullifier double-spend check
13
- * (SubmitStep.tsx:419-426), the attest→fulfil sequencing, retry routing
14
- * (SubmitStep.tsx:931-955), and the minimum-visible-state timings
15
- * (SubmitStep.tsx:404-417). Delegates capture to the injected CaptureAdapter,
16
- * the on-chain write (+ receipt + indexer sync) to ProveXClient.fulfillIntent.
17
- */
18
-
19
- /**
20
- * Minimal context object that buildWritableMethod passes to the per-call hooks.
21
- * Kept structural so @provex/proving does not depend on @valve-tech/wallet-adapter.
22
- * Only `hash` is consumed by this hook — the rest is opaque context.
23
- */
24
- interface TxCallbackContext {
25
- chainId: number;
26
- request: unknown;
27
- }
28
- /** Structural params for the fulfillIntent call. Mirrors FulfillIntentParams +
29
- * WriteHookParams from @provex/react but declared here to keep @provex/proving
30
- * from depending on the React package's built dist (which the test environment
31
- * doesn't require). The real ProveXClient.fulfillIntent satisfies this shape.
32
- *
33
- * IMPORTANT: onAwaitingSignature and onTransactionHash match the exact call
34
- * shapes in buildWritableMethod (ProveXClient.ts ~890, ~903):
35
- * params.onAwaitingSignature?.(ctx) → (info: TxCallbackContext) => void
36
- * params.onTransactionHash?.({ ...ctx, hash }) → (info: TxCallbackContext & { hash: Hex }) => void
37
- */
38
- interface FulfillParams {
39
- intentHash: Hex;
40
- attestation: V3AttestationResponseObject;
41
- verificationData: Hex;
42
- postIntentHookData?: Hex;
43
- onAwaitingSignature?: (info: TxCallbackContext) => void;
44
- onTransactionHash?: (info: TxCallbackContext & {
45
- hash: Hex;
46
- }) => void;
47
- }
48
- /** The structural slice of ProveXClient the orchestrator depends on. Keeping
49
- * it structural lets tests pass a light fake and avoids importing the class. */
50
- interface ProvingClient {
51
- fulfillIntent: (params: FulfillParams) => Promise<{
52
- hash: Hex;
53
- receipt: unknown;
54
- }>;
55
- checkNullifierUsed: (params: {
56
- paymentMethodId: Hex;
57
- nullifier: Hex;
58
- }) => Promise<boolean | null>;
59
- }
60
- interface UseProvingOptions {
61
- client: ProvingClient;
62
- captureAdapter: CaptureAdapter;
63
- request: CaptureRequest;
64
- /** Payment method id for the nullifier registry lookup. */
65
- paymentMethodId: Hex;
66
- /** Manual-select resolver forwarded to the adapter. */
67
- selectTransaction?: SelectTransaction;
68
- /**
69
- * Forwarded to ProveXClient.fulfillIntent for the consumer's tx-flight strip.
70
- * Receives the raw TxContext — the hook does not inspect the argument.
71
- */
72
- onAwaitingSignature?: () => void;
73
- /** Receives the raw tx hash extracted from the TxContext+hash payload. */
74
- onTransactionHash?: (hash: Hex) => void;
75
- /** Called once when the flow reaches `complete`. */
76
- onComplete?: (attestation: V3AttestationResponseObject) => void;
77
- }
78
- interface UseProvingReturn {
79
- state: ProvingState;
80
- /** Phase 1+2: authenticate, capture, attest, nullifier-check → readyToFulfill. */
81
- authenticate: () => Promise<void>;
82
- /** Phase 3: submit fulfillIntent → confirm → sync → complete. */
83
- fulfill: () => Promise<void>;
84
- /** Re-run attestation after a pre-fulfilment error. */
85
- retryVerification: () => Promise<void>;
86
- /** Re-run submission after a post-fulfilment error. */
87
- retrySubmission: () => Promise<void>;
88
- }
89
- declare function useProving(options: UseProvingOptions): UseProvingReturn;
90
-
91
- export { type ProvingClient, type UseProvingOptions, type UseProvingReturn, useProving };
@@ -1,3 +1,3 @@
1
- export { useProving } from './chunk-LFDS3XGK.js';
1
+ export { useProving } from './chunk-OSBIXUFA.js';
2
2
  //# sourceMappingURL=useProving.js.map
3
3
  //# sourceMappingURL=useProving.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@provex/proving",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Headless enclave proving orchestrator for the Provex buy flow",
5
5
  "type": "module",
6
6
  "repository": {
@@ -21,11 +21,6 @@
21
21
  "types": "./dist/useProving.d.ts",
22
22
  "import": "./dist/useProving.js",
23
23
  "require": "./dist/useProving.cjs"
24
- },
25
- "./adapters/extensionEnclave": {
26
- "types": "./dist/adapters/extensionEnclave.d.ts",
27
- "import": "./dist/adapters/extensionEnclave.js",
28
- "require": "./dist/adapters/extensionEnclave.cjs"
29
24
  }
30
25
  },
31
26
  "publishConfig": {
@@ -44,8 +39,7 @@
44
39
  "test:watch": "vitest"
45
40
  },
46
41
  "dependencies": {
47
- "@provex/react": "1.8.0",
48
- "@provex/utils": "1.8.0"
42
+ "@provex/utils": "1.9.0"
49
43
  },
50
44
  "peerDependencies": {
51
45
  "react": "^18.0.0 || ^19.0.0",