@provex/react 1.2.3

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/index.cjs ADDED
@@ -0,0 +1,2756 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var reactQuery = require('@tanstack/react-query');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var payment = require('@provex/utils/payment');
7
+ var currencies = require('@provex/utils/currencies');
8
+ var tokens = require('@provex/utils/tokens');
9
+ var units = require('@provex/utils/units');
10
+ var conversionRates = require('@provex/utils/conversionRates');
11
+ var reputation = require('@provex/utils/reputation');
12
+ var contracts = require('@provex/utils/contracts');
13
+ var viem = require('viem');
14
+ var fees = require('@provex/utils/fees');
15
+ var abis = require('@provex/abis');
16
+ var chain = require('@provex/utils/chain');
17
+ var chains = require('viem/chains');
18
+ var wagmi = require('wagmi');
19
+ var ids = require('@provex/utils/ids');
20
+
21
+ // src/ProvexProvider.tsx
22
+
23
+ // src/lib/api.ts
24
+ function createApiClient(apiUrl) {
25
+ async function request(path, options) {
26
+ const url = `${apiUrl}${path}`;
27
+ const response = await fetch(url, {
28
+ ...options,
29
+ headers: {
30
+ "Content-Type": "application/json",
31
+ ...options.headers
32
+ }
33
+ });
34
+ if (!response.ok) {
35
+ throw new Error(`API request failed: ${response.status} ${response.statusText} (${url})`);
36
+ }
37
+ return response.json();
38
+ }
39
+ return {
40
+ get: (path, options) => request(path, { ...options, method: "GET" }),
41
+ post: (path, body, options) => request(path, {
42
+ ...options,
43
+ method: "POST",
44
+ body: JSON.stringify(body)
45
+ })
46
+ };
47
+ }
48
+ var ProvexContext = react.createContext(null);
49
+ var defaultQueryClient = new reactQuery.QueryClient({
50
+ defaultOptions: {
51
+ queries: {
52
+ staleTime: 3e4,
53
+ refetchOnWindowFocus: false
54
+ }
55
+ }
56
+ });
57
+ function ProvexProvider({
58
+ config,
59
+ indexer,
60
+ queryClient,
61
+ children
62
+ }) {
63
+ const resolvedQueryClient = queryClient ?? defaultQueryClient;
64
+ const contextValue = react.useMemo(() => {
65
+ const apiClient = createApiClient(config.apiUrl);
66
+ return { config, indexer, apiClient };
67
+ }, [config.apiUrl, config.chain, indexer]);
68
+ return /* @__PURE__ */ jsxRuntime.jsx(ProvexContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: resolvedQueryClient, children }) });
69
+ }
70
+ function useProvex() {
71
+ const context = react.useContext(ProvexContext);
72
+ if (!context) {
73
+ throw new Error("useProvex must be used within a <ProvexProvider>");
74
+ }
75
+ return context;
76
+ }
77
+ function useOptionalProvex() {
78
+ return react.useContext(ProvexContext);
79
+ }
80
+ var DEFAULT_REFETCH_INTERVAL_MS = 5e3;
81
+ function getRate({
82
+ deposit,
83
+ paymentMethod,
84
+ currency
85
+ }) {
86
+ if (!deposit) return 0n;
87
+ const paymentConfig = payment.providerConfigs(deposit.chainId)[paymentMethod];
88
+ const candidateKeys = [
89
+ paymentMethod,
90
+ ...Array.from(paymentConfig?.subProviders ?? []).map((sp) => sp.key)
91
+ ];
92
+ let minRate = null;
93
+ for (const key of candidateKeys) {
94
+ const contractId = payment.providerKeyToContractId(key).toLowerCase();
95
+ const value = deposit.conversionRates.get(contractId)?.get(currency.contractId.toLowerCase());
96
+ if (value) {
97
+ if (minRate === null || value < minRate) {
98
+ minRate = value;
99
+ }
100
+ }
101
+ }
102
+ return minRate ?? 0n;
103
+ }
104
+ function toDepositInfo(deposit) {
105
+ return {
106
+ escrow: deposit.escrow,
107
+ localId: deposit.localId,
108
+ chainId: deposit.chainId,
109
+ participantAddress: deposit.participantAddress,
110
+ token: deposit.token,
111
+ remaining: deposit.remaining,
112
+ deposited: deposit.deposited,
113
+ minAmount: deposit.minAmount,
114
+ maxAmount: deposit.maxAmount,
115
+ status: deposit.status,
116
+ acceptingIntents: deposit.acceptingIntents,
117
+ availableFunds: deposit.availableFunds,
118
+ conversionRates: deposit.conversionRates,
119
+ verifierPaymentMethodIds: deposit.paymentMethodIds
120
+ };
121
+ }
122
+ function useDeposits(options) {
123
+ const {
124
+ token,
125
+ currency = currencies.SUPPORTED_CURRENCIES.USD,
126
+ paymentMethod,
127
+ refetchIntervalMs = DEFAULT_REFETCH_INTERVAL_MS
128
+ } = options;
129
+ const { indexer } = useProvex();
130
+ const queryClient = reactQuery.useQueryClient();
131
+ const paymentMethodIds = react.useMemo(() => {
132
+ const paymentConfig = payment.providerConfigs(token.chainId)[paymentMethod];
133
+ return /* @__PURE__ */ new Set([
134
+ payment.providerKeyToContractId(paymentMethod).toLowerCase(),
135
+ ...paymentConfig.v2Verifiers.map((v) => v.toLowerCase()),
136
+ ...Array.from(paymentConfig.subProviders).flatMap((subProvider) => [
137
+ payment.providerKeyToContractId(subProvider.key).toLowerCase(),
138
+ ...subProvider.v2Verifiers(token.chainId).map((v) => v.toLowerCase())
139
+ ])
140
+ ]);
141
+ }, [token.chainId, paymentMethod]);
142
+ const depositsQuery = reactQuery.useQuery({
143
+ queryKey: ["provex", "deposits", token.address, token.chainId, currency.contractId, paymentMethod],
144
+ queryFn: async () => {
145
+ const result = await indexer.getMatchableDeposits({
146
+ chainId: token.chainId,
147
+ token: token.address,
148
+ currencyId: currency.contractId
149
+ });
150
+ return result.items.filter((d) => {
151
+ for (const id of d.paymentMethodIds) {
152
+ if (paymentMethodIds.has(id)) return true;
153
+ }
154
+ return false;
155
+ }).map(toDepositInfo);
156
+ },
157
+ staleTime: refetchIntervalMs,
158
+ refetchInterval: refetchIntervalMs
159
+ });
160
+ const deposits = react.useMemo(() => depositsQuery.data ?? [], [depositsQuery.data]);
161
+ const canAcceptAmount = react.useCallback((deposit, amount) => {
162
+ return amount <= deposit.availableFunds && amount >= deposit.minAmount && amount <= deposit.maxAmount;
163
+ }, []);
164
+ const filterDepositsByAmount = react.useCallback((amountInInt) => {
165
+ if (!amountInInt || !currency) return [];
166
+ const matchable = [];
167
+ for (const deposit of deposits) {
168
+ const rate = getRate({ deposit, paymentMethod, currency });
169
+ if (rate === 0n) continue;
170
+ const amountToken = conversionRates.convertCurrencyToTokenOutput({
171
+ token,
172
+ currency,
173
+ currencyAmountInt: amountInInt,
174
+ rate
175
+ });
176
+ if (canAcceptAmount(deposit, amountToken)) {
177
+ matchable.push({ deposit, rate });
178
+ }
179
+ }
180
+ return matchable.sort((a, b) => {
181
+ if (a.rate < b.rate) return -1;
182
+ if (a.rate > b.rate) return 1;
183
+ if (a.deposit.localId < b.deposit.localId) return -1;
184
+ if (a.deposit.localId > b.deposit.localId) return 1;
185
+ return 0;
186
+ }).map((m) => m.deposit);
187
+ }, [deposits, currency, paymentMethod, token, canAcceptAmount]);
188
+ const refetch = react.useCallback(() => {
189
+ queryClient.invalidateQueries({
190
+ queryKey: ["provex", "deposits", token.address, token.chainId, currency.contractId, paymentMethod]
191
+ });
192
+ }, [queryClient, token.address, token.chainId, currency.contractId, paymentMethod]);
193
+ return {
194
+ deposits,
195
+ isLoading: depositsQuery.isLoading,
196
+ isUpdating: depositsQuery.isFetching && !depositsQuery.isLoading,
197
+ error: depositsQuery.error,
198
+ refetch,
199
+ filterDepositsByAmount,
200
+ getMatchableDeposits: (amountInInt) => {
201
+ if (typeof amountInInt === "string") {
202
+ amountInInt = units.parseUnits(amountInInt, { decimals: currency?.decimals || 2 });
203
+ }
204
+ if (!amountInInt) return [];
205
+ return filterDepositsByAmount(amountInInt);
206
+ }
207
+ };
208
+ }
209
+ var getTransactionGasInputs = async (chainId) => {
210
+ const chain$1 = chain.chainIdToChain.get(chainId);
211
+ if (!chain$1) {
212
+ throw new Error(`Unsupported chainId: ${chainId}`);
213
+ }
214
+ const block = await tokens.publicClient(chain$1).getBlock({
215
+ blockTag: "latest"
216
+ });
217
+ if (!block.baseFeePerGas) {
218
+ throw new Error(`No baseFeePerGas available for chainId: ${chainId}`);
219
+ }
220
+ return {
221
+ maxFeePerGas: block.baseFeePerGas * 2n,
222
+ maxPriorityFeePerGas: block.baseFeePerGas / 5n
223
+ };
224
+ };
225
+
226
+ // src/lib/updates.ts
227
+ var POLL_INTERVAL_MS = 1e3;
228
+ var SLOW_SYNC_THRESHOLD_MS = 15e3;
229
+ var checkMutation = async ({
230
+ queryDeposit,
231
+ depositIdHex,
232
+ status,
233
+ onSuccess,
234
+ onSlowSync,
235
+ slowSyncThresholdMs = SLOW_SYNC_THRESHOLD_MS
236
+ }) => {
237
+ const startTime = Date.now();
238
+ let slowSyncFired = false;
239
+ while (true) {
240
+ const result = await queryDeposit(depositIdHex);
241
+ if (result?.status === status) {
242
+ break;
243
+ }
244
+ if (!slowSyncFired && onSlowSync && Date.now() - startTime > slowSyncThresholdMs) {
245
+ slowSyncFired = true;
246
+ onSlowSync();
247
+ }
248
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
249
+ }
250
+ setTimeout(() => {
251
+ onSuccess?.();
252
+ }, POLL_INTERVAL_MS);
253
+ };
254
+ var checkTransactionIndexed = async ({
255
+ queryTransaction,
256
+ hash,
257
+ onSuccess,
258
+ onSlowSync,
259
+ slowSyncThresholdMs = SLOW_SYNC_THRESHOLD_MS
260
+ }) => {
261
+ const startTime = Date.now();
262
+ let slowSyncFired = false;
263
+ while (true) {
264
+ const result = await queryTransaction(hash);
265
+ if (result) {
266
+ break;
267
+ }
268
+ if (!slowSyncFired && onSlowSync && Date.now() - startTime > slowSyncThresholdMs) {
269
+ slowSyncFired = true;
270
+ onSlowSync();
271
+ }
272
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
273
+ }
274
+ onSuccess?.();
275
+ };
276
+
277
+ // src/lib/errors.ts
278
+ var ERROR_PATTERNS = [
279
+ // Wallet / signer rejections
280
+ { pattern: /user rejected|user denied|rejected the request|user cancelled|user canceled/i, message: "Transaction was cancelled." },
281
+ // On-chain reverts — specific errors first (first match wins)
282
+ { pattern: /InsufficientDepositLiquidity|0xc3df48f3/i, message: "This deposit no longer has enough available liquidity. Please try a smaller amount or a different deposit." },
283
+ { pattern: /execution reverted/i, message: "Transaction failed on-chain. The order may no longer be available." },
284
+ // Gas / balance issues
285
+ { pattern: /insufficient funds/i, message: "Insufficient funds for gas fees." },
286
+ { pattern: /gas required exceeds/i, message: "Transaction requires more gas than allowed." },
287
+ // Transaction replacement / nonce issues
288
+ { pattern: /could not replace existing tx|replacement transaction underpriced|nonce too low/i, message: "A previous transaction is still pending. Please wait for it to confirm or speed it up in your wallet." },
289
+ // Deposit state errors
290
+ { pattern: /DepositNotAcceptingIntents|0x67688c1a/i, message: "This deposit is currently paused. Please try a different deposit." },
291
+ // Network / RPC errors
292
+ { pattern: /could not detect network|network changed/i, message: "Network connection issue. Please check your wallet network." },
293
+ { pattern: /disconnected|not connected/i, message: "Wallet disconnected. Please reconnect and try again." },
294
+ { pattern: /timeout|etimedout/i, message: "Request timed out. Please try again." },
295
+ { pattern: /fetch failed|econnrefused|enotfound/i, message: "Unable to reach the server. Please check your connection." },
296
+ // Rate limiting / service unavailable
297
+ { pattern: /429|too many requests/i, message: "Too many requests. Please wait a moment and try again." },
298
+ { pattern: /503|service unavailable/i, message: "Service temporarily unavailable. Please try again shortly." },
299
+ // Nullifier / double-spend (keep the original — it's already user-friendly)
300
+ { pattern: /nullifier has already been used/i, message: "This payment has already been verified and cannot be used again." },
301
+ { pattern: /already been verified/i, message: "This payment has already been verified and cannot be used again." }
302
+ ];
303
+ function getUserFriendlyErrorMessage(error) {
304
+ const raw = extractErrorString(error);
305
+ for (const { pattern, message } of ERROR_PATTERNS) {
306
+ if (pattern.test(raw)) {
307
+ return message;
308
+ }
309
+ }
310
+ return "Something went wrong. Please try again.";
311
+ }
312
+ function isUserRejectionError(error) {
313
+ const raw = extractErrorString(error);
314
+ return /user rejected|user denied|rejected the request|user cancelled|user canceled/i.test(raw);
315
+ }
316
+ function extractErrorString(error) {
317
+ if (error instanceof Error) return error.message;
318
+ if (typeof error === "string") return error;
319
+ try {
320
+ return JSON.stringify(error);
321
+ } catch {
322
+ return String(error);
323
+ }
324
+ }
325
+
326
+ // src/client/types.ts
327
+ var PROVEX_DEFAULTS = {
328
+ 8453: { apiUrl: "https://app.provex.com", indexerUrl: "https://indexer.provex.com" },
329
+ 369: { apiUrl: "https://app.provex.com", indexerUrl: "https://indexer.provex.com" }
330
+ };
331
+ var ProveXError = class extends Error {
332
+ code;
333
+ cause;
334
+ constructor(code, message, cause) {
335
+ super(message);
336
+ this.name = "ProveXError";
337
+ this.code = code;
338
+ this.cause = cause;
339
+ }
340
+ /** Check if this error was caused by the user rejecting in their wallet. */
341
+ get isRejection() {
342
+ return this.code === "WALLET_REJECTED";
343
+ }
344
+ };
345
+
346
+ // src/client/ProveXClient.ts
347
+ var ProveXClient = class {
348
+ /** The viem Chain object — carries RPC URLs, chain ID, and metadata. */
349
+ chain;
350
+ /** Numeric chain ID (derived from chain.id). */
351
+ chainId;
352
+ wallet;
353
+ indexer;
354
+ escrowOverride;
355
+ onTransactionHash;
356
+ apiUrl;
357
+ onSlowSync;
358
+ /** Lazily-created public client for RPC calls. */
359
+ _publicClient = null;
360
+ /** Cached orchestrator address (lazy-loaded). */
361
+ cachedOrchestratorAddress = null;
362
+ constructor(config) {
363
+ this.chain = config.chain;
364
+ this.chainId = config.chain.id;
365
+ const defaults = PROVEX_DEFAULTS[this.chainId];
366
+ this.wallet = config.wallet;
367
+ this.indexer = config.indexer;
368
+ this.escrowOverride = config.escrowAddress;
369
+ this.onTransactionHash = config.onTransactionHash;
370
+ this.onSlowSync = config.onSlowSync;
371
+ this.apiUrl = config.apiUrl ?? defaults?.apiUrl ?? "https://app.provex.com";
372
+ this.createDepositRaw = this.buildWritableMethod({
373
+ abi: abis.V3EscrowAbi,
374
+ contractResolver: () => this.getEscrowAddress(),
375
+ functionName: "createDeposit",
376
+ argsMapper: (params) => [params]
377
+ });
378
+ this.addFunds = this.buildWritableMethod({
379
+ abi: abis.V3EscrowAbi,
380
+ contractResolver: () => this.getEscrowAddress(),
381
+ functionName: "addFunds",
382
+ argsMapper: (params) => [params.depositId, params.amount]
383
+ });
384
+ this.removeFunds = this.buildWritableMethod({
385
+ abi: abis.V3EscrowAbi,
386
+ contractResolver: () => this.getEscrowAddress(),
387
+ functionName: "removeFunds",
388
+ argsMapper: (params) => [params.depositId, params.amount]
389
+ });
390
+ this.withdrawDeposit = this.buildWritableMethod({
391
+ abi: abis.V3EscrowAbi,
392
+ contractResolver: () => this.getEscrowAddress(),
393
+ functionName: "withdrawDeposit",
394
+ argsMapper: (params) => [params.depositId]
395
+ });
396
+ this.pruneExpiredIntents = this.buildWritableMethod({
397
+ abi: abis.V3EscrowAbi,
398
+ contractResolver: () => this.getEscrowAddress(),
399
+ functionName: "pruneExpiredIntents",
400
+ argsMapper: (params) => [params.depositId]
401
+ });
402
+ this.setAcceptingIntents = this.buildWritableMethod({
403
+ abi: abis.V3EscrowAbi,
404
+ contractResolver: () => this.getEscrowAddress(),
405
+ functionName: "setAcceptingIntents",
406
+ argsMapper: (params) => [params.depositId, params.accepting]
407
+ });
408
+ this.setRetainOnEmpty = this.buildWritableMethod({
409
+ abi: abis.V3EscrowAbi,
410
+ contractResolver: () => this.getEscrowAddress(),
411
+ functionName: "setRetainOnEmpty",
412
+ argsMapper: (params) => [params.depositId, params.retain]
413
+ });
414
+ this.setIntentRange = this.buildWritableMethod({
415
+ abi: abis.V3EscrowAbi,
416
+ contractResolver: () => this.getEscrowAddress(),
417
+ functionName: "setIntentRange",
418
+ argsMapper: (params) => [params.depositId, { min: params.min, max: params.max }]
419
+ });
420
+ this.setPaymentMethodActive = this.buildWritableMethod({
421
+ abi: abis.V3EscrowAbi,
422
+ contractResolver: () => this.getEscrowAddress(),
423
+ functionName: "setPaymentMethodActive",
424
+ argsMapper: (params) => [params.depositId, params.paymentMethod, params.active]
425
+ });
426
+ this.setCurrencyMinRate = this.buildWritableMethod({
427
+ abi: abis.V3EscrowAbi,
428
+ contractResolver: () => this.getEscrowAddress(),
429
+ functionName: "setCurrencyMinRate",
430
+ argsMapper: (params) => [params.depositId, params.paymentMethod, params.currency, params.rate]
431
+ });
432
+ this.addCurrencies = this.buildWritableMethod({
433
+ abi: abis.V3EscrowAbi,
434
+ contractResolver: () => this.getEscrowAddress(),
435
+ functionName: "addCurrencies",
436
+ argsMapper: (params) => [params.depositId, params.paymentMethod, params.currencies]
437
+ });
438
+ this.deactivateCurrency = this.buildWritableMethod({
439
+ abi: abis.V3EscrowAbi,
440
+ contractResolver: () => this.getEscrowAddress(),
441
+ functionName: "deactivateCurrency",
442
+ argsMapper: (params) => [params.depositId, params.paymentMethod, params.currencyCode]
443
+ });
444
+ this.addPaymentMethods = this.buildWritableMethod({
445
+ abi: abis.V3EscrowAbi,
446
+ contractResolver: () => this.getEscrowAddress(),
447
+ functionName: "addPaymentMethods",
448
+ argsMapper: (params) => [params.depositId, params.paymentMethods, params.paymentMethodData, params.currencies]
449
+ });
450
+ this.cancelIntent = this.buildWritableMethod({
451
+ abi: abis.OrchestratorAbi,
452
+ contractResolver: () => this.getOrchestratorAddress(),
453
+ functionName: "cancelIntent",
454
+ argsMapper: (params) => [params.intentHash]
455
+ });
456
+ this.releaseFundsToPayer = this.buildWritableMethod({
457
+ abi: abis.OrchestratorAbi,
458
+ contractResolver: () => this.getOrchestratorAddress(),
459
+ functionName: "releaseFundsToPayer",
460
+ argsMapper: (params) => [params.intentHash]
461
+ });
462
+ this.approve = this.buildWritableMethod({
463
+ abi: [{ name: "approve", type: "function", stateMutability: "nonpayable", inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ name: "", type: "bool" }] }],
464
+ contractResolver: (_params) => _params.token,
465
+ functionName: "approve",
466
+ argsMapper: (params) => [params.spender, params.amount ?? BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")]
467
+ });
468
+ }
469
+ // ─── Address Resolution ──────────────────────────────────────────────────
470
+ /** Get the V3 escrow address for this client's chain. */
471
+ getEscrowAddress() {
472
+ if (this.escrowOverride) return this.escrowOverride;
473
+ const addr = contracts.getLatestContract(this.chainId, "escrow", "v3");
474
+ if (!addr) throw new ProveXError("VALIDATION_ERROR", `No V3 escrow found for chain ${this.chainId}`);
475
+ return addr;
476
+ }
477
+ /** Get the orchestrator address (cached after first read). */
478
+ async getOrchestratorAddress() {
479
+ if (this.cachedOrchestratorAddress) return this.cachedOrchestratorAddress;
480
+ const escrow = this.getEscrowAddress();
481
+ const result = await this.readContract({
482
+ address: escrow,
483
+ abi: abis.V3EscrowAbi,
484
+ functionName: "orchestrator"
485
+ });
486
+ this.cachedOrchestratorAddress = result;
487
+ return this.cachedOrchestratorAddress;
488
+ }
489
+ // ─── Indexer Reads ───────────────────────────────────────────────────────
490
+ requireIndexer() {
491
+ if (!this.indexer) throw new ProveXError("INDEXER_NOT_CONFIGURED", "No IndexerAdapter provided. Pass `indexer` to createProveXClient().");
492
+ return this.indexer;
493
+ }
494
+ async getMatchableDeposits(params) {
495
+ return this.requireIndexer().getMatchableDeposits({ ...params, chainId: this.chainId });
496
+ }
497
+ async getDepositWithVerifiers(params) {
498
+ return this.requireIndexer().getDepositWithVerifiers({ ...params, chainId: this.chainId });
499
+ }
500
+ async getIntent(intentHash) {
501
+ return this.requireIndexer().getIntent({ intentHash, chainId: this.chainId });
502
+ }
503
+ async getPayeeDetailsHash(params) {
504
+ return this.requireIndexer().getPayeeDetailsHash({ ...params, chainId: this.chainId });
505
+ }
506
+ async getUserReputation(address) {
507
+ return this.requireIndexer().getUserReputation({ address, chainId: this.chainId });
508
+ }
509
+ // ─── On-Chain Reads ──────────────────────────────────────────────────────
510
+ async getProtocolFees() {
511
+ const orchestrator = await this.getOrchestratorAddress();
512
+ const [feeRaw, feeRecipient] = await Promise.all([
513
+ this.readContract({ address: orchestrator, abi: abis.OrchestratorAbi, functionName: "protocolFee" }),
514
+ this.readContract({ address: orchestrator, abi: abis.OrchestratorAbi, functionName: "protocolFeeRecipient" })
515
+ ]);
516
+ return {
517
+ feeRaw,
518
+ feeInfo: fees.normalizeFeePrecision(feeRaw),
519
+ feeRecipient
520
+ };
521
+ }
522
+ async getDeposit(depositId) {
523
+ return this.readContract({
524
+ address: this.getEscrowAddress(),
525
+ abi: abis.V3EscrowAbi,
526
+ functionName: "getDeposit",
527
+ args: [depositId]
528
+ });
529
+ }
530
+ async getAccountDeposits(account) {
531
+ return this.readContract({
532
+ address: this.getEscrowAddress(),
533
+ abi: abis.V3EscrowAbi,
534
+ functionName: "getAccountDeposits",
535
+ args: [account]
536
+ });
537
+ }
538
+ async getDepositCounter() {
539
+ return this.readContract({
540
+ address: this.getEscrowAddress(),
541
+ abi: abis.V3EscrowAbi,
542
+ functionName: "depositCounter"
543
+ });
544
+ }
545
+ async getAllowance(params) {
546
+ return this.readContract({
547
+ address: params.token,
548
+ abi: [{ name: "allowance", type: "function", stateMutability: "view", inputs: [{ name: "owner", type: "address" }, { name: "spender", type: "address" }], outputs: [{ name: "", type: "uint256" }] }],
549
+ functionName: "allowance",
550
+ args: [params.owner, params.spender]
551
+ });
552
+ }
553
+ async checkNullifierUsed(params) {
554
+ try {
555
+ const orchestrator = await this.getOrchestratorAddress();
556
+ const verifierRegistry = await this.readContract({ address: orchestrator, abi: abis.OrchestratorAbi, functionName: "paymentVerifierRegistry" });
557
+ const verifier = await this.readContract({ address: verifierRegistry, abi: abis.PaymentVerifierRegistryAbi, functionName: "getVerifier", args: [params.paymentMethodId] });
558
+ const nullifierRegistry = await this.readContract({ address: verifier, abi: abis.UnifiedPaymentVerifierAbi, functionName: "nullifierRegistry" });
559
+ return await this.readContract({ address: nullifierRegistry, abi: abis.NullifierRegistryAbi, functionName: "isNullified", args: [params.nullifier] });
560
+ } catch {
561
+ return null;
562
+ }
563
+ }
564
+ // ─── Escrow Writes ───────────────────────────────────────────────────────
565
+ /** Low-level deposit creation — pass pre-hashed contract params directly. */
566
+ createDepositRaw;
567
+ addFunds;
568
+ removeFunds;
569
+ withdrawDeposit;
570
+ pruneExpiredIntents;
571
+ setAcceptingIntents;
572
+ setRetainOnEmpty;
573
+ setIntentRange;
574
+ setPaymentMethodActive;
575
+ setCurrencyMinRate;
576
+ addCurrencies;
577
+ deactivateCurrency;
578
+ addPaymentMethods;
579
+ /**
580
+ * Create a deposit with human-readable params.
581
+ *
582
+ * Handles all encoding internally:
583
+ * 1. Registers payee details with the API (so buyers can discover them)
584
+ * 2. Hashes provider keys and currency codes to bytes32
585
+ * 3. Expands sub-providers (e.g., 'zelle' → zelle-chase, zelle-bofa, zelle-citi)
586
+ * 4. Encodes gating service witness signers
587
+ * 5. Submits the on-chain transaction
588
+ *
589
+ * @example
590
+ * ```ts
591
+ * await client.createDeposit({
592
+ * token: USDC_ADDRESS,
593
+ * amount: 1000_000000n,
594
+ * intentRange: { min: 10_000000n, max: 500_000000n },
595
+ * paymentMethods: [
596
+ * { provider: 'venmo', payeeId: '@myvenmo', currencies: [{ code: 'USD', minRate: 0n }] },
597
+ * ],
598
+ * retainOnEmpty: true,
599
+ * })
600
+ * ```
601
+ */
602
+ async createDeposit(params) {
603
+ await Promise.all(
604
+ params.paymentMethods.map(
605
+ (pm) => this.registerMaker({
606
+ providerKey: pm.provider,
607
+ providerId: pm.payeeId,
608
+ telegramHandle: params.telegramHandle
609
+ }).catch((err) => {
610
+ console.warn(`[ProveXClient] Failed to register maker for ${pm.provider}:`, err);
611
+ })
612
+ )
613
+ );
614
+ const rawParams = this.buildCreateDepositRawParams(params);
615
+ return this.createDepositRaw(rawParams);
616
+ }
617
+ /**
618
+ * Prepare an unsigned createDeposit transaction (no API call, no wallet).
619
+ *
620
+ * Use this for smart accounts, multisig, gasless relayers, or offline signing.
621
+ * Maker registration is NOT performed — call `registerMaker()` separately
622
+ * if you need buyers to discover the payee.
623
+ */
624
+ async prepareCreateDeposit(params) {
625
+ return this.createDepositRaw.prepare(this.buildCreateDepositRawParams(params));
626
+ }
627
+ /**
628
+ * Convert human-readable deposit params to raw contract params.
629
+ * Handles provider expansion, hashing, gating service encoding, and defaults.
630
+ */
631
+ buildCreateDepositRawParams(params) {
632
+ const expanded = [];
633
+ const configs = payment.providerConfigs(this.chainId);
634
+ for (const pm of params.paymentMethods) {
635
+ const hash = payment.computePayeeDetailsHash(pm.payeeId);
636
+ const config = configs[pm.provider];
637
+ if (config?.subProviders && config.subProviders.size > 0) {
638
+ for (const sub of config.subProviders) {
639
+ expanded.push({
640
+ providerKey: sub.key,
641
+ payeeDetailsHash: hash,
642
+ currencies: pm.currencies,
643
+ intentGatingService: pm.intentGatingService,
644
+ data: pm.data
645
+ });
646
+ }
647
+ } else {
648
+ expanded.push({
649
+ providerKey: pm.provider,
650
+ payeeDetailsHash: hash,
651
+ currencies: pm.currencies,
652
+ intentGatingService: pm.intentGatingService,
653
+ data: pm.data
654
+ });
655
+ }
656
+ }
657
+ const defaultGatingData = viem.encodeAbiParameters(
658
+ viem.parseAbiParameters(["address[]"]),
659
+ [[contracts.reclaimSigners.peerWitnessSigner, contracts.reclaimSigners.reclaimWitnessSigner]]
660
+ );
661
+ const defaultGatingService = contracts.getLatestContract(this.chainId, "gatingService", "v3") ?? viem.zeroAddress;
662
+ return {
663
+ token: params.token,
664
+ amount: params.amount,
665
+ intentAmountRange: {
666
+ min: params.intentRange?.min ?? 1000000n,
667
+ max: params.intentRange?.max ?? params.amount
668
+ },
669
+ paymentMethods: expanded.map((p) => payment.providerKeyToContractId(p.providerKey)),
670
+ paymentMethodData: expanded.map((p) => ({
671
+ intentGatingService: p.intentGatingService ?? defaultGatingService,
672
+ payeeDetails: p.payeeDetailsHash,
673
+ data: p.data ?? defaultGatingData
674
+ })),
675
+ currencies: expanded.map(
676
+ (p) => p.currencies.map((c) => ({
677
+ code: currencies.tickerToContractId(c.code),
678
+ minConversionRate: c.minRate
679
+ }))
680
+ ),
681
+ delegate: params.delegate ?? viem.zeroAddress,
682
+ intentGuardian: params.intentGuardian ?? viem.zeroAddress,
683
+ retainOnEmpty: params.retainOnEmpty ?? false
684
+ };
685
+ }
686
+ // ─── Orchestrator Writes ─────────────────────────────────────────────────
687
+ cancelIntent;
688
+ releaseFundsToPayer;
689
+ /**
690
+ * Signal an intent to buy tokens from a deposit (buyer side).
691
+ *
692
+ * Handles the full multi-step flow:
693
+ * 1. Fetches payee details hash from the indexer
694
+ * 2. Requests a gating service signature from the API
695
+ * 3. Simulates the transaction
696
+ * 4. Submits to Orchestrator.signalIntent()
697
+ * 5. Parses the IntentSignaled event to extract the intent hash
698
+ *
699
+ * @returns Transaction result with the on-chain intent hash.
700
+ * @throws {ProveXError} VALIDATION_ERROR if payee details or gating signature missing.
701
+ * @throws {ProveXError} API_ERROR if gating service rejects the intent.
702
+ * @throws {ProveXError} WALLET_REJECTED if user cancels in wallet.
703
+ *
704
+ * @example
705
+ * ```ts
706
+ * const { hash, intentHash } = await client.signalIntent({
707
+ * deposit: { escrow: '0x...', localId: 1n },
708
+ * paymentMethod: providerKeyToContractId('venmo'),
709
+ * tokenAmount: 100_000000n,
710
+ * toAddress: wallet.address,
711
+ * fiatCurrencyCode: tickerToContractId('USD'),
712
+ * conversionRate: 1_000000000000000000n,
713
+ * })
714
+ * ```
715
+ */
716
+ async signalIntent(params) {
717
+ const wallet = this.requireWallet();
718
+ const orchestrator = await this.getOrchestratorAddress();
719
+ const indexer = this.requireIndexer();
720
+ const payeeDetailsHash = await indexer.getPayeeDetailsHash({
721
+ escrow: params.deposit.escrow,
722
+ localId: params.deposit.localId,
723
+ chainId: this.chainId,
724
+ paymentMethodId: params.paymentMethod
725
+ });
726
+ if (!payeeDetailsHash) {
727
+ throw new ProveXError("VALIDATION_ERROR", "No payee details found for this deposit and payment method.");
728
+ }
729
+ const gatingResponse = await this.requestGatingSignature({
730
+ processorName: params.subProvider ?? "",
731
+ depositId: params.deposit.localId.toString(),
732
+ amount: params.tokenAmount.toString(),
733
+ payeeDetails: payeeDetailsHash,
734
+ toAddress: params.toAddress,
735
+ paymentMethod: params.paymentMethod,
736
+ fiatCurrency: params.fiatCurrencyCode,
737
+ conversionRate: params.conversionRate.toString(),
738
+ chainId: this.chainId.toString(),
739
+ orchestratorAddress: orchestrator,
740
+ escrowAddress: params.deposit.escrow
741
+ });
742
+ const rawParams = {
743
+ escrow: params.deposit.escrow,
744
+ depositId: params.deposit.localId,
745
+ amount: params.tokenAmount,
746
+ to: params.toAddress,
747
+ paymentMethod: params.paymentMethod,
748
+ fiatCurrency: params.fiatCurrencyCode,
749
+ conversionRate: params.conversionRate,
750
+ referrer: viem.zeroAddress,
751
+ referrerFee: 0n,
752
+ gatingServiceSignature: gatingResponse.signature,
753
+ signatureExpiration: gatingResponse.expiration,
754
+ postIntentHook: viem.zeroAddress,
755
+ data: "0x"
756
+ };
757
+ const publicClient2 = this.getPublicClient();
758
+ try {
759
+ await publicClient2.simulateContract({
760
+ address: orchestrator,
761
+ abi: abis.OrchestratorAbi,
762
+ functionName: "signalIntent",
763
+ args: [rawParams],
764
+ account: params.toAddress
765
+ });
766
+ } catch (simError) {
767
+ throw new ProveXError("CONTRACT_ERROR", simError.message ?? "Signal intent simulation failed", simError);
768
+ }
769
+ const gasInputs = await getTransactionGasInputs(this.chainId);
770
+ const txData = viem.encodeFunctionData({
771
+ abi: abis.OrchestratorAbi,
772
+ functionName: "signalIntent",
773
+ args: [rawParams]
774
+ });
775
+ let txHash;
776
+ try {
777
+ txHash = await wallet.sendTransaction({
778
+ to: orchestrator,
779
+ data: txData,
780
+ chainId: this.chainId,
781
+ ...gasInputs
782
+ });
783
+ } catch (err) {
784
+ if (isUserRejectionError(err)) {
785
+ throw new ProveXError("WALLET_REJECTED", "Transaction was rejected in wallet.", err);
786
+ }
787
+ throw new ProveXError("CONTRACT_ERROR", err.message ?? "Transaction failed", err);
788
+ }
789
+ this.onTransactionHash?.(txHash);
790
+ const receipt = await publicClient2.waitForTransactionReceipt({ hash: txHash });
791
+ let intentHash = null;
792
+ for (const log of receipt.logs) {
793
+ try {
794
+ const decoded = viem.decodeEventLog({
795
+ abi: abis.OrchestratorAbi,
796
+ data: log.data,
797
+ topics: log.topics
798
+ });
799
+ if (decoded.eventName === "IntentSignaled") {
800
+ intentHash = decoded.args.intentHash;
801
+ break;
802
+ }
803
+ } catch {
804
+ }
805
+ }
806
+ if (!intentHash) {
807
+ throw new ProveXError("CONTRACT_ERROR", "Transaction succeeded but IntentSignaled event not found in receipt.");
808
+ }
809
+ if (this.indexer) {
810
+ await checkTransactionIndexed({
811
+ queryTransaction: this.indexer.getTransactionByHash,
812
+ hash: txHash,
813
+ onSlowSync: this.onSlowSync
814
+ });
815
+ }
816
+ return { hash: txHash, receipt, intentHash };
817
+ }
818
+ /**
819
+ * Request a gating service signature for intent signaling.
820
+ * The gating service validates the intent and returns a signature
821
+ * the Orchestrator contract verifies on-chain.
822
+ */
823
+ async requestGatingSignature(params) {
824
+ const res = await this.apiPost("/api/v0/attestation/verify/intent", params);
825
+ if (!res.success) {
826
+ throw new ProveXError("API_ERROR", res.message ?? "Gating service rejected the intent.");
827
+ }
828
+ const signature = res.responseObject.intentData?.gatingServiceSignature ?? res.responseObject.signedIntent;
829
+ if (!signature) {
830
+ throw new ProveXError("API_ERROR", "No signature received from gating service.");
831
+ }
832
+ const expiration = res.responseObject.signatureExpiration ? BigInt(res.responseObject.signatureExpiration) : BigInt(Math.floor(Date.now() / 1e3) + 3600);
833
+ return { signature, expiration };
834
+ }
835
+ // ─── ERC20 ───────────────────────────────────────────────────────────────
836
+ approve;
837
+ // ─── API Methods (Maker Registration, Attestation) ──────────────────────
838
+ /** Get payee details for a provider and user ID. */
839
+ async getPayeeInfo(params) {
840
+ try {
841
+ const res = await fetch(`${this.apiUrl}/api/v0/makers/${params.provider}/${params.userId}`);
842
+ if (!res.ok) return null;
843
+ const json = await res.json();
844
+ return json.responseObject ?? null;
845
+ } catch {
846
+ return null;
847
+ }
848
+ }
849
+ /** Validate a maker's payment identity before registration. */
850
+ async validateMaker(params) {
851
+ const res = await this.apiPost("/api/v0/makers/validate", {
852
+ processorName: params.providerKey,
853
+ depositData: {
854
+ [params.providerKey]: params.providerId,
855
+ telegramUsername: params.telegramHandle ?? ""
856
+ }
857
+ });
858
+ return res.responseObject ?? false;
859
+ }
860
+ /** Register a maker's payment identity (e.g., Venmo username). Required before creating deposits. */
861
+ async registerMaker(params) {
862
+ const res = await this.apiPost("/api/v0/makers/create", {
863
+ processorName: params.providerKey,
864
+ depositData: {
865
+ [params.providerKey]: params.providerId,
866
+ telegramUsername: params.telegramHandle ?? ""
867
+ }
868
+ });
869
+ return res.responseObject ?? null;
870
+ }
871
+ /**
872
+ * Submit zkTLS proofs and get a signed attestation for fulfillIntent.
873
+ * This is the proof verification step — after the buyer makes payment and
874
+ * generates proofs via the browser extension.
875
+ */
876
+ async getAttestation(params) {
877
+ const endpoint = `/api/v0/attestation/verify/${params.platform}/${params.actionType}`;
878
+ const proof = params.proofs.length === 1 ? params.proofs[0] : params.proofs;
879
+ const res = await fetch(`${this.apiUrl}${endpoint}`, {
880
+ method: "POST",
881
+ headers: { "Content-Type": "application/json" },
882
+ body: JSON.stringify({
883
+ proofType: "reclaim",
884
+ proof: JSON.stringify(proof),
885
+ chainId: params.chainId,
886
+ verifyingContract: params.verifyingContract,
887
+ intent: params.intent
888
+ })
889
+ });
890
+ if (!res.ok) {
891
+ let errorMessage = `Attestation error: ${res.status}`;
892
+ try {
893
+ const errorJson = await res.json();
894
+ if (errorJson.message) errorMessage = errorJson.message;
895
+ } catch {
896
+ }
897
+ return {
898
+ success: false,
899
+ message: errorMessage,
900
+ statusCode: res.status,
901
+ responseObject: {}
902
+ };
903
+ }
904
+ return await res.json();
905
+ }
906
+ /** Make a POST request to the API. */
907
+ async apiPost(path, body) {
908
+ const res = await fetch(`${this.apiUrl}${path}`, {
909
+ method: "POST",
910
+ headers: { "Content-Type": "application/json" },
911
+ body: JSON.stringify(body)
912
+ });
913
+ if (!res.ok) {
914
+ throw new ProveXError("API_ERROR", `API request failed: ${res.status}`);
915
+ }
916
+ return await res.json();
917
+ }
918
+ // ─── Indexer Sync ────────────────────────────────────────────────────────
919
+ async waitForTransactionIndexed(hash) {
920
+ const indexer = this.requireIndexer();
921
+ await checkTransactionIndexed({
922
+ queryTransaction: indexer.getTransactionByHash,
923
+ hash,
924
+ onSlowSync: this.onSlowSync
925
+ });
926
+ }
927
+ // ─── Internal Helpers ────────────────────────────────────────────────────
928
+ /** Get or create a viem PublicClient for this chain. Cached after first use. */
929
+ getPublicClient() {
930
+ if (!this._publicClient) {
931
+ this._publicClient = viem.createPublicClient({ chain: this.chain, transport: viem.http() });
932
+ }
933
+ return this._publicClient;
934
+ }
935
+ /** Read from a contract using wallet adapter or public RPC. */
936
+ async readContract(params) {
937
+ if (this.wallet?.readContract) {
938
+ return this.wallet.readContract({ ...params, chainId: this.chainId });
939
+ }
940
+ return this.getPublicClient().readContract(params);
941
+ }
942
+ /** Require a connected wallet, or throw. */
943
+ requireWallet() {
944
+ if (!this.wallet) throw new ProveXError("WALLET_NOT_CONNECTED", "No wallet adapter provided. Pass `wallet` to createProveXClient().");
945
+ if (!this.wallet.address) throw new ProveXError("WALLET_NOT_CONNECTED", "Wallet is not connected.");
946
+ return this.wallet;
947
+ }
948
+ /**
949
+ * Build a WritableMethod from an ABI definition.
950
+ * The returned function supports both execute (full lifecycle) and .prepare() (unsigned tx).
951
+ */
952
+ buildWritableMethod({
953
+ abi,
954
+ contractResolver,
955
+ functionName,
956
+ argsMapper
957
+ }) {
958
+ const prepare = async (params) => {
959
+ const to = await contractResolver(params);
960
+ const args = argsMapper(params);
961
+ const data = viem.encodeFunctionData({
962
+ abi,
963
+ functionName,
964
+ args
965
+ });
966
+ return { to, data, value: 0n, chainId: this.chainId };
967
+ };
968
+ const execute = async (params) => {
969
+ const wallet = this.requireWallet();
970
+ const prepared = await prepare(params);
971
+ const gasInputs = await getTransactionGasInputs(this.chainId);
972
+ let hash;
973
+ try {
974
+ hash = await wallet.sendTransaction({ ...prepared, ...gasInputs });
975
+ } catch (err) {
976
+ if (isUserRejectionError(err)) {
977
+ throw new ProveXError("WALLET_REJECTED", "Transaction was rejected in wallet.", err);
978
+ }
979
+ throw new ProveXError("CONTRACT_ERROR", err.message ?? "Transaction failed", err);
980
+ }
981
+ this.onTransactionHash?.(hash);
982
+ const receipt = await this.getPublicClient().waitForTransactionReceipt({ hash });
983
+ if (this.indexer) {
984
+ await checkTransactionIndexed({
985
+ queryTransaction: this.indexer.getTransactionByHash,
986
+ hash,
987
+ onSlowSync: this.onSlowSync
988
+ });
989
+ }
990
+ return { hash, receipt };
991
+ };
992
+ execute.prepare = prepare;
993
+ return execute;
994
+ }
995
+ };
996
+ function createProveXClient(config) {
997
+ return new ProveXClient(config);
998
+ }
999
+
1000
+ // src/hooks/useSignalIntent.ts
1001
+ var ERROR_PATTERNS2 = [
1002
+ { pattern: /user rejected|user denied|rejected the request|user cancelled|user canceled/i, message: "Transaction was cancelled." },
1003
+ { pattern: /InsufficientDepositLiquidity|0xc3df48f3/i, message: "This deposit no longer has enough available liquidity. Please try a smaller amount or a different deposit." },
1004
+ { pattern: /execution reverted/i, message: "Transaction failed on-chain. The order may no longer be available." },
1005
+ { pattern: /insufficient funds/i, message: "Insufficient funds for gas fees." },
1006
+ { pattern: /timeout|etimedout/i, message: "Request timed out. Please try again." }
1007
+ ];
1008
+ function getUserFriendlyErrorMessage2(error) {
1009
+ const raw = error instanceof Error ? error.message : String(error);
1010
+ for (const { pattern, message } of ERROR_PATTERNS2) {
1011
+ if (pattern.test(raw)) return message;
1012
+ }
1013
+ return "Something went wrong. Please try again.";
1014
+ }
1015
+ function useSignalIntent({
1016
+ wallet,
1017
+ onSuccess,
1018
+ onFailure,
1019
+ deposit,
1020
+ refetchDeposits
1021
+ }) {
1022
+ const provex = useOptionalProvex();
1023
+ const [status, setStatus] = react.useState("input");
1024
+ const [message, setMessage] = react.useState(null);
1025
+ const client = react.useMemo(() => {
1026
+ if (!provex) return null;
1027
+ return createProveXClient({
1028
+ chain: provex.config.chain,
1029
+ apiUrl: provex.config.apiUrl,
1030
+ wallet,
1031
+ indexer: provex.indexer
1032
+ });
1033
+ }, [provex, wallet]);
1034
+ const isLoading = react.useMemo(() => {
1035
+ return status === "loading_payee" || status === "loading_intent" || status === "prompt_wallet_confirm" || status === "writing_intent";
1036
+ }, [status]);
1037
+ const startOrder = react.useCallback(async ({
1038
+ paymentMethod,
1039
+ depositId,
1040
+ tokenAmount,
1041
+ toAddress,
1042
+ fiatCurrencyCode,
1043
+ conversionRate,
1044
+ subProvider
1045
+ }) => {
1046
+ if (!client || !deposit) {
1047
+ setStatus("error");
1048
+ setMessage("Client or deposit not available");
1049
+ return;
1050
+ }
1051
+ const escrow = deposit.escrow;
1052
+ if (!escrow) {
1053
+ setStatus("error");
1054
+ setMessage("No escrow address found");
1055
+ return;
1056
+ }
1057
+ setStatus("loading_intent");
1058
+ try {
1059
+ const result = await client.signalIntent({
1060
+ deposit: { escrow, localId: deposit.localId ?? depositId },
1061
+ paymentMethod,
1062
+ tokenAmount,
1063
+ toAddress,
1064
+ fiatCurrencyCode,
1065
+ conversionRate,
1066
+ subProvider
1067
+ });
1068
+ setStatus("success");
1069
+ setMessage("Intent signaled successfully");
1070
+ onSuccess(result.intentHash);
1071
+ } catch (error) {
1072
+ if (error instanceof ProveXError && error.isRejection) {
1073
+ setStatus("input");
1074
+ setMessage(null);
1075
+ return;
1076
+ }
1077
+ console.error("Failed to signal intent", error);
1078
+ setStatus("error");
1079
+ setMessage(getUserFriendlyErrorMessage2(error));
1080
+ refetchDeposits?.();
1081
+ onFailure?.();
1082
+ }
1083
+ }, [client, deposit, onSuccess, onFailure, refetchDeposits]);
1084
+ return {
1085
+ startOrder,
1086
+ status,
1087
+ message,
1088
+ isLoading
1089
+ };
1090
+ }
1091
+ function useReputation({
1092
+ address,
1093
+ chainId
1094
+ }) {
1095
+ const { indexer } = useProvex();
1096
+ const queryClient = reactQuery.useQueryClient();
1097
+ const isBase = chainId === chains.base.id;
1098
+ const reputationQuery = reactQuery.useQuery({
1099
+ queryKey: ["provex", "reputation", address, isBase ? chains.base.id : "all"],
1100
+ queryFn: async () => {
1101
+ if (!address) return null;
1102
+ return indexer.getUserReputation({
1103
+ address,
1104
+ chainId: isBase ? chains.base.id : void 0
1105
+ });
1106
+ },
1107
+ enabled: !!address,
1108
+ staleTime: 6e4,
1109
+ refetchInterval: 12e4
1110
+ });
1111
+ const reputation$1 = react.useMemo(() => {
1112
+ const data = reputationQuery.data;
1113
+ if (!data) return reputation.DEFAULT_REPUTATION;
1114
+ let tier = reputation.getTierFromVolume(data.fulfilledVolumeUsdc);
1115
+ const lockScore = data.lateCancellations * 50;
1116
+ const dilutedLockScore = data.fulfilledCount > 0 ? lockScore / (1 + data.fulfilledCount * 0.1) : lockScore;
1117
+ let tierPenalty = 0;
1118
+ if (dilutedLockScore >= 1e3) tierPenalty = 4;
1119
+ else if (dilutedLockScore >= 500) tierPenalty = 3;
1120
+ else if (dilutedLockScore >= 200) tierPenalty = 2;
1121
+ else if (dilutedLockScore >= 50) tierPenalty = 1;
1122
+ if (tierPenalty > 0) {
1123
+ const currentTierIndex = reputation.TIER_ORDER.indexOf(tier);
1124
+ const newTierIndex = Math.max(0, currentTierIndex - tierPenalty);
1125
+ tier = reputation.TIER_ORDER[newTierIndex] ?? "peasant";
1126
+ }
1127
+ const cooldownHours = reputation.TAKER_TIERS[tier].cooldownHours;
1128
+ let cooldownEndsAt = null;
1129
+ let isOnCooldown = false;
1130
+ const baseTimestamp = data.lastSignaledAt[chains.base.id] ?? null;
1131
+ const pulseTimestamp = data.lastSignaledAt[369] ?? null;
1132
+ const candidateTimestamps = isBase ? [baseTimestamp] : [baseTimestamp, pulseTimestamp];
1133
+ const lastSignaledTimestamp = candidateTimestamps.filter((ts) => ts !== null).sort((a, b) => Number(b) - Number(a))[0] ?? null;
1134
+ if (cooldownHours > 0 && lastSignaledTimestamp) {
1135
+ const lastSignaledTime = Number(lastSignaledTimestamp) * 1e3;
1136
+ const cooldownMs = cooldownHours * 60 * 60 * 1e3;
1137
+ cooldownEndsAt = new Date(lastSignaledTime + cooldownMs);
1138
+ isOnCooldown = cooldownEndsAt > /* @__PURE__ */ new Date();
1139
+ }
1140
+ return {
1141
+ tier,
1142
+ fulfilledVolumeUsdc: data.fulfilledVolumeUsdc,
1143
+ lockScore: dilutedLockScore,
1144
+ cooldownEndsAt,
1145
+ isOnCooldown
1146
+ };
1147
+ }, [reputationQuery.data, isBase]);
1148
+ const hasLoadedData = reputationQuery.data !== void 0;
1149
+ const isLoading = reputationQuery.isLoading && !hasLoadedData;
1150
+ const refetch = react.useCallback(() => {
1151
+ queryClient.invalidateQueries({
1152
+ queryKey: ["provex", "reputation", address]
1153
+ });
1154
+ }, [queryClient, address]);
1155
+ return {
1156
+ reputation: reputation$1,
1157
+ isLoading,
1158
+ error: reputationQuery.error,
1159
+ refetch
1160
+ };
1161
+ }
1162
+ function getTierDisplayInfo(tier) {
1163
+ const config = reputation.TAKER_TIERS[tier];
1164
+ const colors = {
1165
+ [payment.tiers.PEASANT]: "gray",
1166
+ [payment.tiers.NEUTRAL]: "blue",
1167
+ [payment.tiers.PLUS]: "green",
1168
+ [payment.tiers.PRO]: "purple",
1169
+ [payment.tiers.PLATINUM]: "yellow",
1170
+ [payment.tiers.PRESIDENT]: "cyan"
1171
+ };
1172
+ return {
1173
+ name: config.name,
1174
+ baseCap: config.baseCap,
1175
+ cooldownHours: config.cooldownHours,
1176
+ color: colors[tier],
1177
+ volumeRangeDisplay: reputation.getVolumeRangeDisplay(tier)
1178
+ };
1179
+ }
1180
+ function useReputationLimits({
1181
+ chainId,
1182
+ selectedPaymentMethod,
1183
+ tier,
1184
+ amountInInt,
1185
+ cooldownEndsAt
1186
+ }) {
1187
+ const hasLimits = true;
1188
+ const { cooldownRemaining, isOnCooldown } = react.useMemo(() => {
1189
+ const providerCooldownHours = reputation.getCooldownHours({ tier, providerKey: selectedPaymentMethod }) ;
1190
+ const providerHasCooldown = providerCooldownHours > 0;
1191
+ if (!cooldownEndsAt || !providerHasCooldown) {
1192
+ return { cooldownRemaining: null, isOnCooldown: false };
1193
+ }
1194
+ const remainingMs = cooldownEndsAt.getTime() - Date.now();
1195
+ if (remainingMs <= 0) {
1196
+ return { cooldownRemaining: null, isOnCooldown: false };
1197
+ }
1198
+ const hours = Math.floor(remainingMs / (1e3 * 60 * 60));
1199
+ const minutes = Math.floor(remainingMs % (1e3 * 60 * 60) / (1e3 * 60));
1200
+ const formatted = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
1201
+ return { cooldownRemaining: formatted, isOnCooldown: true };
1202
+ }, [cooldownEndsAt, hasLimits, tier, selectedPaymentMethod]);
1203
+ const noCooldownProviders = react.useMemo(() => {
1204
+ const available = payment.verifiablePaymentMethods(chainId);
1205
+ return available.filter((key) => {
1206
+ if (key === selectedPaymentMethod) return false;
1207
+ const platformRisk = payment.getPlatformRisk(chainId, key);
1208
+ if (!platformRisk.hasCooldown) return false;
1209
+ return reputation.getCooldownHours({ tier, providerKey: key }) === 0;
1210
+ });
1211
+ }, [hasLimits, chainId, selectedPaymentMethod, tier]);
1212
+ const selectedProviderCooldownHours = react.useMemo(() => {
1213
+ return reputation.getCooldownHours({ tier, providerKey: selectedPaymentMethod });
1214
+ }, [hasLimits, tier, selectedPaymentMethod]);
1215
+ const effectiveCap = react.useMemo(() => {
1216
+ return reputation.getEffectiveCap({ tier, providerKey: selectedPaymentMethod });
1217
+ }, [hasLimits, tier, selectedPaymentMethod]);
1218
+ const amountExceedsCap = react.useMemo(() => {
1219
+ if (effectiveCap === null) return false;
1220
+ if (!amountInInt) return false;
1221
+ const amountUsd = Number(amountInInt) / 1e6;
1222
+ return amountUsd > effectiveCap;
1223
+ }, [effectiveCap, amountInInt]);
1224
+ const capExceededMessage = react.useMemo(() => {
1225
+ if (!amountExceedsCap || effectiveCap === null) return null;
1226
+ const providerName = payment.providerConfigs(chainId)[selectedPaymentMethod]?.name ?? selectedPaymentMethod;
1227
+ return `Amount exceeds your $${effectiveCap.toLocaleString()} limit for ${providerName}`;
1228
+ }, [amountExceedsCap, effectiveCap, chainId, selectedPaymentMethod]);
1229
+ return {
1230
+ hasLimits,
1231
+ noCooldownProviders,
1232
+ selectedProviderCooldownHours,
1233
+ cooldownRemaining,
1234
+ isOnCooldown,
1235
+ effectiveCap,
1236
+ amountExceedsCap,
1237
+ capExceededMessage
1238
+ };
1239
+ }
1240
+ var intentStatuses = {
1241
+ pending: "pending",
1242
+ signalled: "signalled",
1243
+ fulfilled: "fulfilled",
1244
+ pruned: "pruned"
1245
+ };
1246
+ function usePayeeDetails({
1247
+ intentHash,
1248
+ chainId
1249
+ }) {
1250
+ const { indexer, apiClient } = useProvex();
1251
+ const queryClient = reactQuery.useQueryClient();
1252
+ const intentQuery = reactQuery.useQuery({
1253
+ queryKey: ["provex", "intent", intentHash, chainId],
1254
+ queryFn: () => indexer.getIntent({ intentHash, chainId }),
1255
+ enabled: !!intentHash,
1256
+ staleTime: Infinity,
1257
+ refetchInterval: false
1258
+ });
1259
+ const indexedIntent = intentQuery.data ?? null;
1260
+ const intentStatus = react.useMemo(() => {
1261
+ if (!indexedIntent) return null;
1262
+ return indexedIntent.status;
1263
+ }, [indexedIntent]);
1264
+ const intent = react.useMemo(() => {
1265
+ if (!indexedIntent) return null;
1266
+ return {
1267
+ owner: indexedIntent.ownerAddress,
1268
+ to: indexedIntent.toAddress,
1269
+ escrow: indexedIntent.escrow,
1270
+ depositId: indexedIntent.depositLocalId,
1271
+ amount: indexedIntent.amount,
1272
+ timestamp: indexedIntent.timestamp,
1273
+ paymentMethod: indexedIntent.paymentMethodId,
1274
+ fiatCurrency: indexedIntent.fiatCurrency,
1275
+ conversionRate: indexedIntent.conversionRate
1276
+ };
1277
+ }, [indexedIntent]);
1278
+ const expiryTime = indexedIntent?.expiryTime ?? null;
1279
+ const depositQuery = reactQuery.useQuery({
1280
+ queryKey: ["provex", "deposit", indexedIntent?.escrow, indexedIntent?.depositLocalId, chainId],
1281
+ queryFn: () => indexer.getDepositWithVerifiers({
1282
+ escrow: indexedIntent.escrow,
1283
+ localId: indexedIntent.depositLocalId,
1284
+ chainId
1285
+ }),
1286
+ enabled: !!indexedIntent,
1287
+ staleTime: Infinity
1288
+ });
1289
+ const deposit = depositQuery.data ?? null;
1290
+ const providerKey = react.useMemo(() => {
1291
+ if (!deposit || !indexedIntent) return null;
1292
+ return payment.verifierGetsKey(deposit.chainId, indexedIntent.paymentMethodId);
1293
+ }, [deposit, indexedIntent]);
1294
+ const userId = react.useMemo(() => {
1295
+ if (!deposit || !providerKey) return null;
1296
+ const verifier = deposit.verifiers.find(
1297
+ (v) => payment.verifierGetsKey(deposit.chainId, v.paymentMethodId) === providerKey
1298
+ );
1299
+ return verifier?.payeeDetailsHash ?? null;
1300
+ }, [deposit, providerKey]);
1301
+ const apiProviderKey = react.useMemo(() => {
1302
+ if (!providerKey) return null;
1303
+ if (payment.isSubProviderKey(providerKey)) {
1304
+ return payment.getParentProviderKey(providerKey);
1305
+ }
1306
+ return providerKey;
1307
+ }, [providerKey]);
1308
+ const payeeDetailsQuery = reactQuery.useQuery({
1309
+ queryKey: ["provex", "payeeDetails", apiProviderKey, userId],
1310
+ queryFn: async () => {
1311
+ if (!userId || !apiProviderKey) return null;
1312
+ try {
1313
+ const response = await apiClient.get(
1314
+ `/api/v1/makers/${apiProviderKey}/${userId}`
1315
+ );
1316
+ return response.responseObject ?? null;
1317
+ } catch {
1318
+ return null;
1319
+ }
1320
+ },
1321
+ enabled: !!userId && !!apiProviderKey,
1322
+ staleTime: Infinity,
1323
+ retry: false
1324
+ });
1325
+ const payeeDetails = payeeDetailsQuery.data ?? null;
1326
+ const isFetching = intentQuery.isFetching || depositQuery.isFetching;
1327
+ const refetch = react.useCallback(() => {
1328
+ queryClient.invalidateQueries({ queryKey: ["provex", "intent", intentHash] });
1329
+ }, [queryClient, intentHash]);
1330
+ const refetchAll = react.useCallback(async () => {
1331
+ const [intentResult] = await Promise.all([
1332
+ intentQuery.refetch(),
1333
+ depositQuery.refetch(),
1334
+ payeeDetailsQuery.refetch()
1335
+ ]);
1336
+ const updated = intentResult.data;
1337
+ if (!updated) return null;
1338
+ return updated.status;
1339
+ }, [intentQuery, depositQuery, payeeDetailsQuery]);
1340
+ return react.useMemo(() => ({
1341
+ intentStatus,
1342
+ isFetching,
1343
+ isValid: !!intent,
1344
+ intent,
1345
+ providerKey,
1346
+ userId,
1347
+ payeeDetails,
1348
+ deposit,
1349
+ expiryTime,
1350
+ isV2Intent: false,
1351
+ // Adapter only returns V3 intents
1352
+ isPruned: indexedIntent?.status === "pruned",
1353
+ prunedAt: indexedIntent?.prunedAt ?? null,
1354
+ isFulfilled: indexedIntent?.status === "fulfilled",
1355
+ refetch,
1356
+ refetchAll
1357
+ }), [
1358
+ intentStatus,
1359
+ isFetching,
1360
+ intent,
1361
+ providerKey,
1362
+ userId,
1363
+ payeeDetails,
1364
+ deposit,
1365
+ expiryTime,
1366
+ indexedIntent,
1367
+ refetch,
1368
+ refetchAll
1369
+ ]);
1370
+ }
1371
+ var FEE_CACHE_STALE_TIME = 60 * 60 * 1e3;
1372
+ function useProtocolFees({
1373
+ chainId,
1374
+ escrowAddress,
1375
+ enabled = true
1376
+ }) {
1377
+ const {
1378
+ data: orchestratorAddress,
1379
+ isLoading: isLoadingOrchestrator,
1380
+ isError: isErrorOrchestrator,
1381
+ error: errorOrchestrator,
1382
+ refetch: refetchOrchestrator
1383
+ } = wagmi.useReadContract({
1384
+ address: escrowAddress,
1385
+ abi: abis.V3EscrowAbi,
1386
+ functionName: "orchestrator",
1387
+ chainId,
1388
+ query: {
1389
+ enabled,
1390
+ staleTime: FEE_CACHE_STALE_TIME,
1391
+ gcTime: FEE_CACHE_STALE_TIME
1392
+ }
1393
+ });
1394
+ const {
1395
+ data: feeData,
1396
+ isLoading: isLoadingFees,
1397
+ isError: isErrorFees,
1398
+ error: errorFees,
1399
+ refetch: refetchFees
1400
+ } = wagmi.useReadContracts({
1401
+ contracts: [
1402
+ {
1403
+ address: orchestratorAddress,
1404
+ abi: abis.OrchestratorAbi,
1405
+ functionName: "protocolFee",
1406
+ chainId
1407
+ },
1408
+ {
1409
+ address: orchestratorAddress,
1410
+ abi: abis.OrchestratorAbi,
1411
+ functionName: "protocolFeeRecipient",
1412
+ chainId
1413
+ }
1414
+ ],
1415
+ query: {
1416
+ enabled: enabled && !!orchestratorAddress,
1417
+ staleTime: FEE_CACHE_STALE_TIME,
1418
+ gcTime: FEE_CACHE_STALE_TIME
1419
+ }
1420
+ });
1421
+ const protocolFeeRaw = feeData?.[0]?.result;
1422
+ const protocolFeeRecipient = feeData?.[1]?.result;
1423
+ const feeInfo = react.useMemo(() => {
1424
+ if (protocolFeeRaw === void 0) return void 0;
1425
+ return fees.normalizeFeePrecision(protocolFeeRaw);
1426
+ }, [protocolFeeRaw]);
1427
+ const isFeeQueryPending = !!orchestratorAddress && protocolFeeRaw === void 0 && !isErrorFees;
1428
+ const isLoading = isLoadingOrchestrator || isLoadingFees || isFeeQueryPending;
1429
+ const isError = isErrorOrchestrator || isErrorFees;
1430
+ const error = errorOrchestrator || errorFees || null;
1431
+ const refetch = () => {
1432
+ refetchOrchestrator();
1433
+ refetchFees();
1434
+ };
1435
+ return {
1436
+ protocolFeeRaw,
1437
+ feeInfo,
1438
+ protocolFeeRecipient,
1439
+ orchestratorAddress,
1440
+ isLoading,
1441
+ isError,
1442
+ error,
1443
+ refetch
1444
+ };
1445
+ }
1446
+ function useProtocolFeePercentage(params) {
1447
+ const { feeInfo } = useProtocolFees(params);
1448
+ return feeInfo?.percentage;
1449
+ }
1450
+
1451
+ // src/useProvexBuy.ts
1452
+ function parseAmountSafe(value, decimals) {
1453
+ if (!value || value === "." || value === "0.") return null;
1454
+ try {
1455
+ const parsed = units.parseUnits(value, { decimals });
1456
+ return parsed > 0n ? parsed : null;
1457
+ } catch {
1458
+ return null;
1459
+ }
1460
+ }
1461
+ function useProvexBuy({
1462
+ wallet,
1463
+ onIntentSignaled,
1464
+ onComplete,
1465
+ paymentMethods: allowedPaymentMethods
1466
+ }) {
1467
+ const { config } = useProvex();
1468
+ const [phase, setPhase] = react.useState("browse");
1469
+ const [amount, setAmountRaw] = react.useState("");
1470
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = react.useState("");
1471
+ const [selectedDeposit, setSelectedDeposit] = react.useState(null);
1472
+ const [intentHash, setIntentHash] = react.useState(null);
1473
+ const [errorMessage, setErrorMessage] = react.useState(null);
1474
+ const chainId = config.chain?.id ?? config.chainId;
1475
+ const token = react.useMemo(() => tokens.getDefaultToken(chainId), [chainId]);
1476
+ const currency = currencies.SUPPORTED_CURRENCIES.USD;
1477
+ const availablePaymentMethods = react.useMemo(() => {
1478
+ const all = payment.verifiablePaymentMethods(chainId);
1479
+ if (!allowedPaymentMethods || allowedPaymentMethods.length === 0) return all;
1480
+ return all.filter((pm) => allowedPaymentMethods.includes(pm));
1481
+ }, [chainId, allowedPaymentMethods]);
1482
+ const activePaymentMethod = react.useMemo(() => {
1483
+ if (selectedPaymentMethod) return selectedPaymentMethod;
1484
+ return availablePaymentMethods[0] ?? null;
1485
+ }, [selectedPaymentMethod, availablePaymentMethods]);
1486
+ const depositsResult = useDeposits({
1487
+ token,
1488
+ paymentMethod: activePaymentMethod,
1489
+ currency
1490
+ });
1491
+ const { reputation: reputation$1 } = useReputation({
1492
+ address: wallet.address,
1493
+ chainId
1494
+ });
1495
+ const amountBigInt = react.useMemo(
1496
+ () => parseAmountSafe(amount, currency.decimals),
1497
+ [amount, currency.decimals]
1498
+ );
1499
+ const limits = useReputationLimits({
1500
+ chainId,
1501
+ selectedPaymentMethod: activePaymentMethod,
1502
+ tier: reputation$1.tier,
1503
+ amountInInt: amountBigInt,
1504
+ cooldownEndsAt: reputation$1.cooldownEndsAt
1505
+ });
1506
+ const signalIntent = useSignalIntent({
1507
+ wallet,
1508
+ chainId,
1509
+ deposit: selectedDeposit,
1510
+ onSuccess: (hash) => {
1511
+ setIntentHash(hash);
1512
+ setPhase("committed");
1513
+ onIntentSignaled?.(hash, chainId);
1514
+ },
1515
+ onFailure: () => {
1516
+ setErrorMessage("Order failed. Please try again.");
1517
+ },
1518
+ refetchDeposits: depositsResult.refetch
1519
+ });
1520
+ const escrowAddress = react.useMemo(
1521
+ () => contracts.getLatestContract(chainId, "escrow"),
1522
+ [chainId]
1523
+ );
1524
+ const { feeInfo } = useProtocolFees({
1525
+ chainId,
1526
+ escrowAddress: escrowAddress ?? "0x",
1527
+ enabled: !!escrowAddress
1528
+ });
1529
+ const payeeResult = usePayeeDetails({
1530
+ intentHash,
1531
+ chainId
1532
+ });
1533
+ const refetchAllRef = react.useRef(payeeResult.refetchAll);
1534
+ refetchAllRef.current = payeeResult.refetchAll;
1535
+ react.useEffect(() => {
1536
+ if (phase !== "committed" && phase !== "proving") return;
1537
+ if (!intentHash) return;
1538
+ const interval = setInterval(() => {
1539
+ refetchAllRef.current();
1540
+ }, 5e3);
1541
+ return () => clearInterval(interval);
1542
+ }, [phase, intentHash]);
1543
+ react.useEffect(() => {
1544
+ if (!payeeResult.isFulfilled) return;
1545
+ if (phase === "complete") return;
1546
+ setPhase("complete");
1547
+ if (intentHash) onComplete?.(intentHash, chainId);
1548
+ }, [payeeResult.isFulfilled, phase, intentHash, chainId, onComplete]);
1549
+ react.useEffect(() => {
1550
+ if (!payeeResult.isPruned) return;
1551
+ if (phase === "browse") return;
1552
+ setErrorMessage("Order expired. Please try again.");
1553
+ setPhase("browse");
1554
+ }, [payeeResult.isPruned, phase]);
1555
+ const intentExpiryTime = react.useMemo(() => {
1556
+ if (!payeeResult.expiryTime) return null;
1557
+ return new Date(Number(payeeResult.expiryTime) * 1e3);
1558
+ }, [payeeResult.expiryTime]);
1559
+ const matchableDeposits = react.useMemo(() => {
1560
+ if (!amountBigInt) return [];
1561
+ return depositsResult.getMatchableDeposits(amountBigInt);
1562
+ }, [amountBigInt, depositsResult]);
1563
+ const bestDeposit = matchableDeposits[0] ?? null;
1564
+ const bestRate = react.useMemo(() => {
1565
+ if (!bestDeposit || !activePaymentMethod) return null;
1566
+ const r = getRate({ deposit: bestDeposit, paymentMethod: activePaymentMethod, currency });
1567
+ return r === 0n ? null : r;
1568
+ }, [bestDeposit, activePaymentMethod, currency]);
1569
+ const tokenAmount = react.useMemo(() => {
1570
+ if (!amountBigInt || !bestRate || !token) return null;
1571
+ return conversionRates.convertCurrencyToTokenOutput({
1572
+ token,
1573
+ currency,
1574
+ currencyAmountInt: amountBigInt,
1575
+ rate: bestRate
1576
+ });
1577
+ }, [amountBigInt, bestRate, token, currency]);
1578
+ const rateDisplay = react.useMemo(() => {
1579
+ if (!bestRate || !token) return null;
1580
+ const rateFloat = Number(bestRate) / 1e18;
1581
+ return `1 ${token.symbol} = ${currency.symbol}${rateFloat.toFixed(currency.decimals)}`;
1582
+ }, [bestRate, token, currency]);
1583
+ const tokenAmountDisplay = react.useMemo(() => {
1584
+ if (!tokenAmount || !token) return null;
1585
+ return units.formatUnits(tokenAmount, { decimals: token.decimals });
1586
+ }, [tokenAmount, token]);
1587
+ const canSubmit = react.useMemo(() => {
1588
+ if (!wallet.address) return false;
1589
+ if (!activePaymentMethod) return false;
1590
+ if (!amountBigInt || amountBigInt <= 0n) return false;
1591
+ if (!bestDeposit) return false;
1592
+ if (limits.isOnCooldown) return false;
1593
+ if (limits.amountExceedsCap) return false;
1594
+ if (signalIntent.isLoading) return false;
1595
+ return true;
1596
+ }, [wallet.address, activePaymentMethod, amountBigInt, bestDeposit, limits, signalIntent.isLoading]);
1597
+ const validationMessage = react.useMemo(() => {
1598
+ if (!wallet.address) return "Connect your wallet to continue";
1599
+ if (!activePaymentMethod) return "Select a payment method";
1600
+ if (!amount) return null;
1601
+ if (!amountBigInt || amountBigInt <= 0n) return "Enter a valid amount";
1602
+ if (limits.isOnCooldown && limits.cooldownRemaining) {
1603
+ return `Cooldown active: ${limits.cooldownRemaining} remaining`;
1604
+ }
1605
+ if (limits.capExceededMessage) return limits.capExceededMessage;
1606
+ if (depositsResult.isLoading) return "Loading available orders...";
1607
+ if (!bestDeposit && amount) return "No orders available for this amount";
1608
+ return null;
1609
+ }, [wallet.address, activePaymentMethod, amount, amountBigInt, limits, depositsResult.isLoading, bestDeposit]);
1610
+ const setAmount = react.useCallback((value) => {
1611
+ if (value === "" || /^\d*\.?\d*$/.test(value)) {
1612
+ setAmountRaw(value);
1613
+ setErrorMessage(null);
1614
+ }
1615
+ }, []);
1616
+ const selectDeposit = react.useCallback((deposit) => {
1617
+ setSelectedDeposit(deposit);
1618
+ }, []);
1619
+ const submitOrder = react.useCallback(async () => {
1620
+ if (!canSubmit || !bestDeposit || !tokenAmount || !activePaymentMethod || !wallet.address) return;
1621
+ setSelectedDeposit(bestDeposit);
1622
+ setErrorMessage(null);
1623
+ const paymentMethodContractId = payment.providerKeyToContractId(activePaymentMethod);
1624
+ await signalIntent.startOrder({
1625
+ paymentMethod: paymentMethodContractId,
1626
+ depositId: bestDeposit.localId,
1627
+ tokenAmount,
1628
+ toAddress: wallet.address,
1629
+ fiatCurrencyCode: currency.contractId,
1630
+ conversionRate: bestRate
1631
+ });
1632
+ }, [canSubmit, bestDeposit, tokenAmount, activePaymentMethod, wallet.address, signalIntent, currency.contractId, bestRate]);
1633
+ const confirmPayment = react.useCallback(() => {
1634
+ setPhase("proving");
1635
+ }, []);
1636
+ const reset = react.useCallback(() => {
1637
+ setPhase("browse");
1638
+ setAmountRaw("");
1639
+ setSelectedPaymentMethod("");
1640
+ setSelectedDeposit(null);
1641
+ setIntentHash(null);
1642
+ setErrorMessage(null);
1643
+ }, []);
1644
+ return {
1645
+ phase,
1646
+ amount,
1647
+ setAmount,
1648
+ selectedPaymentMethod,
1649
+ setSelectedPaymentMethod,
1650
+ availablePaymentMethods,
1651
+ deposits: depositsResult.deposits,
1652
+ selectedDeposit,
1653
+ selectDeposit,
1654
+ isLoadingDeposits: depositsResult.isLoading,
1655
+ rate: bestRate,
1656
+ rateDisplay,
1657
+ tokenAmount,
1658
+ tokenAmountDisplay,
1659
+ token,
1660
+ currency,
1661
+ reputation: reputation$1 ?? reputation.DEFAULT_REPUTATION,
1662
+ limits,
1663
+ feePercentage: feeInfo?.percentage ?? null,
1664
+ payeeDetails: payeeResult.payeeDetails,
1665
+ payeeName: payeeResult.payeeDetails?.name ?? null,
1666
+ payeeId: payeeResult.payeeDetails?.payeeId ?? null,
1667
+ isPayeeLoading: payeeResult.isFetching,
1668
+ intentExpiryTime,
1669
+ isIntentExpired: payeeResult.isPruned,
1670
+ indexedIntentStatus: payeeResult.intentStatus,
1671
+ canSubmit,
1672
+ validationMessage,
1673
+ submitOrder,
1674
+ confirmPayment,
1675
+ reset,
1676
+ intentHash,
1677
+ intentStatus: signalIntent.status,
1678
+ intentError: errorMessage ?? signalIntent.message,
1679
+ isSubmitting: signalIntent.isLoading,
1680
+ chainId
1681
+ };
1682
+ }
1683
+ var ProvexBuyContext = react.createContext(null);
1684
+ function useProvexBuyContext() {
1685
+ const ctx = react.useContext(ProvexBuyContext);
1686
+ if (!ctx) {
1687
+ throw new Error(
1688
+ "useProvexBuyContext must be used within a <ProvexBuyProvider>. Wrap your component tree with <ProvexBuyProvider> or use useProvexBuy() directly."
1689
+ );
1690
+ }
1691
+ return ctx;
1692
+ }
1693
+ function ProvexBuyProvider({
1694
+ children,
1695
+ ...options
1696
+ }) {
1697
+ const buy = useProvexBuy(options);
1698
+ return /* @__PURE__ */ jsxRuntime.jsx(ProvexBuyContext.Provider, { value: buy, children });
1699
+ }
1700
+ var STATUS_MESSAGES = {
1701
+ input: "",
1702
+ loading_payee: "Loading payment details...",
1703
+ loading_intent: "Requesting signature...",
1704
+ loading_orchestrator: "Loading orchestrator...",
1705
+ prompt_wallet_confirm: "Confirm in your wallet...",
1706
+ writing_intent: "Submitting transaction...",
1707
+ success: "Order placed successfully!",
1708
+ error: "Something went wrong."
1709
+ };
1710
+ function BrowsePhase({
1711
+ className,
1712
+ style,
1713
+ render
1714
+ }) {
1715
+ const ctx = useProvexBuyContext();
1716
+ if (ctx.phase !== "browse") return null;
1717
+ const state = {
1718
+ amount: ctx.amount,
1719
+ setAmount: ctx.setAmount,
1720
+ selectedPaymentMethod: ctx.selectedPaymentMethod,
1721
+ setSelectedPaymentMethod: ctx.setSelectedPaymentMethod,
1722
+ availablePaymentMethods: ctx.availablePaymentMethods,
1723
+ chainId: ctx.chainId,
1724
+ rateDisplay: ctx.rateDisplay,
1725
+ tokenAmountDisplay: ctx.tokenAmountDisplay,
1726
+ token: ctx.token,
1727
+ currency: ctx.currency,
1728
+ canSubmit: ctx.canSubmit,
1729
+ isSubmitting: ctx.isSubmitting,
1730
+ validationMessage: ctx.validationMessage,
1731
+ intentError: ctx.intentError,
1732
+ intentStatus: ctx.intentStatus,
1733
+ isLoadingDeposits: ctx.isLoadingDeposits,
1734
+ deposits: ctx.deposits,
1735
+ submitOrder: ctx.submitOrder
1736
+ };
1737
+ if (render) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render(state) });
1738
+ const tokenSymbol = ctx.token?.symbol ?? "USDC";
1739
+ const matchCount = ctx.deposits.length;
1740
+ const statusMessage = STATUS_MESSAGES[ctx.intentStatus] ?? "";
1741
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1742
+ "div",
1743
+ {
1744
+ "data-provex-phase": "browse",
1745
+ "data-provex-section": "browse",
1746
+ className,
1747
+ style,
1748
+ children: [
1749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-field": "payment-method", children: [
1750
+ /* @__PURE__ */ jsxRuntime.jsx("label", { "data-provex-label": "payment-method", htmlFor: "provex-payment-method", children: "Payment Method" }),
1751
+ /* @__PURE__ */ jsxRuntime.jsx(
1752
+ "select",
1753
+ {
1754
+ "data-provex-select": "payment-method",
1755
+ id: "provex-payment-method",
1756
+ value: ctx.selectedPaymentMethod || (ctx.availablePaymentMethods[0] ?? ""),
1757
+ onChange: (e) => ctx.setSelectedPaymentMethod(e.target.value),
1758
+ children: ctx.availablePaymentMethods.map((pm) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: pm, children: payment.getPaymentMethodName(ctx.chainId, pm) }, pm))
1759
+ }
1760
+ )
1761
+ ] }),
1762
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-field": "amount", children: [
1763
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { "data-provex-label": "amount", htmlFor: "provex-amount", children: [
1764
+ "Amount (",
1765
+ ctx.currency?.ticker ?? "USD",
1766
+ ")"
1767
+ ] }),
1768
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-input-group": "amount", children: [
1769
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-input-prefix": "", children: ctx.currency?.symbol ?? "$" }),
1770
+ /* @__PURE__ */ jsxRuntime.jsx(
1771
+ "input",
1772
+ {
1773
+ "data-provex-input": "amount",
1774
+ id: "provex-amount",
1775
+ type: "text",
1776
+ inputMode: "decimal",
1777
+ placeholder: "0.00",
1778
+ value: ctx.amount,
1779
+ onChange: (e) => ctx.setAmount(e.target.value),
1780
+ autoComplete: "off"
1781
+ }
1782
+ )
1783
+ ] })
1784
+ ] }),
1785
+ ctx.rateDisplay && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-info": "rate", children: [
1786
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "rate", children: "Rate" }),
1787
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-value": "rate", children: ctx.rateDisplay })
1788
+ ] }),
1789
+ ctx.tokenAmountDisplay && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-info": "receive", children: [
1790
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "receive", children: "You receive" }),
1791
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "receive", children: [
1792
+ ctx.tokenAmountDisplay,
1793
+ " ",
1794
+ tokenSymbol
1795
+ ] })
1796
+ ] }),
1797
+ ctx.isLoadingDeposits && ctx.amount && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-provex-info": "loading", children: /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-status": "loading", children: "Searching for orders..." }) }),
1798
+ !ctx.isLoadingDeposits && ctx.amount && matchCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-provex-info": "match-count", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "match-count", children: [
1799
+ matchCount,
1800
+ " order",
1801
+ matchCount !== 1 ? "s" : "",
1802
+ " available"
1803
+ ] }) }),
1804
+ ctx.validationMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-message": "validation", children: ctx.validationMessage }),
1805
+ ctx.intentError && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-message": "error", children: ctx.intentError }),
1806
+ ctx.isSubmitting && statusMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-message": "status", children: statusMessage }),
1807
+ /* @__PURE__ */ jsxRuntime.jsx(
1808
+ "button",
1809
+ {
1810
+ "data-provex-button": "buy",
1811
+ type: "button",
1812
+ disabled: !ctx.canSubmit,
1813
+ onClick: ctx.submitOrder,
1814
+ children: ctx.isSubmitting ? "Processing..." : "Buy"
1815
+ }
1816
+ )
1817
+ ]
1818
+ }
1819
+ );
1820
+ }
1821
+ function CommittedPhase({
1822
+ className,
1823
+ style,
1824
+ render
1825
+ }) {
1826
+ const ctx = useProvexBuyContext();
1827
+ if (ctx.phase !== "committed") return null;
1828
+ const state = {
1829
+ intentHash: ctx.intentHash,
1830
+ amount: ctx.amount,
1831
+ currency: ctx.currency,
1832
+ selectedPaymentMethod: ctx.selectedPaymentMethod,
1833
+ chainId: ctx.chainId,
1834
+ tokenAmountDisplay: ctx.tokenAmountDisplay,
1835
+ token: ctx.token,
1836
+ confirmPayment: ctx.confirmPayment,
1837
+ payeeDetails: ctx.payeeDetails,
1838
+ payeeName: ctx.payeeName,
1839
+ payeeId: ctx.payeeId,
1840
+ isPayeeLoading: ctx.isPayeeLoading,
1841
+ intentExpiryTime: ctx.intentExpiryTime
1842
+ };
1843
+ if (render) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render(state) });
1844
+ const tokenSymbol = ctx.token?.symbol ?? "USDC";
1845
+ const paymentName = ctx.selectedPaymentMethod ? payment.getPaymentMethodName(ctx.chainId, ctx.selectedPaymentMethod) : "Unknown";
1846
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1847
+ "div",
1848
+ {
1849
+ "data-provex-phase": "committed",
1850
+ "data-provex-section": "committed",
1851
+ className,
1852
+ style,
1853
+ children: [
1854
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-info": "order-summary", children: [
1855
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-heading": "committed", children: "Order Placed" }),
1856
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "amount", children: [
1857
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "amount", children: "Amount" }),
1858
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "amount", children: [
1859
+ ctx.currency?.symbol,
1860
+ ctx.amount
1861
+ ] })
1862
+ ] }),
1863
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "receive", children: [
1864
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "receive", children: "You receive" }),
1865
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "receive", children: [
1866
+ ctx.tokenAmountDisplay,
1867
+ " ",
1868
+ tokenSymbol
1869
+ ] })
1870
+ ] }),
1871
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "payment-method", children: [
1872
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "payment-method", children: "Pay via" }),
1873
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-value": "payment-method", children: paymentName })
1874
+ ] }),
1875
+ ctx.intentHash && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "intent-hash", children: [
1876
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "intent-hash", children: "Order ID" }),
1877
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "intent-hash", title: ctx.intentHash, children: [
1878
+ ctx.intentHash.slice(0, 10),
1879
+ "...",
1880
+ ctx.intentHash.slice(-8)
1881
+ ] })
1882
+ ] })
1883
+ ] }),
1884
+ ctx.isPayeeLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-provex-info": "payee-loading", children: /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-status": "loading", children: "Loading seller details..." }) }),
1885
+ !ctx.isPayeeLoading && ctx.payeeId && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-info": "payee", children: [
1886
+ ctx.payeeName && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "payee-name", children: [
1887
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "payee-name", children: "Seller" }),
1888
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-value": "payee-name", children: ctx.payeeName })
1889
+ ] }),
1890
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "payee-id", children: [
1891
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "payee-id", children: "Send to" }),
1892
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-value": "payee-id", children: ctx.payeeId })
1893
+ ] })
1894
+ ] }),
1895
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-provex-info": "instructions", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { "data-provex-text": "instructions", children: [
1896
+ "Send ",
1897
+ ctx.currency?.symbol,
1898
+ ctx.amount,
1899
+ " via ",
1900
+ paymentName,
1901
+ " to the seller, then verify your payment to release the ",
1902
+ tokenSymbol,
1903
+ "."
1904
+ ] }) }),
1905
+ /* @__PURE__ */ jsxRuntime.jsx(
1906
+ "button",
1907
+ {
1908
+ "data-provex-button": "prove",
1909
+ type: "button",
1910
+ disabled: ctx.isPayeeLoading,
1911
+ onClick: ctx.confirmPayment,
1912
+ children: "I have paid"
1913
+ }
1914
+ )
1915
+ ]
1916
+ }
1917
+ );
1918
+ }
1919
+ function ProvingPhase({
1920
+ className,
1921
+ style,
1922
+ render
1923
+ }) {
1924
+ const ctx = useProvexBuyContext();
1925
+ if (ctx.phase !== "proving") return null;
1926
+ const state = {
1927
+ intentHash: ctx.intentHash,
1928
+ intentExpiryTime: ctx.intentExpiryTime,
1929
+ isIntentExpired: ctx.isIntentExpired,
1930
+ indexedIntentStatus: ctx.indexedIntentStatus
1931
+ };
1932
+ if (render) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render(state) });
1933
+ const statusText = ctx.indexedIntentStatus === "fulfilled" ? "Payment verified!" : "Generating zero-knowledge proof of your payment...";
1934
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1935
+ "div",
1936
+ {
1937
+ "data-provex-phase": "proving",
1938
+ "data-provex-section": "proving",
1939
+ className,
1940
+ style,
1941
+ children: [
1942
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-heading": "proving", children: "Verifying Payment" }),
1943
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-text": "proving", children: statusText }),
1944
+ ctx.intentHash && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "intent-hash", children: [
1945
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "intent-hash", children: "Order ID" }),
1946
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "intent-hash", title: ctx.intentHash, children: [
1947
+ ctx.intentHash.slice(0, 10),
1948
+ "...",
1949
+ ctx.intentHash.slice(-8)
1950
+ ] })
1951
+ ] }),
1952
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "data-provex-info": "status", children: /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-status": ctx.indexedIntentStatus ?? "proving", children: ctx.indexedIntentStatus === "fulfilled" ? "Complete" : "Processing..." }) })
1953
+ ]
1954
+ }
1955
+ );
1956
+ }
1957
+ function CompletePhase({
1958
+ className,
1959
+ style,
1960
+ render
1961
+ }) {
1962
+ const ctx = useProvexBuyContext();
1963
+ if (ctx.phase !== "complete") return null;
1964
+ const state = {
1965
+ intentHash: ctx.intentHash,
1966
+ chainId: ctx.chainId,
1967
+ reset: ctx.reset
1968
+ };
1969
+ if (render) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render(state) });
1970
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1971
+ "div",
1972
+ {
1973
+ "data-provex-phase": "complete",
1974
+ "data-provex-section": "complete",
1975
+ className,
1976
+ style,
1977
+ children: [
1978
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-heading": "complete", children: "Purchase Complete" }),
1979
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-provex-text": "complete", children: "Your tokens have been released to your wallet." }),
1980
+ ctx.intentHash && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-provex-detail": "intent-hash", children: [
1981
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-provex-label": "intent-hash", children: "Order ID" }),
1982
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-provex-value": "intent-hash", title: ctx.intentHash, children: [
1983
+ ctx.intentHash.slice(0, 10),
1984
+ "...",
1985
+ ctx.intentHash.slice(-8)
1986
+ ] })
1987
+ ] }),
1988
+ /* @__PURE__ */ jsxRuntime.jsx(
1989
+ "button",
1990
+ {
1991
+ "data-provex-button": "buy-more",
1992
+ type: "button",
1993
+ onClick: ctx.reset,
1994
+ children: "Buy More"
1995
+ }
1996
+ )
1997
+ ]
1998
+ }
1999
+ );
2000
+ }
2001
+ function applyTheme(baseStyle, theme) {
2002
+ if (!theme) return baseStyle;
2003
+ const vars = {};
2004
+ const themeEntries = [
2005
+ ["accent", theme.accent],
2006
+ ["background", theme.background],
2007
+ ["backgroundCard", theme.backgroundCard],
2008
+ ["text", theme.text],
2009
+ ["textMuted", theme.textMuted],
2010
+ ["border", theme.border],
2011
+ ["error", theme.error],
2012
+ ["radius", theme.radius],
2013
+ ["font", theme.font]
2014
+ ];
2015
+ for (const [key, value] of themeEntries) {
2016
+ if (value === void 0) continue;
2017
+ const kebab = key.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);
2018
+ vars[`--provex-${kebab}`] = value;
2019
+ }
2020
+ if (Object.keys(vars).length === 0) return baseStyle;
2021
+ return { ...baseStyle, ...vars };
2022
+ }
2023
+ function ProvexBuy({
2024
+ wallet,
2025
+ onIntentSignaled,
2026
+ onComplete,
2027
+ paymentMethods,
2028
+ className,
2029
+ style,
2030
+ theme
2031
+ }) {
2032
+ return /* @__PURE__ */ jsxRuntime.jsx(
2033
+ ProvexBuyProvider,
2034
+ {
2035
+ wallet,
2036
+ onIntentSignaled,
2037
+ onComplete,
2038
+ paymentMethods,
2039
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2040
+ ProvexBuyRoot,
2041
+ {
2042
+ className,
2043
+ style: applyTheme(style, theme)
2044
+ }
2045
+ )
2046
+ }
2047
+ );
2048
+ }
2049
+ function ProvexBuyRoot({
2050
+ className,
2051
+ style
2052
+ }) {
2053
+ const { phase } = useProvexBuyContext();
2054
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2055
+ "div",
2056
+ {
2057
+ className,
2058
+ style,
2059
+ "data-provex-root": "",
2060
+ "data-provex-phase": phase,
2061
+ children: [
2062
+ /* @__PURE__ */ jsxRuntime.jsx(BrowsePhase, {}),
2063
+ /* @__PURE__ */ jsxRuntime.jsx(CommittedPhase, {}),
2064
+ /* @__PURE__ */ jsxRuntime.jsx(ProvingPhase, {}),
2065
+ /* @__PURE__ */ jsxRuntime.jsx(CompletePhase, {})
2066
+ ]
2067
+ }
2068
+ );
2069
+ }
2070
+ function useWagmiWallet() {
2071
+ const { address } = wagmi.useAccount();
2072
+ const { data: walletClient } = wagmi.useWalletClient();
2073
+ return react.useMemo(() => {
2074
+ if (!walletClient || !address) return null;
2075
+ return {
2076
+ address,
2077
+ async sendTransaction(tx) {
2078
+ return walletClient.sendTransaction({
2079
+ to: tx.to,
2080
+ data: tx.data,
2081
+ value: tx.value ?? 0n,
2082
+ chain: walletClient.chain,
2083
+ maxFeePerGas: tx.maxFeePerGas,
2084
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
2085
+ });
2086
+ }
2087
+ };
2088
+ }, [walletClient, address]);
2089
+ }
2090
+ var DISCONNECTED_WALLET = {
2091
+ address: void 0,
2092
+ sendTransaction: () => Promise.reject(new Error("Wallet not connected"))
2093
+ };
2094
+ function ProvexBuyWagmi({
2095
+ onIntentSignaled,
2096
+ onComplete,
2097
+ paymentMethods,
2098
+ className,
2099
+ style,
2100
+ theme
2101
+ }) {
2102
+ const wallet = useWagmiWallet() ?? DISCONNECTED_WALLET;
2103
+ return /* @__PURE__ */ jsxRuntime.jsx(
2104
+ ProvexBuy,
2105
+ {
2106
+ wallet,
2107
+ onIntentSignaled,
2108
+ onComplete,
2109
+ paymentMethods,
2110
+ className,
2111
+ style,
2112
+ theme
2113
+ }
2114
+ );
2115
+ }
2116
+ function useNullifierRegistry({
2117
+ escrowAddress,
2118
+ paymentMethodId,
2119
+ chainId
2120
+ }) {
2121
+ const publicClient2 = wagmi.usePublicClient({ chainId });
2122
+ const { data: orchestratorAddress, isLoading: isLoadingOrchestrator } = wagmi.useReadContract({
2123
+ address: escrowAddress,
2124
+ abi: abis.V3EscrowAbi,
2125
+ functionName: "orchestrator",
2126
+ chainId,
2127
+ query: {
2128
+ enabled: !!escrowAddress
2129
+ }
2130
+ });
2131
+ const { data: paymentVerifierRegistryAddress, isLoading: isLoadingRegistry } = wagmi.useReadContract({
2132
+ address: orchestratorAddress,
2133
+ abi: abis.OrchestratorAbi,
2134
+ functionName: "paymentVerifierRegistry",
2135
+ chainId,
2136
+ query: {
2137
+ enabled: !!orchestratorAddress
2138
+ }
2139
+ });
2140
+ const { data: verifierAddress, isLoading: isLoadingVerifier } = wagmi.useReadContract({
2141
+ address: paymentVerifierRegistryAddress,
2142
+ abi: abis.PaymentVerifierRegistryAbi,
2143
+ functionName: "getVerifier",
2144
+ args: paymentMethodId ? [paymentMethodId] : void 0,
2145
+ chainId,
2146
+ query: {
2147
+ enabled: !!paymentVerifierRegistryAddress && !!paymentMethodId
2148
+ }
2149
+ });
2150
+ const { data: nullifierRegistryAddress, isLoading: isLoadingNullifierRegistry } = wagmi.useReadContract({
2151
+ address: verifierAddress,
2152
+ abi: abis.UnifiedPaymentVerifierAbi,
2153
+ functionName: "nullifierRegistry",
2154
+ chainId,
2155
+ query: {
2156
+ enabled: !!verifierAddress
2157
+ }
2158
+ });
2159
+ const isLoading = isLoadingOrchestrator || isLoadingRegistry || isLoadingVerifier || isLoadingNullifierRegistry;
2160
+ const checkNullifierUsed = react.useCallback(
2161
+ async (nullifier) => {
2162
+ if (!nullifierRegistryAddress || !publicClient2) {
2163
+ return null;
2164
+ }
2165
+ try {
2166
+ const isUsed = await publicClient2.readContract({
2167
+ address: nullifierRegistryAddress,
2168
+ abi: abis.NullifierRegistryAbi,
2169
+ functionName: "isNullified",
2170
+ args: [nullifier]
2171
+ });
2172
+ return isUsed;
2173
+ } catch (error) {
2174
+ console.error("[useNullifierRegistry] Failed to check nullifier:", error);
2175
+ return null;
2176
+ }
2177
+ },
2178
+ [nullifierRegistryAddress, publicClient2]
2179
+ );
2180
+ return {
2181
+ orchestratorAddress,
2182
+ paymentVerifierRegistryAddress,
2183
+ verifierAddress,
2184
+ nullifierRegistryAddress,
2185
+ isLoading,
2186
+ checkNullifierUsed
2187
+ };
2188
+ }
2189
+ var useAllowance = ({
2190
+ token,
2191
+ spender,
2192
+ account,
2193
+ balance,
2194
+ defaultAllowance = "max",
2195
+ onSuccess,
2196
+ onError,
2197
+ onSettled
2198
+ }) => {
2199
+ const targetAllowance = react.useMemo(() => {
2200
+ if (defaultAllowance === "balance" && balance) {
2201
+ return balance;
2202
+ }
2203
+ return viem.maxUint256;
2204
+ }, [defaultAllowance, balance]);
2205
+ const { chain: currentChain } = wagmi.useConnection();
2206
+ const { mutateAsync: switchChainAsync } = wagmi.useSwitchChain();
2207
+ const publicClient2 = wagmi.usePublicClient({ chainId: token.chainId });
2208
+ const queryEnabled = !!publicClient2 && !!account && !!spender && !!token.address;
2209
+ const { data, isLoading, error, refetch } = reactQuery.useQuery({
2210
+ queryKey: ["allowance", token.address, spender, account, token.chainId],
2211
+ queryFn: async () => {
2212
+ const result = await publicClient2.readContract({
2213
+ abi: viem.erc20Abi,
2214
+ address: token.address,
2215
+ functionName: "allowance",
2216
+ args: [account, spender]
2217
+ });
2218
+ return result;
2219
+ },
2220
+ enabled: queryEnabled
2221
+ });
2222
+ const [approvalPhase, setApprovalPhase] = react.useState("idle");
2223
+ const { mutateAsync } = wagmi.useWriteContract();
2224
+ const isWritingApproval = approvalPhase !== "idle";
2225
+ const approvalLoadingText = approvalPhase === "confirming" ? "Confirming..." : `Approve ${token.symbol}`;
2226
+ return {
2227
+ allowance: data ?? null,
2228
+ isLoading,
2229
+ isWritingApproval,
2230
+ /** Loading text for the approval button */
2231
+ approvalLoadingText,
2232
+ error,
2233
+ writeApproval: async () => {
2234
+ if (!account || !spender || !token.address || !publicClient2) {
2235
+ return;
2236
+ }
2237
+ setApprovalPhase("submitting");
2238
+ try {
2239
+ if (currentChain?.id !== token.chainId) {
2240
+ await switchChainAsync({ chainId: token.chainId });
2241
+ }
2242
+ const gasInputs = await getTransactionGasInputs(token.chainId);
2243
+ const hash = await mutateAsync({
2244
+ chainId: token.chainId,
2245
+ address: token.address,
2246
+ abi: viem.erc20Abi,
2247
+ functionName: "approve",
2248
+ ...gasInputs,
2249
+ args: [spender, targetAllowance]
2250
+ });
2251
+ setApprovalPhase("confirming");
2252
+ await publicClient2.waitForTransactionReceipt({ hash });
2253
+ await refetch();
2254
+ onSuccess?.(hash);
2255
+ } catch (err) {
2256
+ onError?.(err);
2257
+ } finally {
2258
+ setApprovalPhase("idle");
2259
+ onSettled?.();
2260
+ }
2261
+ }
2262
+ };
2263
+ };
2264
+ var useCancelIntent = ({
2265
+ onSuccess,
2266
+ onMutate,
2267
+ onError,
2268
+ onSettled,
2269
+ chainId,
2270
+ escrowAddress: providedEscrowAddress,
2271
+ version: explicitVersion
2272
+ }) => {
2273
+ const provex = useOptionalProvex();
2274
+ const wallet = useWagmiWallet();
2275
+ const [status, setStatus] = react.useState("idle");
2276
+ const [errorMessage, setErrorMessage] = react.useState(null);
2277
+ const isLoading = react.useMemo(() => status === "prompt_wallet_confirm" || status === "writing_tx", [status]);
2278
+ const version = react.useMemo(() => {
2279
+ if (explicitVersion) return explicitVersion;
2280
+ if (providedEscrowAddress) {
2281
+ const detected = contracts.getEscrowVersion(providedEscrowAddress);
2282
+ if (detected) return detected;
2283
+ }
2284
+ return "v3";
2285
+ }, [explicitVersion, providedEscrowAddress]);
2286
+ const escrowAddress = react.useMemo(() => {
2287
+ if (providedEscrowAddress) return providedEscrowAddress;
2288
+ if (!chainId) return null;
2289
+ return contracts.getLatestContract(chainId, "escrow", version);
2290
+ }, [providedEscrowAddress, chainId, version]);
2291
+ const client = react.useMemo(() => {
2292
+ if (!provex || !chainId || version !== "v3") return null;
2293
+ return createProveXClient({
2294
+ chain: provex.config.chain,
2295
+ apiUrl: provex.config.apiUrl,
2296
+ wallet: wallet ?? void 0,
2297
+ indexer: provex.indexer,
2298
+ escrowAddress: escrowAddress ?? void 0
2299
+ });
2300
+ }, [provex, chainId, version, wallet, escrowAddress]);
2301
+ const { mutateAsync: writeContractAsync } = wagmi.useWriteContract();
2302
+ const publicClient2 = wagmi.usePublicClient();
2303
+ const cancelIntent = react.useCallback(async (intentHash) => {
2304
+ if (!intentHash || !chainId) return;
2305
+ setStatus("prompt_wallet_confirm");
2306
+ setErrorMessage(null);
2307
+ onMutate?.();
2308
+ try {
2309
+ if (version === "v3" && client) {
2310
+ await client.cancelIntent({ intentHash });
2311
+ } else {
2312
+ if (!escrowAddress) throw new Error("Missing escrow address");
2313
+ const gasInputs = await getTransactionGasInputs(chainId);
2314
+ const hash = await writeContractAsync({
2315
+ address: escrowAddress,
2316
+ abi: abis.V2EscrowAbi,
2317
+ functionName: "cancelIntent",
2318
+ args: [intentHash],
2319
+ ...gasInputs
2320
+ });
2321
+ if (publicClient2) await publicClient2.waitForTransactionReceipt({ hash });
2322
+ if (provex?.indexer) {
2323
+ await checkTransactionIndexed({ queryTransaction: provex.indexer.getTransactionByHash, hash });
2324
+ }
2325
+ }
2326
+ setStatus("success");
2327
+ onSuccess?.();
2328
+ } catch (error) {
2329
+ const isRejection = error instanceof ProveXError && error.isRejection || isUserRejectionError(error);
2330
+ if (isRejection) {
2331
+ setStatus("idle");
2332
+ } else {
2333
+ setStatus("error");
2334
+ setErrorMessage(getUserFriendlyErrorMessage(error));
2335
+ }
2336
+ onError?.(error);
2337
+ } finally {
2338
+ onSettled?.();
2339
+ }
2340
+ }, [chainId, escrowAddress, version, client, publicClient2, writeContractAsync, provex, onSuccess, onMutate, onError, onSettled]);
2341
+ return {
2342
+ cancelIntent,
2343
+ status,
2344
+ isLoading,
2345
+ version,
2346
+ errorMessage
2347
+ };
2348
+ };
2349
+ var useReleaseFundsToPayer = ({
2350
+ onSuccess,
2351
+ onSettled,
2352
+ chainId,
2353
+ escrowAddress: providedEscrowAddress,
2354
+ version: explicitVersion
2355
+ }) => {
2356
+ const provex = useOptionalProvex();
2357
+ const wallet = useWagmiWallet();
2358
+ const [status, setStatus] = react.useState("idle");
2359
+ const [errorMessage, setErrorMessage] = react.useState(null);
2360
+ const isLoading = react.useMemo(
2361
+ () => status === "prompt_wallet_confirm" || status === "writing_tx",
2362
+ [status]
2363
+ );
2364
+ const version = react.useMemo(() => {
2365
+ if (explicitVersion) return explicitVersion;
2366
+ if (providedEscrowAddress) {
2367
+ const detected = contracts.getEscrowVersion(providedEscrowAddress);
2368
+ if (detected) return detected;
2369
+ }
2370
+ return "v3";
2371
+ }, [explicitVersion, providedEscrowAddress]);
2372
+ const escrowAddress = react.useMemo(() => {
2373
+ if (providedEscrowAddress) return providedEscrowAddress;
2374
+ if (!chainId) return null;
2375
+ return contracts.getLatestContract(chainId, "escrow", version);
2376
+ }, [providedEscrowAddress, chainId, version]);
2377
+ const client = react.useMemo(() => {
2378
+ if (!provex || !chainId || version !== "v3") return null;
2379
+ return createProveXClient({
2380
+ chain: provex.config.chain,
2381
+ apiUrl: provex.config.apiUrl,
2382
+ wallet: wallet ?? void 0,
2383
+ indexer: provex.indexer,
2384
+ escrowAddress: escrowAddress ?? void 0
2385
+ });
2386
+ }, [provex, chainId, version, wallet, escrowAddress]);
2387
+ const { mutateAsync: writeContractAsync } = wagmi.useWriteContract();
2388
+ const publicClient2 = wagmi.usePublicClient();
2389
+ const releaseFundsToPayer = react.useCallback(async (intentHash) => {
2390
+ if (!intentHash || !chainId) return;
2391
+ setStatus("prompt_wallet_confirm");
2392
+ setErrorMessage(null);
2393
+ try {
2394
+ if (version === "v3" && client) {
2395
+ await client.releaseFundsToPayer({ intentHash });
2396
+ } else {
2397
+ if (!escrowAddress) throw new Error("Missing escrow address");
2398
+ const gasInputs = await getTransactionGasInputs(chainId);
2399
+ const hash = await writeContractAsync({
2400
+ address: escrowAddress,
2401
+ abi: abis.V2EscrowAbi,
2402
+ functionName: "releaseFundsToPayer",
2403
+ args: [intentHash],
2404
+ ...gasInputs
2405
+ });
2406
+ if (publicClient2) await publicClient2.waitForTransactionReceipt({ hash });
2407
+ if (provex?.indexer) {
2408
+ await checkTransactionIndexed({ queryTransaction: provex.indexer.getTransactionByHash, hash });
2409
+ }
2410
+ }
2411
+ setStatus("success");
2412
+ onSuccess?.();
2413
+ } catch (error) {
2414
+ const isRejection = error instanceof ProveXError && error.isRejection || isUserRejectionError(error);
2415
+ if (isRejection) {
2416
+ setStatus("idle");
2417
+ setErrorMessage(null);
2418
+ } else {
2419
+ setStatus("error");
2420
+ setErrorMessage(error instanceof Error ? error.message : "Transaction failed");
2421
+ }
2422
+ } finally {
2423
+ onSettled?.();
2424
+ }
2425
+ }, [chainId, escrowAddress, version, client, publicClient2, writeContractAsync, provex, onSuccess, onSettled]);
2426
+ return {
2427
+ releaseFundsToPayer,
2428
+ status,
2429
+ isLoading,
2430
+ errorMessage,
2431
+ version
2432
+ };
2433
+ };
2434
+ var useV3Escrow = ({
2435
+ chainId,
2436
+ escrowAddress,
2437
+ onSlowSync,
2438
+ onTransactionConfirmed,
2439
+ onError
2440
+ }) => {
2441
+ const provex = useOptionalProvex();
2442
+ const wallet = useWagmiWallet();
2443
+ const [status, setStatus] = react.useState("idle");
2444
+ const [error, setError] = react.useState(null);
2445
+ const [txHash, setTxHash] = react.useState(null);
2446
+ const escrow = react.useMemo(() => {
2447
+ if (escrowAddress) return escrowAddress;
2448
+ if (!chainId) return null;
2449
+ return contracts.getLatestContract(chainId, "escrow", "v3");
2450
+ }, [chainId, escrowAddress]);
2451
+ const isLoading = react.useMemo(
2452
+ () => status === "prompt_wallet_confirm" || status === "writing_tx" || status === "confirming" || status === "syncing",
2453
+ [status]
2454
+ );
2455
+ const client = react.useMemo(() => {
2456
+ if (!provex || !chainId) return null;
2457
+ return createProveXClient({
2458
+ chain: provex.config.chain,
2459
+ apiUrl: provex.config.apiUrl,
2460
+ wallet: wallet ?? void 0,
2461
+ indexer: provex.indexer,
2462
+ escrowAddress: escrow ?? void 0,
2463
+ onTransactionHash: (hash) => {
2464
+ setTxHash(hash);
2465
+ setStatus("confirming");
2466
+ },
2467
+ onSlowSync: () => {
2468
+ setStatus("syncing");
2469
+ onSlowSync?.();
2470
+ }
2471
+ });
2472
+ }, [provex, chainId, wallet, escrow, onSlowSync]);
2473
+ const { data: orchestratorAddress, refetch: refetchOrchestrator } = wagmi.useReadContract({
2474
+ address: escrow ?? void 0,
2475
+ abi: abis.V3EscrowAbi,
2476
+ functionName: "orchestrator",
2477
+ query: { enabled: !!escrow }
2478
+ });
2479
+ const { data: depositCounter, refetch: refetchDepositCounter } = wagmi.useReadContract({
2480
+ address: escrow ?? void 0,
2481
+ abi: abis.V3EscrowAbi,
2482
+ functionName: "depositCounter",
2483
+ query: { enabled: !!escrow }
2484
+ });
2485
+ const executeClientMethod = react.useCallback(async (method, action) => {
2486
+ if (!client || !chainId) {
2487
+ const msg = "Client or chain not configured";
2488
+ setError(msg);
2489
+ onError?.(new Error(msg));
2490
+ return null;
2491
+ }
2492
+ try {
2493
+ setStatus("prompt_wallet_confirm");
2494
+ setError(null);
2495
+ setTxHash(null);
2496
+ const result = await method();
2497
+ setStatus("success");
2498
+ onTransactionConfirmed?.({ hash: result.hash, chainId, action });
2499
+ return result.hash;
2500
+ } catch (err) {
2501
+ if (err instanceof ProveXError && err.isRejection) {
2502
+ setStatus("idle");
2503
+ setError(null);
2504
+ return null;
2505
+ }
2506
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2507
+ setStatus("error");
2508
+ setError(errorObj.message);
2509
+ onError?.(errorObj);
2510
+ return null;
2511
+ }
2512
+ }, [client, chainId, onTransactionConfirmed, onError]);
2513
+ const createDeposit = react.useCallback(
2514
+ (params) => executeClientMethod(() => client.createDepositRaw(params), "Deposit created"),
2515
+ [client, executeClientMethod]
2516
+ );
2517
+ const addFunds = react.useCallback(
2518
+ (depositId, amount) => executeClientMethod(() => client.addFunds({ depositId, amount }), "Funds added"),
2519
+ [client, executeClientMethod]
2520
+ );
2521
+ const removeFunds = react.useCallback(
2522
+ (depositId, amount) => executeClientMethod(() => client.removeFunds({ depositId, amount }), "Funds removed"),
2523
+ [client, executeClientMethod]
2524
+ );
2525
+ const withdrawDeposit = react.useCallback(
2526
+ (depositId) => executeClientMethod(() => client.withdrawDeposit({ depositId }), "Deposit withdrawn"),
2527
+ [client, executeClientMethod]
2528
+ );
2529
+ const pruneExpiredIntents = react.useCallback(
2530
+ (depositId) => executeClientMethod(() => client.pruneExpiredIntents({ depositId }), "Expired intents pruned"),
2531
+ [client, executeClientMethod]
2532
+ );
2533
+ const setAcceptingIntents = react.useCallback(
2534
+ (depositId, accepting) => executeClientMethod(() => client.setAcceptingIntents({ depositId, accepting }), "Deposit updated"),
2535
+ [client, executeClientMethod]
2536
+ );
2537
+ const setRetainOnEmpty = react.useCallback(
2538
+ (depositId, retain) => executeClientMethod(() => client.setRetainOnEmpty({ depositId, retain }), "Deposit updated"),
2539
+ [client, executeClientMethod]
2540
+ );
2541
+ const setIntentRange = react.useCallback(
2542
+ (depositId, intentAmountRange) => executeClientMethod(() => client.setIntentRange({ depositId, ...intentAmountRange }), "Amount range updated"),
2543
+ [client, executeClientMethod]
2544
+ );
2545
+ const setPaymentMethodActive = react.useCallback(
2546
+ (depositId, paymentMethod, active) => executeClientMethod(() => client.setPaymentMethodActive({ depositId, paymentMethod, active }), "Platform updated"),
2547
+ [client, executeClientMethod]
2548
+ );
2549
+ const setCurrencyMinRate = react.useCallback(
2550
+ (params) => executeClientMethod(() => client.setCurrencyMinRate({
2551
+ depositId: params.depositId,
2552
+ paymentMethod: params.paymentMethod,
2553
+ currency: params.currency,
2554
+ rate: params.newMinConversionRate
2555
+ }), "Conversion rate updated"),
2556
+ [client, executeClientMethod]
2557
+ );
2558
+ const addCurrencies = react.useCallback(
2559
+ (params) => executeClientMethod(() => client.addCurrencies(params), "Currencies added"),
2560
+ [client, executeClientMethod]
2561
+ );
2562
+ const deactivateCurrency = react.useCallback(
2563
+ (params) => executeClientMethod(() => client.deactivateCurrency(params), "Currency deactivated"),
2564
+ [client, executeClientMethod]
2565
+ );
2566
+ const addPaymentMethods = react.useCallback(
2567
+ (params) => executeClientMethod(() => client.addPaymentMethods(params), "Payment method added"),
2568
+ [client, executeClientMethod]
2569
+ );
2570
+ const getDeposit = react.useCallback(
2571
+ async (depositId) => {
2572
+ if (!client) return null;
2573
+ try {
2574
+ return await client.getDeposit(depositId);
2575
+ } catch {
2576
+ return null;
2577
+ }
2578
+ },
2579
+ [client]
2580
+ );
2581
+ const getAccountDeposits = react.useCallback(
2582
+ async (account) => {
2583
+ if (!client) return [];
2584
+ try {
2585
+ return await client.getAccountDeposits(account);
2586
+ } catch {
2587
+ return [];
2588
+ }
2589
+ },
2590
+ [client]
2591
+ );
2592
+ const reset = react.useCallback(() => {
2593
+ setStatus("idle");
2594
+ setError(null);
2595
+ setTxHash(null);
2596
+ }, []);
2597
+ return {
2598
+ status,
2599
+ error,
2600
+ isLoading,
2601
+ escrow,
2602
+ orchestratorAddress,
2603
+ depositCounter,
2604
+ txHash,
2605
+ createDeposit,
2606
+ addFunds,
2607
+ removeFunds,
2608
+ withdrawDeposit,
2609
+ pruneExpiredIntents,
2610
+ setAcceptingIntents,
2611
+ setRetainOnEmpty,
2612
+ setIntentRange,
2613
+ setPaymentMethodActive,
2614
+ setCurrencyMinRate,
2615
+ addCurrencies,
2616
+ deactivateCurrency,
2617
+ addPaymentMethods,
2618
+ getDeposit,
2619
+ getAccountDeposits,
2620
+ reset,
2621
+ refetchOrchestrator,
2622
+ refetchDepositCounter
2623
+ };
2624
+ };
2625
+ var useTransactionWithIndexer = (options = {}) => {
2626
+ const { onSuccess, onError, onSlowSync, onTransactionConfirmed, onTransactionError, indexer: indexerParam } = options;
2627
+ const provex = useOptionalProvex();
2628
+ const indexer = indexerParam ?? provex?.indexer;
2629
+ const [phase, setPhase] = react.useState("idle");
2630
+ const publicClient2 = wagmi.usePublicClient();
2631
+ const waitForTransaction = react.useCallback(async ({
2632
+ hash,
2633
+ chainId,
2634
+ action
2635
+ }) => {
2636
+ try {
2637
+ setPhase("confirming");
2638
+ await publicClient2?.waitForTransactionReceipt({ hash });
2639
+ if (indexer) {
2640
+ setPhase("syncing");
2641
+ await checkTransactionIndexed({
2642
+ queryTransaction: indexer.getTransactionByHash,
2643
+ hash,
2644
+ onSlowSync
2645
+ });
2646
+ }
2647
+ setPhase("complete");
2648
+ onTransactionConfirmed?.({ hash, chainId, action });
2649
+ onSuccess?.();
2650
+ } catch (e) {
2651
+ setPhase("error");
2652
+ const error = e instanceof Error ? e : new Error(String(e));
2653
+ onTransactionError?.(getUserFriendlyErrorMessage(error));
2654
+ onError?.(error);
2655
+ throw e;
2656
+ }
2657
+ }, [publicClient2, indexer, onSuccess, onError, onSlowSync, onTransactionConfirmed, onTransactionError]);
2658
+ const waitForMutation = react.useCallback(async ({
2659
+ hash,
2660
+ depositId,
2661
+ chainId,
2662
+ status,
2663
+ action
2664
+ }) => {
2665
+ try {
2666
+ setPhase("confirming");
2667
+ await publicClient2?.waitForTransactionReceipt({ hash });
2668
+ if (indexer) {
2669
+ setPhase("syncing");
2670
+ const escrow = contracts.getLatestContract(chainId, "escrow");
2671
+ const depositIdHex = ids.orderId.deposit.serialize({
2672
+ escrow,
2673
+ depositId,
2674
+ chainId
2675
+ });
2676
+ await checkMutation({
2677
+ queryDeposit: indexer.getDepositById,
2678
+ depositIdHex,
2679
+ status,
2680
+ onSlowSync
2681
+ });
2682
+ }
2683
+ setPhase("complete");
2684
+ onTransactionConfirmed?.({ hash, chainId, action });
2685
+ onSuccess?.();
2686
+ } catch (e) {
2687
+ setPhase("error");
2688
+ const error = e instanceof Error ? e : new Error(String(e));
2689
+ onTransactionError?.(getUserFriendlyErrorMessage(error));
2690
+ onError?.(error);
2691
+ throw e;
2692
+ }
2693
+ }, [publicClient2, indexer, onSuccess, onError, onSlowSync, onTransactionConfirmed, onTransactionError]);
2694
+ const reset = react.useCallback(() => {
2695
+ setPhase("idle");
2696
+ }, []);
2697
+ const isLoading = phase === "submitting" || phase === "confirming" || phase === "syncing";
2698
+ const loadingText = phase === "syncing" ? "Syncing..." : phase === "confirming" ? "Confirming..." : void 0;
2699
+ return {
2700
+ phase,
2701
+ isLoading,
2702
+ loadingText,
2703
+ waitForTransaction,
2704
+ waitForMutation,
2705
+ reset,
2706
+ setPhase
2707
+ };
2708
+ };
2709
+ function useProveXClient(wallet) {
2710
+ const { config, indexer } = useProvex();
2711
+ return react.useMemo(
2712
+ () => new ProveXClient({
2713
+ chain: config.chain,
2714
+ apiUrl: config.apiUrl,
2715
+ wallet,
2716
+ indexer
2717
+ }),
2718
+ [config.chain, config.apiUrl, wallet, indexer]
2719
+ );
2720
+ }
2721
+
2722
+ exports.BrowsePhase = BrowsePhase;
2723
+ exports.CommittedPhase = CommittedPhase;
2724
+ exports.CompletePhase = CompletePhase;
2725
+ exports.ProveXClient = ProveXClient;
2726
+ exports.ProveXError = ProveXError;
2727
+ exports.ProvexBuy = ProvexBuy;
2728
+ exports.ProvexBuyProvider = ProvexBuyProvider;
2729
+ exports.ProvexBuyWagmi = ProvexBuyWagmi;
2730
+ exports.ProvexProvider = ProvexProvider;
2731
+ exports.ProvingPhase = ProvingPhase;
2732
+ exports.createApiClient = createApiClient;
2733
+ exports.createProveXClient = createProveXClient;
2734
+ exports.getRate = getRate;
2735
+ exports.getTierDisplayInfo = getTierDisplayInfo;
2736
+ exports.intentStatuses = intentStatuses;
2737
+ exports.useAllowance = useAllowance;
2738
+ exports.useCancelIntent = useCancelIntent;
2739
+ exports.useDeposits = useDeposits;
2740
+ exports.useNullifierRegistry = useNullifierRegistry;
2741
+ exports.usePayeeDetails = usePayeeDetails;
2742
+ exports.useProtocolFeePercentage = useProtocolFeePercentage;
2743
+ exports.useProtocolFees = useProtocolFees;
2744
+ exports.useProveXClient = useProveXClient;
2745
+ exports.useProvex = useProvex;
2746
+ exports.useProvexBuy = useProvexBuy;
2747
+ exports.useProvexBuyContext = useProvexBuyContext;
2748
+ exports.useReleaseFundsToPayer = useReleaseFundsToPayer;
2749
+ exports.useReputation = useReputation;
2750
+ exports.useReputationLimits = useReputationLimits;
2751
+ exports.useSignalIntent = useSignalIntent;
2752
+ exports.useTransactionWithIndexer = useTransactionWithIndexer;
2753
+ exports.useV3Escrow = useV3Escrow;
2754
+ exports.useWagmiWallet = useWagmiWallet;
2755
+ //# sourceMappingURL=index.cjs.map
2756
+ //# sourceMappingURL=index.cjs.map