@bankofai/x402-extensions 2.6.0-beta.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.
Files changed (34) hide show
  1. package/README.md +792 -0
  2. package/dist/cjs/bazaar/index.d.ts +3 -0
  3. package/dist/cjs/bazaar/index.js +629 -0
  4. package/dist/cjs/bazaar/index.js.map +1 -0
  5. package/dist/cjs/index-DPJQYYGe.d.ts +662 -0
  6. package/dist/cjs/index.d.ts +360 -0
  7. package/dist/cjs/index.js +1880 -0
  8. package/dist/cjs/index.js.map +1 -0
  9. package/dist/cjs/payment-identifier/index.d.ts +345 -0
  10. package/dist/cjs/payment-identifier/index.js +285 -0
  11. package/dist/cjs/payment-identifier/index.js.map +1 -0
  12. package/dist/cjs/sign-in-with-x/index.d.ts +1091 -0
  13. package/dist/cjs/sign-in-with-x/index.js +845 -0
  14. package/dist/cjs/sign-in-with-x/index.js.map +1 -0
  15. package/dist/esm/bazaar/index.d.mts +3 -0
  16. package/dist/esm/bazaar/index.mjs +33 -0
  17. package/dist/esm/bazaar/index.mjs.map +1 -0
  18. package/dist/esm/chunk-MTWK6ERV.mjs +777 -0
  19. package/dist/esm/chunk-MTWK6ERV.mjs.map +1 -0
  20. package/dist/esm/chunk-RERA4OZZ.mjs +233 -0
  21. package/dist/esm/chunk-RERA4OZZ.mjs.map +1 -0
  22. package/dist/esm/chunk-UHTNEDIJ.mjs +580 -0
  23. package/dist/esm/chunk-UHTNEDIJ.mjs.map +1 -0
  24. package/dist/esm/index-DPJQYYGe.d.mts +662 -0
  25. package/dist/esm/index.d.mts +360 -0
  26. package/dist/esm/index.mjs +323 -0
  27. package/dist/esm/index.mjs.map +1 -0
  28. package/dist/esm/payment-identifier/index.d.mts +345 -0
  29. package/dist/esm/payment-identifier/index.mjs +39 -0
  30. package/dist/esm/payment-identifier/index.mjs.map +1 -0
  31. package/dist/esm/sign-in-with-x/index.d.mts +1091 -0
  32. package/dist/esm/sign-in-with-x/index.mjs +71 -0
  33. package/dist/esm/sign-in-with-x/index.mjs.map +1 -0
  34. package/package.json +99 -0
package/README.md ADDED
@@ -0,0 +1,792 @@
1
+ # @x402/extensions
2
+
3
+ x402 Payment Protocol Extensions. This package provides optional extensions that enhance the x402 payment protocol with additional functionality.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm install @x402/extensions
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ Extensions are optional features that can be added to x402 payment flows. They follow a standardized `{ info, schema }` structure and are included in `PaymentRequired.extensions` and `PaymentPayload.extensions`.
14
+
15
+ This package includes:
16
+ - **Bazaar Discovery**: Automatic cataloging and indexing of x402-enabled resources
17
+ - **Sign-In-With-X (SIWx)**: CAIP-122 wallet authentication for accessing previously purchased resources
18
+
19
+ ## Bazaar Discovery Extension
20
+
21
+ The Bazaar Discovery Extension enables facilitators to automatically catalog and index x402-enabled resources by following server-declared discovery instructions. This allows users to discover paid APIs and services through facilitator catalogs.
22
+
23
+ ### How It Works
24
+
25
+ 1. **Servers** declare discovery metadata when configuring their payment endpoints
26
+ 2. The HTTP method is automatically inferred from the route definition (e.g., `"GET /weather"`)
27
+ 3. **Facilitators** extract this metadata from payment requests
28
+ 4. **Users** can browse and discover available paid resources through facilitator catalogs
29
+
30
+ ### For Resource Servers
31
+
32
+ Declare endpoint discovery metadata in your payment middleware configuration. This helps facilitators understand how to call your endpoints and what they return.
33
+
34
+ > **Note:** The HTTP method is automatically inferred from the route key (e.g., `"GET /weather"` → GET method). You don't need to specify it in `declareDiscoveryExtension`.
35
+
36
+ #### Basic Example: GET Endpoint with Query Parameters
37
+
38
+ ```typescript
39
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
40
+
41
+ const resources = {
42
+ "GET /weather": {
43
+ accepts: {
44
+ scheme: "exact",
45
+ price: "$0.001",
46
+ network: "eip155:84532",
47
+ payTo: "0xYourAddress"
48
+ },
49
+ extensions: {
50
+ ...declareDiscoveryExtension({
51
+ input: { city: "San Francisco" },
52
+ inputSchema: {
53
+ properties: {
54
+ city: { type: "string" },
55
+ units: { type: "string", enum: ["celsius", "fahrenheit"] }
56
+ },
57
+ required: ["city"]
58
+ },
59
+ output: {
60
+ example: {
61
+ city: "San Francisco",
62
+ weather: "foggy",
63
+ temperature: 15,
64
+ humidity: 85
65
+ }
66
+ },
67
+ }),
68
+ },
69
+ },
70
+ };
71
+ ```
72
+
73
+ #### Example: POST Endpoint with JSON Body
74
+
75
+ For POST, PUT, and PATCH endpoints, specify `bodyType` to indicate the request body format:
76
+
77
+ ```typescript
78
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
79
+
80
+ const resources = {
81
+ "POST /api/translate": {
82
+ accepts: {
83
+ scheme: "exact",
84
+ price: "$0.01",
85
+ network: "eip155:84532",
86
+ payTo: "0xYourAddress"
87
+ },
88
+ extensions: {
89
+ ...declareDiscoveryExtension({
90
+ input: {
91
+ text: "Hello, world!",
92
+ targetLanguage: "es"
93
+ },
94
+ inputSchema: {
95
+ properties: {
96
+ text: { type: "string" },
97
+ targetLanguage: { type: "string", pattern: "^[a-z]{2}$" }
98
+ },
99
+ required: ["text", "targetLanguage"]
100
+ },
101
+ bodyType: "json",
102
+ output: {
103
+ example: {
104
+ translatedText: "¡Hola, mundo!",
105
+ sourceLanguage: "en",
106
+ targetLanguage: "es"
107
+ }
108
+ },
109
+ }),
110
+ },
111
+ },
112
+ };
113
+ ```
114
+
115
+ #### Example: PUT Endpoint with Form Data
116
+
117
+ ```typescript
118
+ const resources = {
119
+ "PUT /api/user/profile": {
120
+ accepts: {
121
+ scheme: "exact",
122
+ price: "$0.05",
123
+ network: "eip155:84532",
124
+ payTo: "0xYourAddress"
125
+ },
126
+ extensions: {
127
+ ...declareDiscoveryExtension({
128
+ input: {
129
+ name: "John Doe",
130
+ email: "john@example.com",
131
+ bio: "Software developer"
132
+ },
133
+ inputSchema: {
134
+ properties: {
135
+ name: { type: "string", minLength: 1 },
136
+ email: { type: "string", format: "email" },
137
+ bio: { type: "string", maxLength: 500 }
138
+ },
139
+ required: ["name", "email"]
140
+ },
141
+ bodyType: "form-data",
142
+ output: {
143
+ example: {
144
+ success: true,
145
+ userId: "123",
146
+ updatedAt: "2024-01-01T00:00:00Z"
147
+ }
148
+ },
149
+ }),
150
+ },
151
+ },
152
+ };
153
+ ```
154
+
155
+ #### Example: DELETE Endpoint
156
+
157
+ ```typescript
158
+ const resources = {
159
+ "DELETE /api/data/:id": {
160
+ accepts: {
161
+ scheme: "exact",
162
+ price: "$0.001",
163
+ network: "eip155:84532",
164
+ payTo: "0xYourAddress"
165
+ },
166
+ extensions: {
167
+ ...declareDiscoveryExtension({
168
+ input: { id: "123" },
169
+ inputSchema: {
170
+ properties: {
171
+ id: { type: "string" }
172
+ },
173
+ required: ["id"]
174
+ },
175
+ output: {
176
+ example: {
177
+ success: true,
178
+ deletedId: "123"
179
+ }
180
+ },
181
+ }),
182
+ },
183
+ },
184
+ };
185
+ ```
186
+
187
+ #### Example: MCP Tool
188
+
189
+ For MCP (Model Context Protocol) tools, use the `toolName` field instead of `bodyType`/`input`. The HTTP method is not relevant -- MCP tools are invoked by name.
190
+
191
+ ```typescript
192
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
193
+
194
+ const resources = {
195
+ "POST /mcp": {
196
+ accepts: {
197
+ scheme: "exact",
198
+ price: "$0.01",
199
+ network: "eip155:84532",
200
+ payTo: "0xYourAddress"
201
+ },
202
+ extensions: {
203
+ ...declareDiscoveryExtension({
204
+ toolName: "financial_analysis",
205
+ description: "Analyze financial data for a given ticker",
206
+ inputSchema: {
207
+ type: "object",
208
+ properties: {
209
+ ticker: { type: "string", description: "Stock ticker symbol" },
210
+ analysis_type: {
211
+ type: "string",
212
+ enum: ["fundamental", "technical", "sentiment"],
213
+ },
214
+ },
215
+ required: ["ticker"],
216
+ },
217
+ example: { ticker: "AAPL", analysis_type: "fundamental" },
218
+ output: {
219
+ example: {
220
+ pe_ratio: 28.5,
221
+ recommendation: "hold",
222
+ confidence: 0.85
223
+ }
224
+ },
225
+ }),
226
+ },
227
+ },
228
+ };
229
+ ```
230
+
231
+ You can optionally specify `transport` to indicate the MCP transport type (`"streamable-http"` or `"sse"`). When omitted, `streamable-http` is assumed per the MCP spec.
232
+
233
+ #### Using with Next.js Middleware
234
+
235
+ ```typescript
236
+ import { paymentProxy, x402ResourceServer } from "@x402/next";
237
+ import { HTTPFacilitatorClient } from "@x402/core/http";
238
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
239
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
240
+
241
+ const facilitatorClient = new HTTPFacilitatorClient({ url: "https://facilitator.x402.org" });
242
+ const resourceServer = new x402ResourceServer(facilitatorClient)
243
+ .register("eip155:84532", new ExactEvmScheme());
244
+
245
+ export const proxy = paymentProxy(
246
+ {
247
+ "/api/weather": {
248
+ accepts: {
249
+ scheme: "exact",
250
+ price: "$0.001",
251
+ network: "eip155:84532",
252
+ payTo: "0xYourAddress",
253
+ },
254
+ extensions: {
255
+ ...declareDiscoveryExtension({
256
+ input: { city: "San Francisco" },
257
+ inputSchema: {
258
+ properties: { city: { type: "string" } },
259
+ required: ["city"],
260
+ },
261
+ output: {
262
+ example: { city: "San Francisco", weather: "foggy" }
263
+ },
264
+ }),
265
+ },
266
+ },
267
+ },
268
+ resourceServer,
269
+ );
270
+ ```
271
+
272
+ ### For Facilitators
273
+
274
+ Extract discovery information from incoming payment requests to catalog resources in the Bazaar.
275
+
276
+ #### Basic Usage
277
+
278
+ ```typescript
279
+ import { extractDiscoveryInfo } from "@x402/extensions/bazaar";
280
+ import type { PaymentPayload, PaymentRequirements } from "@x402/core/types";
281
+
282
+ async function handlePayment(
283
+ paymentPayload: PaymentPayload,
284
+ paymentRequirements: PaymentRequirements
285
+ ) {
286
+ // Extract discovery info from the payment
287
+ const discovered = extractDiscoveryInfo(paymentPayload, paymentRequirements);
288
+
289
+ if (discovered) {
290
+ // discovered contains:
291
+ // {
292
+ // resourceUrl: "https://api.example.com/weather",
293
+ // method: "GET",
294
+ // x402Version: 2,
295
+ // discoveryInfo: {
296
+ // input: { type: "http", method: "GET", queryParams: { city: "..." } },
297
+ // output: { type: "json", example: { ... } }
298
+ // }
299
+ // }
300
+
301
+ // Catalog the resource in your Bazaar
302
+ await catalogResource({
303
+ url: discovered.resourceUrl,
304
+ method: discovered.method,
305
+ inputSchema: discovered.discoveryInfo.input,
306
+ outputExample: discovered.discoveryInfo.output?.example,
307
+ });
308
+ }
309
+ }
310
+ ```
311
+
312
+ #### Validating Discovery Extensions
313
+
314
+ ```typescript
315
+ import { validateDiscoveryExtension, extractDiscoveryInfo } from "@x402/extensions/bazaar";
316
+
317
+ function processPayment(paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements) {
318
+ const discovered = extractDiscoveryInfo(paymentPayload, paymentRequirements);
319
+
320
+ if (discovered && paymentPayload.extensions?.bazaar) {
321
+ // Validate the extension schema
322
+ const validation = validateDiscoveryExtension(paymentPayload.extensions.bazaar);
323
+
324
+ if (!validation.valid) {
325
+ console.warn("Invalid discovery extension:", validation.errors);
326
+ // Handle invalid extension (log, reject, etc.)
327
+ return;
328
+ }
329
+
330
+ // Extension is valid, proceed with cataloging
331
+ catalogResource(discovered);
332
+ }
333
+ }
334
+ ```
335
+
336
+ #### Using with Server Extension Helper
337
+
338
+ The `bazaarResourceServerExtension` automatically enriches discovery extensions with HTTP method information from the request context:
339
+
340
+ ```typescript
341
+ import { bazaarResourceServerExtension } from "@x402/extensions/bazaar";
342
+ import { x402ResourceServer } from "@x402/core/server";
343
+
344
+ // The extension helper automatically extracts discovery info
345
+ const resourceServer = new x402ResourceServer(facilitatorClient)
346
+ .register("eip155:84532", new ExactEvmScheme())
347
+ .registerExtension(bazaarResourceServerExtension);
348
+ ```
349
+
350
+ ### Bazaar API Reference
351
+
352
+ #### `declareDiscoveryExtension(config)`
353
+
354
+ Creates a discovery extension object for resource servers. Accepts either an HTTP endpoint config or an MCP tool config.
355
+
356
+ **HTTP Parameters:**
357
+ - `config.input` (optional): Example input values (query params for GET/HEAD/DELETE, body for POST/PUT/PATCH)
358
+ - `config.inputSchema` (optional): JSON Schema for input validation
359
+ - `config.bodyType` (required for body methods): For POST/PUT/PATCH, specify `"json"`, `"form-data"`, or `"text"`. This is how TypeScript discriminates between query methods (GET/HEAD/DELETE) and body methods.
360
+ - `config.output` (optional): Output specification
361
+ - `output.example`: Example output data
362
+ - `output.schema`: JSON Schema for output validation
363
+
364
+ > **Note:** The HTTP method is NOT passed to this function. It is automatically inferred from the route key (e.g., `"GET /weather"`) or enriched by `bazaarResourceServerExtension` at runtime.
365
+
366
+ **MCP Parameters:**
367
+ - `config.toolName` (required): MCP tool name — the presence of this field identifies the config as MCP
368
+ - `config.description` (optional): Human-readable tool description
369
+ - `config.inputSchema` (required): JSON Schema for tool arguments
370
+ - `config.example` (optional): Example tool arguments
371
+ - `config.transport` (optional): MCP transport type (`"streamable-http"` or `"sse"`). Defaults to `streamable-http` per the MCP spec when omitted.
372
+ - `config.output` (optional): Output specification
373
+ - `output.example`: Example output data
374
+ - `output.schema`: JSON Schema for output validation
375
+
376
+ **Returns:** An object with a `bazaar` key containing the discovery extension.
377
+
378
+ **Examples:**
379
+ ```typescript
380
+ // HTTP endpoint
381
+ const httpExtension = declareDiscoveryExtension({
382
+ input: { query: "search term" },
383
+ inputSchema: {
384
+ properties: { query: { type: "string" } },
385
+ required: ["query"]
386
+ },
387
+ output: {
388
+ example: { results: [] }
389
+ }
390
+ });
391
+
392
+ // MCP tool
393
+ const mcpExtension = declareDiscoveryExtension({
394
+ toolName: "search",
395
+ description: "Search for documents",
396
+ inputSchema: {
397
+ type: "object",
398
+ properties: { query: { type: "string" } },
399
+ required: ["query"]
400
+ },
401
+ output: {
402
+ example: { results: [] }
403
+ }
404
+ });
405
+ // Both return: { bazaar: { info: {...}, schema: {...} } }
406
+ ```
407
+
408
+ #### `extractDiscoveryInfo(paymentPayload, paymentRequirements, validate?)`
409
+
410
+ Extracts discovery information from a payment request (for facilitators).
411
+
412
+ **Parameters:**
413
+ - `paymentPayload`: The payment payload from the client
414
+ - `paymentRequirements`: The payment requirements from the server
415
+ - `validate` (optional): Whether to validate the extension (default: `true`)
416
+
417
+ **Returns:** `DiscoveredResource` object or `null` if not found.
418
+
419
+ ```typescript
420
+ interface DiscoveredHTTPResource {
421
+ resourceUrl: string;
422
+ method: string; // e.g. "GET", "POST"
423
+ x402Version: number;
424
+ discoveryInfo: DiscoveryInfo;
425
+ }
426
+
427
+ interface DiscoveredMCPResource {
428
+ resourceUrl: string;
429
+ toolName: string; // MCP tool name
430
+ x402Version: number;
431
+ discoveryInfo: DiscoveryInfo;
432
+ }
433
+
434
+ type DiscoveredResource = DiscoveredHTTPResource | DiscoveredMCPResource;
435
+ ```
436
+
437
+ #### `validateDiscoveryExtension(extension)`
438
+
439
+ Validates a discovery extension's info against its schema.
440
+
441
+ **Returns:** `{ valid: boolean, errors?: string[] }`
442
+
443
+ #### `validateAndExtract(extension)`
444
+
445
+ Validates and extracts discovery info in one step.
446
+
447
+ **Returns:** `{ valid: boolean, info?: DiscoveryInfo, errors?: string[] }`
448
+
449
+ #### `bazaarResourceServerExtension`
450
+
451
+ A server extension that automatically enriches HTTP discovery extensions with method information from the request context. MCP extensions are passed through unchanged.
452
+
453
+ **Usage:**
454
+ ```typescript
455
+ import { bazaarResourceServerExtension } from "@x402/extensions/bazaar";
456
+
457
+ const resourceServer = new x402ResourceServer(facilitatorClient)
458
+ .registerExtension(bazaarResourceServerExtension);
459
+ ```
460
+
461
+ ### `BAZAAR`
462
+
463
+ The extension identifier constant (`"bazaar"`).
464
+
465
+ ## Sign-In-With-X Extension
466
+
467
+ The Sign-In-With-X extension implements [CAIP-122](https://chainagnostic.org/CAIPs/caip-122) for chain-agnostic wallet authentication. It allows clients to prove control of a wallet that previously paid for a resource, enabling access without repurchase.
468
+
469
+ ### How It Works
470
+
471
+ 1. Server returns 402 with `sign-in-with-x` extension containing challenge parameters
472
+ 2. Client signs the CAIP-122 message with their wallet
473
+ 3. Client sends signed proof in `SIGN-IN-WITH-X` header
474
+ 4. Server verifies signature and grants access if wallet has previous payment
475
+
476
+ ### Server Usage
477
+
478
+ #### Recommended: Hooks (Automatic)
479
+
480
+ ```typescript
481
+ import {
482
+ declareSIWxExtension,
483
+ siwxResourceServerExtension,
484
+ createSIWxSettleHook,
485
+ createSIWxRequestHook,
486
+ InMemorySIWxStorage,
487
+ } from '@x402/extensions/sign-in-with-x';
488
+
489
+ // Storage for tracking paid addresses
490
+ const storage = new InMemorySIWxStorage();
491
+
492
+ // 1. Register extension for time-based field refreshment
493
+ const resourceServer = new x402ResourceServer(facilitatorClient)
494
+ .register(NETWORK, new ExactEvmScheme())
495
+ .registerExtension(siwxResourceServerExtension) // Refreshes nonce/timestamps per request
496
+ .onAfterSettle(createSIWxSettleHook({ storage })); // Records payments
497
+
498
+ // 2. Declare SIWX support in routes (network/domain/uri derived automatically)
499
+ const routes = {
500
+ "GET /data": {
501
+ accepts: [{scheme: "exact", price: "$0.01", network: "eip155:8453", payTo}],
502
+ extensions: declareSIWxExtension({
503
+ statement: 'Sign in to access your purchased content',
504
+ }),
505
+ },
506
+ };
507
+
508
+ // 3. Verify incoming SIWX proofs
509
+ const httpServer = new x402HTTPResourceServer(resourceServer, routes)
510
+ .onProtectedRequest(createSIWxRequestHook({ storage })); // Grants access if paid
511
+
512
+ // Optional: Enable smart wallet support (EIP-1271/EIP-6492)
513
+ import { createPublicClient, http } from 'viem';
514
+ import { base } from 'viem/chains';
515
+
516
+ const publicClient = createPublicClient({ chain: base, transport: http() });
517
+ const httpServerWithSmartWallets = new x402HTTPResourceServer(resourceServer, routes)
518
+ .onProtectedRequest(createSIWxRequestHook({
519
+ storage,
520
+ verifyOptions: { evmVerifier: publicClient.verifyMessage },
521
+ }));
522
+ ```
523
+
524
+ The hooks automatically:
525
+ - **siwxResourceServerExtension**: Derives `network` from `accepts`, `domain`/`uri` from request URL, refreshes `nonce`/`issuedAt`/`expirationTime` per request
526
+ - **createSIWxSettleHook**: Records payment when settlement succeeds
527
+ - **createSIWxRequestHook**: Validates and verifies SIWX proofs, grants access if wallet has paid
528
+
529
+ #### Manual Usage (Advanced)
530
+
531
+ ```typescript
532
+ import {
533
+ declareSIWxExtension,
534
+ parseSIWxHeader,
535
+ validateSIWxMessage,
536
+ verifySIWxSignature,
537
+ SIGN_IN_WITH_X,
538
+ } from '@x402/extensions/sign-in-with-x';
539
+
540
+ // 1. Declare in PaymentRequired response
541
+ const extensions = {
542
+ [SIGN_IN_WITH_X]: declareSIWxExtension({
543
+ domain: 'api.example.com',
544
+ resourceUri: 'https://api.example.com/data',
545
+ network: 'eip155:8453',
546
+ statement: 'Sign in to access your purchased content',
547
+ }),
548
+ };
549
+
550
+ // 2. Verify incoming proof
551
+ async function handleRequest(request: Request) {
552
+ const header = request.headers.get('SIGN-IN-WITH-X');
553
+ if (!header) return; // No auth provided
554
+
555
+ // Parse the header
556
+ const payload = parseSIWxHeader(header);
557
+
558
+ // Validate message fields (expiry, nonce, domain, etc.)
559
+ const validation = await validateSIWxMessage(
560
+ payload,
561
+ 'https://api.example.com/data'
562
+ );
563
+ if (!validation.valid) {
564
+ return { error: validation.error };
565
+ }
566
+
567
+ // Verify signature and recover address
568
+ const verification = await verifySIWxSignature(payload);
569
+ if (!verification.valid) {
570
+ return { error: verification.error };
571
+ }
572
+
573
+ // verification.address is the verified wallet
574
+ // Check if this wallet has paid before
575
+ const hasPaid = await checkPaymentHistory(verification.address);
576
+ if (hasPaid) {
577
+ // Grant access without payment
578
+ }
579
+ }
580
+ ```
581
+
582
+ ### Client Usage
583
+
584
+ #### Recommended: Client Hook (Automatic)
585
+
586
+ ```typescript
587
+ import { createSIWxClientHook } from '@x402/extensions/sign-in-with-x';
588
+ import { x402HTTPClient } from '@x402/fetch';
589
+
590
+ // Configure client with SIWX hook - automatically tries SIWX auth before payment
591
+ const httpClient = new x402HTTPClient(client)
592
+ .onPaymentRequired(createSIWxClientHook(signer));
593
+
594
+ // Requests automatically use SIWX auth when server supports it
595
+ const response = await httpClient.fetch(url);
596
+ ```
597
+
598
+ The client hook automatically:
599
+ - Detects SIWX support in 402 responses
600
+ - Matches your wallet's chain with server's `supportedChains`
601
+ - Signs and sends the authentication proof
602
+ - Falls back to payment if SIWX auth fails
603
+
604
+ #### Manual Usage (Advanced)
605
+
606
+ ```typescript
607
+ import {
608
+ createSIWxPayload,
609
+ encodeSIWxHeader,
610
+ } from '@x402/extensions/sign-in-with-x';
611
+
612
+ // 1. Get extension and network from 402 response
613
+ const paymentRequired = await response.json();
614
+ const extension = paymentRequired.extensions['sign-in-with-x'];
615
+ const paymentNetwork = paymentRequired.accepts[0].network; // e.g., "eip155:8453"
616
+
617
+ // 2. Find matching chain in supportedChains
618
+ const matchingChain = extension.supportedChains.find(
619
+ chain => chain.chainId === paymentNetwork
620
+ );
621
+
622
+ if (!matchingChain) {
623
+ // Payment network not supported for SIWX
624
+ throw new Error('Chain not supported');
625
+ }
626
+
627
+ // 3. Build complete info with selected chain
628
+ const completeInfo = {
629
+ ...extension.info,
630
+ chainId: matchingChain.chainId,
631
+ type: matchingChain.type,
632
+ };
633
+
634
+ // 4. Create signed payload
635
+ const payload = await createSIWxPayload(completeInfo, signer);
636
+
637
+ // 5. Encode and send
638
+ const header = encodeSIWxHeader(payload);
639
+ const response = await fetch(url, {
640
+ headers: { 'SIGN-IN-WITH-X': header }
641
+ });
642
+ ```
643
+
644
+ ### SIWx API Reference
645
+
646
+ #### `declareSIWxExtension(options?)`
647
+
648
+ Creates the extension object for servers to include in PaymentRequired. Most fields are derived automatically from request context when using `siwxResourceServerExtension`.
649
+
650
+ ```typescript
651
+ declareSIWxExtension({
652
+ // All fields optional - derived from context if omitted
653
+ domain?: string; // Server domain (derived from request URL)
654
+ resourceUri?: string; // Full resource URI (derived from request URL)
655
+ network?: string | string[]; // CAIP-2 network(s) (derived from accepts[].network)
656
+ statement?: string; // Human-readable purpose
657
+ version?: string; // CAIP-122 version (default: "1")
658
+ expirationSeconds?: number; // Challenge TTL in seconds
659
+ })
660
+ ```
661
+
662
+ **Automatic derivation:** When using `siwxResourceServerExtension`, omitted fields are derived:
663
+ - `network` → from `accepts[].network` in route config
664
+ - `resourceUri` → from request URL
665
+ - `domain` → parsed from resourceUri
666
+
667
+ **Multi-chain support:** When `network` is an array (or multiple networks in `accepts`), `supportedChains` will contain one entry per network.
668
+
669
+ #### `parseSIWxHeader(header)`
670
+
671
+ Parses a base64-encoded SIGN-IN-WITH-X header into a payload object.
672
+
673
+ #### `validateSIWxMessage(payload, resourceUri, options?)`
674
+
675
+ Validates message fields (expiry, domain binding, nonce, etc.).
676
+
677
+ ```typescript
678
+ validateSIWxMessage(payload, resourceUri, {
679
+ maxAge?: number; // Max age for issuedAt (default: 5 min)
680
+ checkNonce?: (nonce) => boolean; // Custom nonce validation
681
+ })
682
+ // Returns: { valid: boolean; error?: string }
683
+ ```
684
+
685
+ #### `verifySIWxSignature(payload, options?)`
686
+
687
+ Verifies the cryptographic signature and recovers the signer address.
688
+
689
+ ```typescript
690
+ verifySIWxSignature(payload, {
691
+ evmVerifier?: EVMMessageVerifier; // For smart wallet support
692
+ })
693
+ // Returns: { valid: boolean; address?: string; error?: string }
694
+ ```
695
+
696
+ **Smart Wallet Support (EIP-1271 / EIP-6492):**
697
+
698
+ By default, only EOA (Externally Owned Account) signatures are verified. To support smart contract wallets (like Coinbase Smart Wallet, Safe, etc.), pass `publicClient.verifyMessage` from viem:
699
+
700
+ ```typescript
701
+ import { createPublicClient, http } from 'viem';
702
+ import { base } from 'viem/chains';
703
+
704
+ const publicClient = createPublicClient({
705
+ chain: base,
706
+ transport: http()
707
+ });
708
+
709
+ // In your request hook
710
+ const result = await verifySIWxSignature(payload, {
711
+ evmVerifier: publicClient.verifyMessage,
712
+ });
713
+ ```
714
+
715
+ This enables:
716
+ - **EIP-1271**: Verification of deployed smart contract wallets
717
+ - **EIP-6492**: Verification of counterfactual (not-yet-deployed) wallets
718
+
719
+ Note: Smart wallet verification requires RPC calls, while EOA verification is purely local.
720
+
721
+ #### `createSIWxPayload(serverInfo, signer)`
722
+
723
+ Client helper that creates and signs a complete payload.
724
+
725
+ #### `encodeSIWxHeader(payload)`
726
+
727
+ Encodes a payload as base64 for the SIGN-IN-WITH-X header.
728
+
729
+ #### `SIGN_IN_WITH_X`
730
+
731
+ Extension identifier constant (`"sign-in-with-x"`).
732
+
733
+ ### Supported Signature Schemes
734
+
735
+ | Scheme | Description |
736
+ |--------|-------------|
737
+ | `eip191` | personal_sign (default for EVM EOAs) |
738
+ | `eip1271` | Smart contract wallet verification |
739
+ | `eip6492` | Counterfactual smart wallet verification |
740
+ | `siws` | Sign-In-With-Solana |
741
+
742
+ ## Troubleshooting
743
+
744
+ ### Bazaar Extension Not Being Extracted
745
+
746
+ **Problem:** `extractDiscoveryInfo` returns `null`.
747
+
748
+ **Solutions:**
749
+ - Ensure the server has declared the extension using `declareDiscoveryExtension`
750
+ - Check that `paymentPayload.extensions.bazaar` exists
751
+ - Verify you're using x402 v2 (v1 uses a different format in `outputSchema`)
752
+
753
+ ### Bazaar Schema Validation Fails
754
+
755
+ **Problem:** `validateDiscoveryExtension` returns `valid: false`.
756
+
757
+ **Solutions:**
758
+ - Ensure `inputSchema` matches the structure of `input`
759
+ - Check that required fields are marked in `inputSchema.required`
760
+ - Verify JSON Schema syntax is correct
761
+
762
+ ### SIWx Signature Verification Fails
763
+
764
+ **Problem:** `verifySIWxSignature` returns `valid: false`.
765
+
766
+ **Solutions:**
767
+ - Ensure the message was signed with the correct wallet
768
+ - Check that the signature scheme matches the wallet type
769
+ - For smart wallets, enable `checkSmartWallet` option with a provider
770
+
771
+ ### SIWx Message Validation Fails
772
+
773
+ **Problem:** `validateSIWxMessage` returns `valid: false`.
774
+
775
+ **Solutions:**
776
+ - Check that `issuedAt` is recent (within `maxAge`, default 5 minutes)
777
+ - Verify `expirationTime` hasn't passed
778
+ - Ensure `domain` matches the server's domain
779
+ - Confirm `uri` matches the resource URI
780
+
781
+ ## Related Resources
782
+
783
+ - [x402 Core Package](../core/README.md) - Core x402 protocol implementation
784
+ - [CAIP-122 Specification](https://chainagnostic.org/CAIPs/caip-122) - Sign-In-With-X standard
785
+
786
+ ## Version Support
787
+
788
+ This package supports both x402 v1 and v2:
789
+ - **v2**: Extensions are in `PaymentPayload.extensions` and `PaymentRequired.extensions`
790
+ - **v1**: Discovery info is in `PaymentRequirements.outputSchema` (automatically converted)
791
+
792
+ The `extractDiscoveryInfo` function automatically handles both versions.