@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/README.md ADDED
@@ -0,0 +1,712 @@
1
+ # @x402x/client
2
+
3
+ > **Client SDK for x402x Serverless Mode** - Execute on-chain contracts directly via facilitator without needing a resource server.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@x402x/client.svg)](https://www.npmjs.com/package/@x402x/client)
6
+ [![License](https://img.shields.io/npm/l/@x402x/client.svg)](https://github.com/nuwa-protocol/x402-exec/blob/main/LICENSE)
7
+
8
+ ## What is x402x Serverless Mode?
9
+
10
+ x402x extends the [x402 protocol](https://github.com/coinbase/x402) with two integration modes:
11
+
12
+ ### 🏢 Server Mode (Traditional x402)
13
+
14
+ ```
15
+ Client → Resource Server → Facilitator → Blockchain
16
+ ```
17
+
18
+ - Requires deploying and maintaining a backend server
19
+ - Suitable for complex business logic (dynamic pricing, inventory management)
20
+
21
+ ### ⚡ Serverless Mode (x402x - This SDK)
22
+
23
+ ```
24
+ Client → Facilitator → Smart Contract (Hook)
25
+ ```
26
+
27
+ - **Zero servers** - No backend needed
28
+ - **Zero runtime** - Business logic in smart contracts (Hooks)
29
+ - **Zero complexity** - 3 lines of code to integrate
30
+ - **Permissionless** - Facilitators are completely trustless
31
+
32
+ ## Why Use This SDK?
33
+
34
+ ### Before (Manual Implementation)
35
+
36
+ 200+ lines of boilerplate code to:
37
+
38
+ - Handle 402 responses
39
+ - Calculate commitment hashes
40
+ - Sign EIP-3009 authorizations
41
+ - Encode payment payloads
42
+ - Call facilitator APIs
43
+
44
+ ### After (@x402x/client)
45
+
46
+ ```typescript
47
+ import { parseDefaultAssetAmount } from "@x402x/core";
48
+
49
+ const atomicAmount = parseDefaultAssetAmount("1", network); // '1000000'
50
+ const client = new X402Client({ wallet, network, facilitatorUrl });
51
+ const result = await client.execute({
52
+ hook: TransferHook.address,
53
+ amount: atomicAmount, // Must be atomic units
54
+ payTo: "0x...",
55
+ });
56
+ ```
57
+
58
+ **98% less code. 100% type-safe. Production-ready.**
59
+
60
+ ---
61
+
62
+ ## Quick Start
63
+
64
+ ### Installation
65
+
66
+ ```bash
67
+ npm install @x402x/client @x402x/core
68
+ # or
69
+ pnpm add @x402x/client @x402x/core
70
+ # or
71
+ yarn add @x402x/client @x402x/core
72
+ ```
73
+
74
+ ### Basic Usage (React + wagmi)
75
+
76
+ ```typescript
77
+ import { X402Client } from '@x402x/client';
78
+ import { TransferHook, parseDefaultAssetAmount } from '@x402x/core';
79
+ import { useWalletClient } from 'wagmi';
80
+ import { publicActions } from 'viem';
81
+
82
+ function PayButton() {
83
+ const { data: wallet } = useWalletClient();
84
+
85
+ const handlePay = async () => {
86
+ // Extend wallet with public actions (required for transaction confirmation)
87
+ const extendedWallet = wallet.extend(publicActions);
88
+
89
+ // Uses default facilitator at https://facilitator.x402x.dev/
90
+ const client = new X402Client({
91
+ wallet: extendedWallet,
92
+ network: 'base-sepolia'
93
+ });
94
+
95
+ // Convert USD amount to atomic units
96
+ const atomicAmount = parseDefaultAssetAmount('1', 'base-sepolia'); // '1000000'
97
+
98
+ const result = await client.execute({
99
+ hook: TransferHook.getAddress('base-sepolia'),
100
+ hookData: TransferHook.encode(),
101
+ amount: atomicAmount, // Must be atomic units
102
+ payTo: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1'
103
+ });
104
+
105
+ console.log('Transaction:', result.txHash);
106
+ };
107
+
108
+ return <button onClick={handlePay}>Pay 1 USDC</button>;
109
+ }
110
+ ```
111
+
112
+ > **Note**: The wallet client must be extended with `publicActions` from viem to support transaction confirmation via `waitForTransactionReceipt`. If you're using the React hooks (`useX402Client`), this is done automatically.
113
+
114
+ ---
115
+
116
+ ## Amount Handling
117
+
118
+ The `amount` parameter in `client.execute()` and `prepareSettlement()` **must be in atomic units** (the smallest unit of the token). This follows the same pattern as viem's `parseEther()` and ethers' `parseEther()`.
119
+
120
+ ### Converting USD Amounts to Atomic Units
121
+
122
+ Use `parseDefaultAssetAmount()` from `@x402x/core` to convert USD amounts:
123
+
124
+ ```typescript
125
+ import { parseDefaultAssetAmount, formatDefaultAssetAmount } from "@x402x/core";
126
+
127
+ // Convert USD to atomic units
128
+ const atomicAmount = parseDefaultAssetAmount("1", "base-sepolia"); // '1000000' (1 USDC)
129
+ const largeAmount = parseDefaultAssetAmount("100", "base-sepolia"); // '100000000' (100 USDC)
130
+
131
+ // Convert atomic units back to USD (for display)
132
+ const displayAmount = formatDefaultAssetAmount("1000000", "base-sepolia"); // '1'
133
+ ```
134
+
135
+ ### Why Atomic Units?
136
+
137
+ - **Consistency**: Matches viem/ethers standard practice
138
+ - **Precision**: Avoids floating-point precision issues
139
+ - **Clarity**: No ambiguity about what unit is expected
140
+ - **Safety**: Prevents double conversion bugs
141
+
142
+ ### Example
143
+
144
+ ```typescript
145
+ import { X402Client } from "@x402x/client";
146
+ import { parseDefaultAssetAmount } from "@x402x/core";
147
+
148
+ const client = new X402Client({ wallet, network: "base-sepolia" });
149
+
150
+ // ✅ Correct: Convert first, then pass atomic units
151
+ const atomicAmount = parseDefaultAssetAmount("5", "base-sepolia");
152
+ await client.execute({ amount: atomicAmount, payTo: "0x..." });
153
+
154
+ // ❌ Wrong: Don't pass USD amounts directly
155
+ await client.execute({ amount: "5", payTo: "0x..." }); // Will fail validation
156
+ ```
157
+
158
+ ---
159
+
160
+ ## API Reference
161
+
162
+ ### High-Level API (Recommended)
163
+
164
+ #### X402Client
165
+
166
+ The main client class that handles the entire settlement flow.
167
+
168
+ ```typescript
169
+ class X402Client {
170
+ constructor(config: X402ClientConfig);
171
+ execute(params: ExecuteParams): Promise<ExecuteResult>;
172
+ calculateFee(hook: Address, hookData?: Hex): Promise<FeeCalculationResult>;
173
+ waitForTransaction(txHash: Hex): Promise<TransactionReceipt>;
174
+ }
175
+ ```
176
+
177
+ **Example:**
178
+
179
+ ```typescript
180
+ import { X402Client } from "@x402x/client";
181
+
182
+ // Uses default facilitator at https://facilitator.x402x.dev/
183
+ const client = new X402Client({
184
+ wallet: walletClient,
185
+ network: "base-sepolia",
186
+ });
187
+
188
+ // Or specify custom facilitator
189
+ const client = new X402Client({
190
+ wallet: walletClient,
191
+ network: "base-sepolia",
192
+ facilitatorUrl: "https://custom-facilitator.example.com",
193
+ timeout: 30000, // optional
194
+ confirmationTimeout: 60000, // optional
195
+ });
196
+
197
+ // Convert USD amount to atomic units
198
+ import { parseDefaultAssetAmount } from "@x402x/core";
199
+ const atomicAmount = parseDefaultAssetAmount("1", "base-sepolia"); // '1000000'
200
+
201
+ const result = await client.execute({
202
+ hook: "0x...",
203
+ hookData: "0x...",
204
+ amount: atomicAmount, // Must be atomic units
205
+ payTo: "0x...",
206
+ facilitatorFee: "10000", // optional, will query if not provided (also atomic units)
207
+ customSalt: "0x...", // optional, will generate if not provided
208
+ });
209
+ ```
210
+
211
+ #### React Hooks
212
+
213
+ ##### useX402Client
214
+
215
+ Automatically creates an X402Client using wagmi's wallet connection.
216
+
217
+ ```typescript
218
+ import { useX402Client } from '@x402x/client';
219
+
220
+ function MyComponent() {
221
+ // Uses default facilitator at https://facilitator.x402x.dev/
222
+ const client = useX402Client();
223
+
224
+ // Or specify custom facilitator
225
+ const client = useX402Client({
226
+ facilitatorUrl: 'https://custom-facilitator.example.com'
227
+ });
228
+
229
+ if (!client) {
230
+ return <div>Please connect your wallet</div>;
231
+ }
232
+
233
+ // Use client...
234
+ }
235
+ ```
236
+
237
+ ##### useExecute
238
+
239
+ Provides automatic state management for settlements.
240
+
241
+ ```typescript
242
+ import { useExecute } from '@x402x/client';
243
+ import { parseDefaultAssetAmount } from '@x402x/core';
244
+
245
+ function PayButton() {
246
+ // Uses default facilitator at https://facilitator.x402x.dev/
247
+ const { execute, status, error, result } = useExecute();
248
+
249
+ // Or specify custom facilitator
250
+ const { execute, status, error, result } = useExecute({
251
+ facilitatorUrl: 'https://custom-facilitator.example.com'
252
+ });
253
+
254
+ const handlePay = async () => {
255
+ // Convert USD amount to atomic units
256
+ const atomicAmount = parseDefaultAssetAmount('1', 'base-sepolia'); // '1000000'
257
+
258
+ await execute({
259
+ hook: '0x...',
260
+ amount: atomicAmount, // Must be atomic units
261
+ payTo: '0x...'
262
+ });
263
+ };
264
+
265
+ return (
266
+ <div>
267
+ <button onClick={handlePay} disabled={status !== 'idle'}>
268
+ {status === 'idle' ? 'Pay' : 'Processing...'}
269
+ </button>
270
+ {status === 'success' && <div>✅ TX: {result.txHash}</div>}
271
+ {status === 'error' && <div>❌ {error.message}</div>}
272
+ </div>
273
+ );
274
+ }
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Terminology
280
+
281
+ Understanding the x402 protocol terminology used in this SDK:
282
+
283
+ ### verify
284
+
285
+ **Verify** (from x402 protocol) - Validate a payment payload without executing it on-chain. This is useful for pre-validation before actual settlement.
286
+
287
+ - In x402 protocol: `POST /verify` endpoint
288
+ - In @x402x/core: `verify()` function
289
+ - Use case: Check if payment is valid before committing resources
290
+
291
+ ### settle
292
+
293
+ **Settle** (from x402 protocol) - Execute a payment on-chain by submitting it to the blockchain. This is the actual payment execution step.
294
+
295
+ - In x402 protocol: `POST /settle` endpoint
296
+ - In @x402x/core: `settle()` function
297
+ - In @x402x/client: `settle()` function (convenience wrapper)
298
+ - Use case: Submit signed payment for blockchain execution
299
+
300
+ ### execute
301
+
302
+ **Execute** (high-level API) - Complete end-to-end payment flow including preparation, signing, settlement, and confirmation.
303
+
304
+ - In @x402x/client: `X402Client.execute()` method
305
+ - Flow: `prepare → sign → settle → wait for confirmation`
306
+ - Use case: One-line payment execution for most developers
307
+
308
+ ### API Hierarchy
309
+
310
+ ```
311
+ High-Level (Recommended for most developers):
312
+ └─ execute() - Complete flow
313
+
314
+ Low-Level (Advanced use cases):
315
+ ├─ prepareSettlement() - Prepare data
316
+ ├─ signAuthorization() - Sign with wallet
317
+ └─ settle() - Submit to facilitator
318
+
319
+ Core Protocol (x402 standard):
320
+ ├─ verify() - Validate payment
321
+ └─ settle() - Execute payment
322
+ ```
323
+
324
+ ---
325
+
326
+ ### Low-Level API (Advanced)
327
+
328
+ For users who need full control over the settlement flow.
329
+
330
+ #### prepareSettlement
331
+
332
+ Prepares settlement data for signing.
333
+
334
+ ```typescript
335
+ import { prepareSettlement } from "@x402x/client";
336
+ import { parseDefaultAssetAmount } from "@x402x/core";
337
+
338
+ // Convert USD amount to atomic units
339
+ const atomicAmount = parseDefaultAssetAmount("1", "base-sepolia"); // '1000000'
340
+
341
+ const settlement = await prepareSettlement({
342
+ wallet: walletClient,
343
+ network: "base-sepolia",
344
+ hook: "0x...",
345
+ hookData: "0x...",
346
+ amount: atomicAmount, // Must be atomic units
347
+ payTo: "0x...",
348
+ facilitatorUrl: "https://facilitator.x402x.dev", // Optional: uses default if not provided
349
+ });
350
+ ```
351
+
352
+ #### signAuthorization
353
+
354
+ Signs EIP-3009 authorization.
355
+
356
+ ```typescript
357
+ import { signAuthorization } from "@x402x/client";
358
+
359
+ const signed = await signAuthorization(walletClient, settlement);
360
+ ```
361
+
362
+ #### settle
363
+
364
+ Submits signed authorization to facilitator.
365
+
366
+ ```typescript
367
+ import { settle } from "@x402x/client";
368
+
369
+ const result = await settle("https://facilitator.x402x.dev", signed);
370
+ ```
371
+
372
+ ---
373
+
374
+ ## Examples
375
+
376
+ ### Example 1: Simple Payment
377
+
378
+ ```typescript
379
+ import { X402Client } from "@x402x/client";
380
+ import { TransferHook, parseDefaultAssetAmount } from "@x402x/core";
381
+
382
+ // Uses default facilitator at https://facilitator.x402x.dev/
383
+ const client = new X402Client({
384
+ wallet: walletClient,
385
+ network: "base-sepolia",
386
+ });
387
+
388
+ // Convert USD amount to atomic units
389
+ const atomicAmount = parseDefaultAssetAmount("1", "base-sepolia"); // '1000000'
390
+
391
+ const result = await client.execute({
392
+ hook: TransferHook.getAddress("base-sepolia"),
393
+ hookData: TransferHook.encode(), // Simple transfer mode
394
+ amount: atomicAmount, // Must be atomic units
395
+ payTo: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1",
396
+ });
397
+
398
+ console.log("Transaction:", result.txHash);
399
+ ```
400
+
401
+ ### Example 2: Distributed Transfer (Payroll, Revenue Split)
402
+
403
+ TransferHook supports distributing funds to multiple recipients by percentage:
404
+
405
+ ```typescript
406
+ import { X402Client } from "@x402x/client";
407
+ import { TransferHook, parseDefaultAssetAmount, type Split } from "@x402x/core";
408
+
409
+ const client = new X402Client({
410
+ wallet: walletClient,
411
+ network: "base-sepolia",
412
+ });
413
+
414
+ // Payroll example: Pay 3 employees with different shares
415
+ const payrollAmount = parseDefaultAssetAmount("10", "base-sepolia"); // '10000000' (10 USDC)
416
+ const result = await client.execute({
417
+ hook: TransferHook.getAddress("base-sepolia"),
418
+ hookData: TransferHook.encode([
419
+ { recipient: "0xEmployee1...", bips: 3000 }, // 30%
420
+ { recipient: "0xEmployee2...", bips: 4000 }, // 40%
421
+ { recipient: "0xEmployee3...", bips: 3000 }, // 30%
422
+ ]),
423
+ amount: payrollAmount, // Must be atomic units
424
+ payTo: "0xCompany...", // Receives remainder (0% in this case)
425
+ });
426
+
427
+ // Revenue split example: Platform takes 30%, creator gets 70%
428
+ const revenueAmount = parseDefaultAssetAmount("100", "base-sepolia"); // '100000000' (100 USDC)
429
+ const result2 = await client.execute({
430
+ hook: TransferHook.getAddress("base-sepolia"),
431
+ hookData: TransferHook.encode([
432
+ { recipient: "0xPlatform...", bips: 3000 }, // 30%
433
+ ]),
434
+ amount: revenueAmount, // Must be atomic units
435
+ payTo: "0xCreator...", // Gets remaining 70% automatically
436
+ });
437
+
438
+ console.log("Distributed transfer:", result.txHash);
439
+ ```
440
+
441
+ **Split Rules:**
442
+
443
+ - `bips` = basis points (1-10000, where 10000 = 100%)
444
+ - Total bips must be ≤ 10000
445
+ - If total < 10000, remainder goes to `recipient` parameter
446
+ - If total = 10000, `recipient` gets 0
447
+
448
+ ### Example 3: NFT Minting (React)
449
+
450
+ ```typescript
451
+ import { useExecute } from '@x402x/client';
452
+ import { NFTMintHook, parseDefaultAssetAmount } from '@x402x/core';
453
+
454
+ function MintNFT() {
455
+ // Uses default facilitator
456
+ const { execute, status, error } = useExecute();
457
+
458
+ const handleMint = async () => {
459
+ // Convert USD amount to atomic units
460
+ const atomicAmount = parseDefaultAssetAmount('5', 'base-sepolia'); // '5000000'
461
+
462
+ const result = await execute({
463
+ hook: NFTMintHook.getAddress('base-sepolia'),
464
+ hookData: NFTMintHook.encode({
465
+ collection: '0x...',
466
+ tokenId: 1
467
+ }),
468
+ amount: atomicAmount, // Must be atomic units
469
+ payTo: '0x...'
470
+ });
471
+
472
+ alert(`NFT Minted! TX: ${result.txHash}`);
473
+ };
474
+
475
+ return (
476
+ <button onClick={handleMint} disabled={status !== 'idle'}>
477
+ {status === 'idle' ? 'Mint NFT for 5 USDC' : 'Processing...'}
478
+ </button>
479
+ );
480
+ }
481
+ ```
482
+
483
+ ### Example 4: Revenue Split (Low-Level API)
484
+
485
+ ```typescript
486
+ import { prepareSettlement, signAuthorization, settle } from "@x402x/client";
487
+ import { calculateFacilitatorFee, TransferHook, parseDefaultAssetAmount } from "@x402x/core";
488
+
489
+ // 1. Query minimum fee
490
+ const hookData = TransferHook.encode([
491
+ { recipient: "0xAlice...", bips: 6000 }, // 60% to Alice
492
+ { recipient: "0xBob...", bips: 4000 }, // 40% to Bob
493
+ ]);
494
+
495
+ const feeEstimate = await calculateFacilitatorFee(
496
+ "https://facilitator.x402x.dev",
497
+ "base-sepolia",
498
+ TransferHook.getAddress("base-sepolia"),
499
+ hookData,
500
+ );
501
+
502
+ // 2. Convert USD amount to atomic units
503
+ const atomicAmount = parseDefaultAssetAmount("10", "base-sepolia"); // '10000000'
504
+
505
+ // 3. Prepare settlement
506
+ const settlement = await prepareSettlement({
507
+ wallet: walletClient,
508
+ network: "base-sepolia",
509
+ hook: TransferHook.getAddress("base-sepolia"),
510
+ hookData,
511
+ amount: atomicAmount, // Must be atomic units
512
+ payTo: "0xCharity...", // Receives 0% (full split)
513
+ facilitatorFee: feeEstimate.facilitatorFee,
514
+ });
515
+
516
+ // 3. Sign authorization
517
+ const signed = await signAuthorization(walletClient, settlement);
518
+
519
+ // 4. Submit to facilitator
520
+ const result = await settle("https://facilitator.x402x.dev", signed);
521
+
522
+ console.log("Transaction:", result.transaction);
523
+ ```
524
+
525
+ ### Example 5: Vue 3 Integration
526
+
527
+ ```typescript
528
+ import { ref } from "vue";
529
+ import { X402Client } from "@x402x/client";
530
+ import { TransferHook, parseDefaultAssetAmount } from "@x402x/core";
531
+
532
+ export function usePayment() {
533
+ const status = ref("idle");
534
+ const error = ref(null);
535
+
536
+ const pay = async (walletClient, usdAmount, recipient, network = "base-sepolia") => {
537
+ status.value = "processing";
538
+ error.value = null;
539
+
540
+ try {
541
+ const client = new X402Client({
542
+ wallet: walletClient,
543
+ network,
544
+ facilitatorUrl: import.meta.env.VITE_FACILITATOR_URL,
545
+ });
546
+
547
+ // Convert USD amount to atomic units
548
+ const atomicAmount = parseDefaultAssetAmount(usdAmount, network);
549
+
550
+ const result = await client.execute({
551
+ hook: TransferHook.getAddress(network),
552
+ hookData: TransferHook.encode(),
553
+ amount: atomicAmount, // Must be atomic units
554
+ payTo: recipient,
555
+ });
556
+
557
+ status.value = "success";
558
+ return result;
559
+ } catch (err) {
560
+ error.value = err;
561
+ status.value = "error";
562
+ throw err;
563
+ }
564
+ };
565
+
566
+ return { status, error, pay };
567
+ }
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Error Handling
573
+
574
+ The SDK provides typed error classes for better error handling:
575
+
576
+ ```typescript
577
+ import {
578
+ X402ClientError,
579
+ NetworkError,
580
+ SigningError,
581
+ FacilitatorError,
582
+ TransactionError,
583
+ ValidationError,
584
+ } from "@x402x/client";
585
+
586
+ try {
587
+ await client.execute(params);
588
+ } catch (error) {
589
+ if (error instanceof ValidationError) {
590
+ console.error("Invalid parameters:", error.message);
591
+ } else if (error instanceof SigningError) {
592
+ if (error.code === "USER_REJECTED") {
593
+ console.log("User rejected signing");
594
+ }
595
+ } else if (error instanceof FacilitatorError) {
596
+ console.error("Facilitator error:", error.statusCode, error.response);
597
+ } else if (error instanceof TransactionError) {
598
+ console.error("Transaction failed:", error.txHash);
599
+ }
600
+ }
601
+ ```
602
+
603
+ ---
604
+
605
+ ## TypeScript Support
606
+
607
+ Full TypeScript support with comprehensive type definitions:
608
+
609
+ ```typescript
610
+ import type {
611
+ X402ClientConfig,
612
+ ExecuteParams,
613
+ ExecuteResult,
614
+ SettlementData,
615
+ SignedAuthorization,
616
+ FeeCalculationResult,
617
+ ExecuteStatus,
618
+ } from "@x402x/client";
619
+ ```
620
+
621
+ ---
622
+
623
+ ## Supported Networks
624
+
625
+ - Base Sepolia (testnet): `base-sepolia`
626
+ - Base (mainnet): `base`
627
+ - X-Layer (mainnet): `x-layer`
628
+ - X-Layer Testnet: `x-layer-testnet`
629
+
630
+ ---
631
+
632
+ ## Requirements
633
+
634
+ - Node.js 18+
635
+ - React 18+ (for hooks)
636
+ - wagmi 2+ (for wallet connection)
637
+ - viem 2+ (for Ethereum interactions)
638
+
639
+ ---
640
+
641
+ ## Migration from Manual Implementation
642
+
643
+ ### Before
644
+
645
+ ```typescript
646
+ // 200+ lines of manual implementation
647
+ import { usePayment } from "./hooks/usePayment";
648
+
649
+ function Component() {
650
+ const { pay, status, error } = usePayment();
651
+
652
+ const handlePay = () => {
653
+ pay("/api/transfer", "base-sepolia", { amount: "1000000" });
654
+ };
655
+ }
656
+ ```
657
+
658
+ ### After
659
+
660
+ ```typescript
661
+ // 10 lines with @x402x/client (no facilitatorUrl needed!)
662
+ import { useExecute } from "@x402x/client";
663
+ import { TransferHook, parseDefaultAssetAmount } from "@x402x/core";
664
+
665
+ function Component() {
666
+ // Uses default facilitator automatically
667
+ const { execute, status, error } = useExecute();
668
+
669
+ const handlePay = async () => {
670
+ // Convert USD amount to atomic units
671
+ const atomicAmount = parseDefaultAssetAmount("1", "base-sepolia"); // '1000000'
672
+
673
+ await execute({
674
+ hook: TransferHook.address,
675
+ amount: atomicAmount, // Must be atomic units
676
+ payTo: "0x...",
677
+ });
678
+ };
679
+ }
680
+ ```
681
+
682
+ ---
683
+
684
+ ## Related Packages
685
+
686
+ - [@x402x/core](../core) - Core utilities and commitment calculation
687
+ - [@x402x/facilitator](../../facilitator) - Facilitator server implementation
688
+ - [x402](https://github.com/coinbase/x402) - Base x402 protocol
689
+
690
+ ---
691
+
692
+ ## Contributing
693
+
694
+ See [CONTRIBUTING.md](../../CONTRIBUTING.md) for development setup and contribution guidelines.
695
+
696
+ ---
697
+
698
+ ## License
699
+
700
+ Apache-2.0 - see [LICENSE](../../LICENSE) for details.
701
+
702
+ ---
703
+
704
+ ## Support
705
+
706
+ - [Documentation](https://x402x.dev/)
707
+ - [GitHub Issues](https://github.com/nuwa-protocol/x402-exec/issues)
708
+ - [Discord Community](https://discord.gg/nuwa-protocol)
709
+
710
+ ---
711
+
712
+ **Built with ❤️ by [Nuwa Protocol](https://github.com/nuwa-protocol)**