@x402x/client 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,676 @@
1
+ import { getNetworkConfig, calculateCommitment, settle as settle$1, TransferHook, calculateFacilitatorFee } from '@x402x/core';
2
+ export { AmountError, formatDefaultAssetAmount, parseDefaultAssetAmount } from '@x402x/core';
3
+ import { getAddress, publicActions, isAddress } from 'viem';
4
+ import { signTypedData } from 'viem/actions';
5
+ import { useMemo, useState, useCallback } from 'react';
6
+ import { useWalletClient, useAccount, useChainId } from 'wagmi';
7
+
8
+ // src/client.ts
9
+
10
+ // src/errors.ts
11
+ var X402ClientError = class _X402ClientError extends Error {
12
+ constructor(message, code) {
13
+ super(message);
14
+ this.code = code;
15
+ this.name = "X402ClientError";
16
+ Object.setPrototypeOf(this, _X402ClientError.prototype);
17
+ }
18
+ };
19
+ var NetworkError = class _NetworkError extends X402ClientError {
20
+ constructor(message, code) {
21
+ super(message, code);
22
+ this.name = "NetworkError";
23
+ Object.setPrototypeOf(this, _NetworkError.prototype);
24
+ }
25
+ };
26
+ var SigningError = class _SigningError extends X402ClientError {
27
+ constructor(message, code) {
28
+ super(message, code);
29
+ this.name = "SigningError";
30
+ Object.setPrototypeOf(this, _SigningError.prototype);
31
+ }
32
+ };
33
+ var FacilitatorError = class _FacilitatorError extends X402ClientError {
34
+ constructor(message, code, statusCode, response) {
35
+ super(message, code);
36
+ this.statusCode = statusCode;
37
+ this.response = response;
38
+ this.name = "FacilitatorError";
39
+ Object.setPrototypeOf(this, _FacilitatorError.prototype);
40
+ }
41
+ };
42
+ var TransactionError = class _TransactionError extends X402ClientError {
43
+ constructor(message, code, txHash) {
44
+ super(message, code);
45
+ this.txHash = txHash;
46
+ this.name = "TransactionError";
47
+ Object.setPrototypeOf(this, _TransactionError.prototype);
48
+ }
49
+ };
50
+ var ValidationError = class _ValidationError extends X402ClientError {
51
+ constructor(message, code) {
52
+ super(message, code);
53
+ this.name = "ValidationError";
54
+ Object.setPrototypeOf(this, _ValidationError.prototype);
55
+ }
56
+ };
57
+ function generateSalt() {
58
+ const randomBytes = crypto.getRandomValues(new Uint8Array(32));
59
+ return `0x${Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
60
+ }
61
+ function normalizeAddress(address, name = "address") {
62
+ if (!address || typeof address !== "string") {
63
+ throw new ValidationError(`${name} is required`);
64
+ }
65
+ try {
66
+ return getAddress(address);
67
+ } catch (error) {
68
+ throw new ValidationError(
69
+ `${name} is not a valid Ethereum address: ${error instanceof Error ? error.message : "Unknown error"}`
70
+ );
71
+ }
72
+ }
73
+ function validateAddress(address, name) {
74
+ if (!address || typeof address !== "string") {
75
+ throw new ValidationError(`${name} is required`);
76
+ }
77
+ if (!isAddress(address)) {
78
+ throw new ValidationError(`${name} must be a valid Ethereum address`);
79
+ }
80
+ }
81
+ function validateHex(hex, name, expectedLength) {
82
+ if (!hex || typeof hex !== "string") {
83
+ throw new ValidationError(`${name} is required`);
84
+ }
85
+ if (!hex.startsWith("0x")) {
86
+ throw new ValidationError(`${name} must start with 0x`);
87
+ }
88
+ if (!/^0x[0-9a-fA-F]*$/.test(hex)) {
89
+ throw new ValidationError(`${name} must be a valid hex string`);
90
+ }
91
+ if (expectedLength !== void 0) {
92
+ const actualLength = (hex.length - 2) / 2;
93
+ if (actualLength !== expectedLength) {
94
+ throw new ValidationError(
95
+ `${name} must be ${expectedLength} bytes, got ${actualLength} bytes`
96
+ );
97
+ }
98
+ }
99
+ }
100
+ function validateAmount(amount, name) {
101
+ if (amount === null || amount === void 0 || amount === "") {
102
+ throw new ValidationError(`${name} is required`);
103
+ }
104
+ const amountStr = typeof amount === "number" ? amount.toString() : amount;
105
+ if (typeof amountStr !== "string" || amountStr.trim() === "") {
106
+ throw new ValidationError(`${name} must be a non-empty string`);
107
+ }
108
+ if (!/^\d+$/.test(amountStr)) {
109
+ throw new ValidationError(
110
+ `${name} must be a positive integer string (atomic units). Use parseDefaultAssetAmount() from @x402x/core to convert USD amounts.`
111
+ );
112
+ }
113
+ try {
114
+ const atomicAmount = BigInt(amountStr);
115
+ if (atomicAmount < 0n) {
116
+ throw new ValidationError(`${name} cannot be negative`);
117
+ }
118
+ if (atomicAmount === 0n) {
119
+ throw new ValidationError(`${name} cannot be zero`);
120
+ }
121
+ } catch (error) {
122
+ if (error instanceof ValidationError) {
123
+ throw error;
124
+ }
125
+ throw new ValidationError(
126
+ `${name} is not a valid amount: ${error instanceof Error ? error.message : "Invalid format"}`
127
+ );
128
+ }
129
+ }
130
+ function formatFacilitatorUrl(url) {
131
+ return url.endsWith("/") ? url.slice(0, -1) : url;
132
+ }
133
+ function calculateTimeWindow(maxTimeoutSeconds = 300) {
134
+ const now = Math.floor(Date.now() / 1e3);
135
+ return {
136
+ validAfter: (now - 600).toString(),
137
+ // 10 minutes before
138
+ validBefore: (now + maxTimeoutSeconds).toString()
139
+ };
140
+ }
141
+
142
+ // src/core/prepare.ts
143
+ async function queryFacilitatorFee(facilitatorUrl, network, hook, hookData = "0x") {
144
+ try {
145
+ return await calculateFacilitatorFee(facilitatorUrl, network, hook, hookData);
146
+ } catch (error) {
147
+ if (error instanceof Error) {
148
+ throw new FacilitatorError(
149
+ `Failed to query facilitator fee: ${error.message}`,
150
+ "FEE_QUERY_FAILED"
151
+ );
152
+ }
153
+ throw new FacilitatorError("Failed to query facilitator fee: Unknown error", "UNKNOWN_ERROR");
154
+ }
155
+ }
156
+ async function prepareSettlement(params) {
157
+ validateAddress(params.hook, "hook");
158
+ validateHex(params.hookData, "hookData");
159
+ validateAddress(params.payTo, "payTo");
160
+ validateAmount(params.amount, "amount");
161
+ const atomicAmount = params.amount;
162
+ if (params.customSalt) {
163
+ validateHex(params.customSalt, "customSalt", 32);
164
+ }
165
+ const from = params.wallet.account?.address;
166
+ if (!from) {
167
+ throw new ValidationError("Wallet client must have an account");
168
+ }
169
+ const networkConfig = params.networkConfig || getNetworkConfig(params.network);
170
+ if (!networkConfig) {
171
+ throw new NetworkError(
172
+ `Network '${params.network}' is not supported. Please provide custom networkConfig.`
173
+ );
174
+ }
175
+ const asset = params.asset || networkConfig.defaultAsset.address;
176
+ validateAddress(asset, "asset");
177
+ const salt = params.customSalt || generateSalt();
178
+ let facilitatorFee = params.facilitatorFee || "0";
179
+ if (!params.facilitatorFee && params.facilitatorUrl) {
180
+ try {
181
+ const feeEstimate = await queryFacilitatorFee(
182
+ params.facilitatorUrl,
183
+ params.network,
184
+ params.hook,
185
+ params.hookData
186
+ // Pass hookData for accurate fee calculation
187
+ );
188
+ if (!feeEstimate.hookAllowed) {
189
+ throw new ValidationError(
190
+ `Hook ${params.hook} is not allowed on network ${params.network}.`
191
+ );
192
+ }
193
+ facilitatorFee = feeEstimate.facilitatorFee;
194
+ } catch (error) {
195
+ console.warn(
196
+ `[x402x] Failed to query facilitator fee, using 0. This may cause settlement to fail. Error: ${error instanceof Error ? error.message : "Unknown"}`
197
+ );
198
+ facilitatorFee = "0";
199
+ }
200
+ }
201
+ const timeWindow = params.validAfter && params.validBefore ? { validAfter: params.validAfter, validBefore: params.validBefore } : calculateTimeWindow(300);
202
+ const totalAmount = (BigInt(atomicAmount) + BigInt(facilitatorFee)).toString();
203
+ const commitment = calculateCommitment({
204
+ chainId: networkConfig.chainId,
205
+ hub: networkConfig.settlementRouter,
206
+ asset,
207
+ from,
208
+ value: totalAmount,
209
+ validAfter: timeWindow.validAfter,
210
+ validBefore: timeWindow.validBefore,
211
+ salt,
212
+ payTo: params.payTo,
213
+ facilitatorFee,
214
+ hook: params.hook,
215
+ hookData: params.hookData
216
+ });
217
+ return {
218
+ network: params.network,
219
+ networkConfig,
220
+ asset,
221
+ from,
222
+ amount: atomicAmount,
223
+ validAfter: timeWindow.validAfter,
224
+ validBefore: timeWindow.validBefore,
225
+ salt,
226
+ payTo: params.payTo,
227
+ facilitatorFee,
228
+ hook: params.hook,
229
+ hookData: params.hookData,
230
+ commitment
231
+ };
232
+ }
233
+ var AUTHORIZATION_TYPES = {
234
+ TransferWithAuthorization: [
235
+ { name: "from", type: "address" },
236
+ { name: "to", type: "address" },
237
+ { name: "value", type: "uint256" },
238
+ { name: "validAfter", type: "uint256" },
239
+ { name: "validBefore", type: "uint256" },
240
+ { name: "nonce", type: "bytes32" }
241
+ ]
242
+ };
243
+ async function signAuthorization(wallet, settlement) {
244
+ try {
245
+ if (!wallet.account) {
246
+ throw new SigningError("Wallet client must have an account");
247
+ }
248
+ const domain = {
249
+ name: settlement.networkConfig.defaultAsset.eip712.name,
250
+ version: settlement.networkConfig.defaultAsset.eip712.version,
251
+ chainId: settlement.networkConfig.chainId,
252
+ verifyingContract: getAddress(settlement.asset)
253
+ };
254
+ const totalAmount = BigInt(settlement.amount) + BigInt(settlement.facilitatorFee);
255
+ const message = {
256
+ from: getAddress(settlement.from),
257
+ to: getAddress(settlement.networkConfig.settlementRouter),
258
+ value: totalAmount,
259
+ validAfter: BigInt(settlement.validAfter),
260
+ validBefore: BigInt(settlement.validBefore),
261
+ nonce: settlement.commitment
262
+ // Use commitment as nonce
263
+ };
264
+ console.log("[x402x/client] EIP-712 Message for signing:", {
265
+ from: message.from,
266
+ to: message.to,
267
+ value: message.value.toString(),
268
+ validAfter: message.validAfter.toString(),
269
+ validBefore: message.validBefore.toString(),
270
+ nonce: message.nonce
271
+ });
272
+ const signature = await signTypedData(wallet, {
273
+ account: wallet.account,
274
+ domain,
275
+ types: AUTHORIZATION_TYPES,
276
+ primaryType: "TransferWithAuthorization",
277
+ message
278
+ });
279
+ const authorization = {
280
+ from: settlement.from,
281
+ to: settlement.networkConfig.settlementRouter,
282
+ value: totalAmount.toString(),
283
+ // Use total amount (business + fee)
284
+ validAfter: settlement.validAfter,
285
+ validBefore: settlement.validBefore,
286
+ nonce: settlement.commitment
287
+ };
288
+ return {
289
+ settlement,
290
+ signature,
291
+ authorization
292
+ };
293
+ } catch (error) {
294
+ if (error instanceof SigningError) {
295
+ throw error;
296
+ }
297
+ if (error instanceof Error) {
298
+ if (error.message.includes("User rejected")) {
299
+ throw new SigningError("User rejected the signing request", "USER_REJECTED");
300
+ }
301
+ if (error.message.includes("account")) {
302
+ throw new SigningError("Wallet account not available", "NO_ACCOUNT");
303
+ }
304
+ throw new SigningError(`Failed to sign authorization: ${error.message}`, "SIGNING_FAILED");
305
+ }
306
+ throw new SigningError("Failed to sign authorization: Unknown error", "UNKNOWN_ERROR");
307
+ }
308
+ }
309
+ async function settle(facilitatorUrl, signed, timeout = 3e4) {
310
+ try {
311
+ const paymentPayload = {
312
+ x402Version: 1,
313
+ scheme: "exact",
314
+ network: signed.settlement.network,
315
+ // Network type compatibility
316
+ payload: {
317
+ signature: signed.signature,
318
+ authorization: {
319
+ from: signed.authorization.from,
320
+ to: signed.authorization.to,
321
+ value: signed.authorization.value,
322
+ validAfter: signed.authorization.validAfter,
323
+ validBefore: signed.authorization.validBefore,
324
+ nonce: signed.authorization.nonce
325
+ }
326
+ }
327
+ };
328
+ const paymentRequirements = {
329
+ scheme: "exact",
330
+ network: signed.settlement.network,
331
+ // Network type compatibility
332
+ maxAmountRequired: signed.settlement.amount,
333
+ asset: signed.settlement.asset,
334
+ payTo: signed.settlement.networkConfig.settlementRouter,
335
+ maxTimeoutSeconds: 300,
336
+ // 5 minutes
337
+ // Required by x402 protocol (even though not used in serverless mode)
338
+ // In the future, the x402 v2 will remove the resource field from the payment requirements
339
+ // (https://github.com/coinbase/x402/pull/446)
340
+ resource: "https://x402x.dev/serverless",
341
+ // Placeholder for serverless mode
342
+ description: "x402x Serverless Settlement",
343
+ mimeType: "application/json",
344
+ extra: {
345
+ name: signed.settlement.networkConfig.defaultAsset.eip712.name,
346
+ version: signed.settlement.networkConfig.defaultAsset.eip712.version,
347
+ settlementRouter: signed.settlement.networkConfig.settlementRouter,
348
+ salt: signed.settlement.salt,
349
+ payTo: signed.settlement.payTo,
350
+ facilitatorFee: signed.settlement.facilitatorFee,
351
+ hook: signed.settlement.hook,
352
+ hookData: signed.settlement.hookData
353
+ }
354
+ };
355
+ paymentPayload.paymentRequirements = paymentRequirements;
356
+ const result = await settle$1(facilitatorUrl, paymentPayload, paymentRequirements, timeout);
357
+ return {
358
+ success: result.success,
359
+ transaction: result.transaction,
360
+ network: result.network || signed.settlement.network,
361
+ payer: result.payer || signed.settlement.from,
362
+ errorReason: result.errorReason
363
+ };
364
+ } catch (error) {
365
+ if (error instanceof FacilitatorError) {
366
+ throw error;
367
+ }
368
+ if (error instanceof Error) {
369
+ throw new FacilitatorError(`Failed to settle payment: ${error.message}`, "SETTLEMENT_ERROR");
370
+ }
371
+ throw new FacilitatorError("Failed to settle payment: Unknown error", "UNKNOWN_ERROR");
372
+ }
373
+ }
374
+
375
+ // src/client.ts
376
+ var DEFAULT_FACILITATOR_URL = "https://facilitator.x402x.dev";
377
+ var X402Client = class {
378
+ /**
379
+ * Create a new X402Client instance
380
+ *
381
+ * @param config - Client configuration
382
+ * @throws NetworkError if network is unsupported
383
+ * @throws ValidationError if configuration is invalid
384
+ */
385
+ constructor(config) {
386
+ if (!config.wallet) {
387
+ throw new ValidationError("wallet is required");
388
+ }
389
+ if (!config.network) {
390
+ throw new ValidationError("network is required");
391
+ }
392
+ this.config = {
393
+ ...config,
394
+ facilitatorUrl: config.facilitatorUrl || DEFAULT_FACILITATOR_URL,
395
+ timeout: config.timeout || 3e4,
396
+ confirmationTimeout: config.confirmationTimeout || 6e4
397
+ };
398
+ }
399
+ /**
400
+ * Execute a settlement transaction
401
+ *
402
+ * This is the main method that orchestrates the entire settlement flow:
403
+ * 1. Validates parameters
404
+ * 2. Prepares settlement data (queries fee if needed)
405
+ * 3. Signs EIP-3009 authorization
406
+ * 4. Submits to facilitator
407
+ * 5. Optionally waits for transaction confirmation
408
+ *
409
+ * @param params - Execution parameters
410
+ * @param waitForConfirmation - Whether to wait for transaction confirmation (default: true)
411
+ * @returns Execution result with transaction hash and optional receipt
412
+ *
413
+ * @throws ValidationError if parameters are invalid
414
+ * @throws NetworkError if network is unsupported
415
+ * @throws SigningError if user rejects signing
416
+ * @throws FacilitatorError if facilitator request fails
417
+ * @throws TransactionError if transaction fails
418
+ *
419
+ * @example Simple transfer (uses TransferHook by default)
420
+ * ```typescript
421
+ * import { parseDefaultAssetAmount } from '@x402x/core';
422
+ *
423
+ * // Convert USD amount to atomic units
424
+ * const atomicAmount = parseDefaultAssetAmount('1', 'base-sepolia'); // '1000000'
425
+ *
426
+ * const result = await client.execute({
427
+ * amount: atomicAmount, // Must be atomic units
428
+ * payTo: '0x...'
429
+ * });
430
+ * console.log('Transaction:', result.txHash);
431
+ * ```
432
+ *
433
+ * @example Custom hook
434
+ * ```typescript
435
+ * import { parseDefaultAssetAmount } from '@x402x/core';
436
+ *
437
+ * const atomicAmount = parseDefaultAssetAmount('5', 'base-sepolia'); // '5000000'
438
+ *
439
+ * const result = await client.execute({
440
+ * hook: '0x...',
441
+ * hookData: '0x...',
442
+ * amount: atomicAmount, // Must be atomic units
443
+ * payTo: '0x...'
444
+ * });
445
+ * ```
446
+ */
447
+ async execute(params, waitForConfirmation = false) {
448
+ const hook = params.hook ? normalizeAddress(params.hook, "hook") : TransferHook.getAddress(this.config.network);
449
+ const payTo = normalizeAddress(params.payTo, "payTo");
450
+ const hookData = params.hookData || "0x";
451
+ if (params.hookData) {
452
+ validateHex(params.hookData, "hookData");
453
+ }
454
+ validateAmount(params.amount, "amount");
455
+ const settlement = await prepareSettlement({
456
+ wallet: this.config.wallet,
457
+ network: this.config.network,
458
+ hook,
459
+ hookData,
460
+ asset: params.asset,
461
+ amount: params.amount,
462
+ payTo,
463
+ facilitatorFee: params.facilitatorFee,
464
+ customSalt: params.customSalt,
465
+ validAfter: params.validAfter,
466
+ validBefore: params.validBefore,
467
+ networkConfig: this.config.networkConfig,
468
+ facilitatorUrl: this.config.facilitatorUrl
469
+ });
470
+ const signed = await signAuthorization(this.config.wallet, settlement);
471
+ const settleResult = await settle(this.config.facilitatorUrl, signed, this.config.timeout);
472
+ let receipt;
473
+ if (waitForConfirmation) {
474
+ receipt = await this.waitForTransaction(settleResult.transaction);
475
+ }
476
+ return {
477
+ txHash: settleResult.transaction,
478
+ network: settleResult.network,
479
+ payer: settleResult.payer,
480
+ receipt,
481
+ settlement
482
+ };
483
+ }
484
+ /**
485
+ * Calculate facilitator fee for a hook with optional hook data
486
+ *
487
+ * Queries the facilitator for the recommended fee based on current gas prices
488
+ * and hook gas usage. The returned fee includes a safety margin to ensure
489
+ * settlement will succeed.
490
+ *
491
+ * @param hook - Hook contract address
492
+ * @param hookData - Optional encoded hook parameters (default: '0x')
493
+ * @returns Fee calculation result from facilitator
494
+ *
495
+ * @throws FacilitatorError if query fails
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * const fee = await client.calculateFee('0x...', '0x');
500
+ * console.log('Facilitator fee:', fee.facilitatorFee);
501
+ * console.log('Fee in USD:', fee.facilitatorFeeUSD);
502
+ * console.log('Valid for:', fee.validitySeconds, 'seconds');
503
+ * ```
504
+ */
505
+ async calculateFee(hook, hookData = "0x") {
506
+ const normalizedHook = normalizeAddress(hook, "hook");
507
+ validateHex(hookData, "hookData");
508
+ try {
509
+ return await calculateFacilitatorFee(
510
+ this.config.facilitatorUrl,
511
+ this.config.network,
512
+ normalizedHook,
513
+ hookData
514
+ );
515
+ } catch (error) {
516
+ if (error instanceof Error) {
517
+ throw new FacilitatorError(
518
+ `Failed to calculate facilitator fee: ${error.message}`,
519
+ "FEE_QUERY_FAILED"
520
+ );
521
+ }
522
+ throw new FacilitatorError(
523
+ "Failed to calculate facilitator fee: Unknown error",
524
+ "UNKNOWN_ERROR"
525
+ );
526
+ }
527
+ }
528
+ /**
529
+ * Wait for transaction confirmation
530
+ *
531
+ * @param txHash - Transaction hash to wait for
532
+ * @returns Transaction receipt
533
+ *
534
+ * @throws TransactionError if transaction fails or times out
535
+ *
536
+ * @example
537
+ * ```typescript
538
+ * const receipt = await client.waitForTransaction('0x...');
539
+ * console.log('Status:', receipt.status);
540
+ * ```
541
+ */
542
+ async waitForTransaction(txHash) {
543
+ const publicClient = this.config.wallet;
544
+ if (typeof publicClient.waitForTransactionReceipt !== "function") {
545
+ throw new Error(
546
+ "Wallet client does not support waitForTransactionReceipt. Please use a viem PublicClient or WalletClient with public actions."
547
+ );
548
+ }
549
+ try {
550
+ const receipt = await publicClient.waitForTransactionReceipt({
551
+ hash: txHash,
552
+ timeout: this.config.confirmationTimeout
553
+ });
554
+ if (receipt.status !== "success") {
555
+ throw new Error(`Transaction failed with status: ${receipt.status}`);
556
+ }
557
+ return receipt;
558
+ } catch (error) {
559
+ if (error instanceof Error) {
560
+ throw new Error(`Failed to confirm transaction: ${error.message}`);
561
+ }
562
+ throw new Error("Failed to confirm transaction: Unknown error");
563
+ }
564
+ }
565
+ /**
566
+ * Get the network name
567
+ */
568
+ get network() {
569
+ return this.config.network;
570
+ }
571
+ /**
572
+ * Get the facilitator URL
573
+ */
574
+ get facilitatorUrl() {
575
+ return this.config.facilitatorUrl;
576
+ }
577
+ /**
578
+ * Get the wallet client
579
+ */
580
+ get wallet() {
581
+ return this.config.wallet;
582
+ }
583
+ };
584
+ var CHAIN_ID_TO_NETWORK = {
585
+ 84532: "base-sepolia",
586
+ 8453: "base",
587
+ 196: "x-layer",
588
+ 1952: "x-layer-testnet"
589
+ };
590
+ function useX402Client(config) {
591
+ const { data: walletClient } = useWalletClient();
592
+ const { isConnected } = useAccount();
593
+ const chainId = useChainId();
594
+ return useMemo(() => {
595
+ if (!isConnected || !walletClient) {
596
+ return null;
597
+ }
598
+ const network = config?.network || CHAIN_ID_TO_NETWORK[chainId];
599
+ if (!network) {
600
+ console.warn(
601
+ `[x402x] Unknown chainId ${chainId}. Please provide network name explicitly in config.`
602
+ );
603
+ return null;
604
+ }
605
+ try {
606
+ const extendedWallet = walletClient.extend(publicActions);
607
+ return new X402Client({
608
+ wallet: extendedWallet,
609
+ network,
610
+ facilitatorUrl: config?.facilitatorUrl,
611
+ networkConfig: config?.networkConfig,
612
+ timeout: config?.timeout,
613
+ confirmationTimeout: config?.confirmationTimeout
614
+ });
615
+ } catch (error) {
616
+ console.error("[x402x] Failed to create X402Client:", error);
617
+ return null;
618
+ }
619
+ }, [
620
+ isConnected,
621
+ walletClient,
622
+ chainId,
623
+ config?.network,
624
+ config?.facilitatorUrl,
625
+ config?.networkConfig,
626
+ config?.timeout,
627
+ config?.confirmationTimeout
628
+ ]);
629
+ }
630
+ function useExecute(config) {
631
+ const client = useX402Client(config);
632
+ const [status, setStatus] = useState("idle");
633
+ const [error, setError] = useState(null);
634
+ const [result, setResult] = useState(null);
635
+ const execute = useCallback(
636
+ async (params, waitForConfirmation = true) => {
637
+ if (!client) {
638
+ throw new Error("X402Client not available. Please connect your wallet.");
639
+ }
640
+ setStatus("preparing");
641
+ setError(null);
642
+ setResult(null);
643
+ try {
644
+ const executeResult = await client.execute(params, waitForConfirmation);
645
+ setStatus("success");
646
+ setResult(executeResult);
647
+ return executeResult;
648
+ } catch (err) {
649
+ const error2 = err instanceof Error ? err : new Error("Unknown error");
650
+ setStatus("error");
651
+ setError(error2);
652
+ throw error2;
653
+ }
654
+ },
655
+ [client]
656
+ );
657
+ const reset = useCallback(() => {
658
+ setStatus("idle");
659
+ setError(null);
660
+ setResult(null);
661
+ }, []);
662
+ return {
663
+ execute,
664
+ status,
665
+ error,
666
+ result,
667
+ reset,
668
+ isExecuting: ["preparing", "signing", "submitting", "confirming"].includes(status),
669
+ isSuccess: status === "success",
670
+ isError: status === "error"
671
+ };
672
+ }
673
+
674
+ export { DEFAULT_FACILITATOR_URL, FacilitatorError, NetworkError, SigningError, TransactionError, ValidationError, X402Client, X402ClientError, calculateTimeWindow, formatFacilitatorUrl, generateSalt, normalizeAddress, prepareSettlement, settle, signAuthorization, useExecute, useX402Client };
675
+ //# sourceMappingURL=index.js.map
676
+ //# sourceMappingURL=index.js.map