@x402/extensions 2.2.0 → 2.3.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 +322 -93
- package/dist/cjs/bazaar/index.d.ts +3 -562
- package/dist/cjs/bazaar/index.js +12 -0
- package/dist/cjs/bazaar/index.js.map +1 -1
- package/dist/cjs/index-DvDlinmy.d.ts +575 -0
- package/dist/cjs/index.d.ts +4 -1
- package/dist/cjs/index.js +1008 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/payment-identifier/index.d.ts +345 -0
- package/dist/cjs/payment-identifier/index.js +285 -0
- package/dist/cjs/payment-identifier/index.js.map +1 -0
- package/dist/cjs/sign-in-with-x/index.d.ts +1054 -1
- package/dist/cjs/sign-in-with-x/index.js +766 -0
- package/dist/cjs/sign-in-with-x/index.js.map +1 -1
- package/dist/esm/bazaar/index.d.mts +3 -562
- package/dist/esm/bazaar/index.mjs +1 -1
- package/dist/esm/chunk-73HCOE6N.mjs +233 -0
- package/dist/esm/chunk-73HCOE6N.mjs.map +1 -0
- package/dist/esm/{chunk-WB72GLC2.mjs → chunk-DFJ4ZQFO.mjs} +13 -1
- package/dist/esm/chunk-DFJ4ZQFO.mjs.map +1 -0
- package/dist/esm/chunk-E3F2XHTI.mjs +719 -0
- package/dist/esm/chunk-E3F2XHTI.mjs.map +1 -0
- package/dist/esm/index-DvDlinmy.d.mts +575 -0
- package/dist/esm/index.d.mts +4 -1
- package/dist/esm/index.mjs +102 -3
- package/dist/esm/payment-identifier/index.d.mts +345 -0
- package/dist/esm/payment-identifier/index.mjs +39 -0
- package/dist/esm/sign-in-with-x/index.d.mts +1054 -1
- package/dist/esm/sign-in-with-x/index.mjs +66 -1
- package/package.json +16 -2
- package/dist/esm/chunk-MKFJ5AA3.mjs +0 -1
- package/dist/esm/chunk-WB72GLC2.mjs.map +0 -1
- /package/dist/esm/{chunk-MKFJ5AA3.mjs.map → payment-identifier/index.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @x402/extensions
|
|
2
2
|
|
|
3
|
-
x402 Payment Protocol Extensions. This package provides optional extensions that enhance the x402 payment protocol with additional functionality
|
|
3
|
+
x402 Payment Protocol Extensions. This package provides optional extensions that enhance the x402 payment protocol with additional functionality.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,10 +10,11 @@ pnpm install @x402/extensions
|
|
|
10
10
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
|
-
Extensions are optional features that can be added to x402 payment flows. They
|
|
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
14
|
|
|
15
|
-
|
|
16
|
-
- **Bazaar Discovery
|
|
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
|
|
17
18
|
|
|
18
19
|
## Bazaar Discovery Extension
|
|
19
20
|
|
|
@@ -39,29 +40,29 @@ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
|
|
|
39
40
|
|
|
40
41
|
const resources = {
|
|
41
42
|
"GET /weather": {
|
|
42
|
-
accepts: {
|
|
43
|
-
scheme: "exact",
|
|
44
|
-
price: "$0.001",
|
|
45
|
-
network: "eip155:84532",
|
|
46
|
-
payTo: "0xYourAddress"
|
|
43
|
+
accepts: {
|
|
44
|
+
scheme: "exact",
|
|
45
|
+
price: "$0.001",
|
|
46
|
+
network: "eip155:84532",
|
|
47
|
+
payTo: "0xYourAddress"
|
|
47
48
|
},
|
|
48
49
|
extensions: {
|
|
49
50
|
...declareDiscoveryExtension({
|
|
50
51
|
input: { city: "San Francisco" },
|
|
51
52
|
inputSchema: {
|
|
52
|
-
properties: {
|
|
53
|
+
properties: {
|
|
53
54
|
city: { type: "string" },
|
|
54
55
|
units: { type: "string", enum: ["celsius", "fahrenheit"] }
|
|
55
56
|
},
|
|
56
57
|
required: ["city"]
|
|
57
58
|
},
|
|
58
|
-
output: {
|
|
59
|
-
example: {
|
|
60
|
-
city: "San Francisco",
|
|
59
|
+
output: {
|
|
60
|
+
example: {
|
|
61
|
+
city: "San Francisco",
|
|
61
62
|
weather: "foggy",
|
|
62
63
|
temperature: 15,
|
|
63
64
|
humidity: 85
|
|
64
|
-
}
|
|
65
|
+
}
|
|
65
66
|
},
|
|
66
67
|
}),
|
|
67
68
|
},
|
|
@@ -78,15 +79,15 @@ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
|
|
|
78
79
|
|
|
79
80
|
const resources = {
|
|
80
81
|
"POST /api/translate": {
|
|
81
|
-
accepts: {
|
|
82
|
-
scheme: "exact",
|
|
83
|
-
price: "$0.01",
|
|
84
|
-
network: "eip155:84532",
|
|
85
|
-
payTo: "0xYourAddress"
|
|
82
|
+
accepts: {
|
|
83
|
+
scheme: "exact",
|
|
84
|
+
price: "$0.01",
|
|
85
|
+
network: "eip155:84532",
|
|
86
|
+
payTo: "0xYourAddress"
|
|
86
87
|
},
|
|
87
88
|
extensions: {
|
|
88
89
|
...declareDiscoveryExtension({
|
|
89
|
-
input: {
|
|
90
|
+
input: {
|
|
90
91
|
text: "Hello, world!",
|
|
91
92
|
targetLanguage: "es"
|
|
92
93
|
},
|
|
@@ -116,15 +117,15 @@ const resources = {
|
|
|
116
117
|
```typescript
|
|
117
118
|
const resources = {
|
|
118
119
|
"PUT /api/user/profile": {
|
|
119
|
-
accepts: {
|
|
120
|
-
scheme: "exact",
|
|
121
|
-
price: "$0.05",
|
|
122
|
-
network: "eip155:84532",
|
|
123
|
-
payTo: "0xYourAddress"
|
|
120
|
+
accepts: {
|
|
121
|
+
scheme: "exact",
|
|
122
|
+
price: "$0.05",
|
|
123
|
+
network: "eip155:84532",
|
|
124
|
+
payTo: "0xYourAddress"
|
|
124
125
|
},
|
|
125
126
|
extensions: {
|
|
126
127
|
...declareDiscoveryExtension({
|
|
127
|
-
input: {
|
|
128
|
+
input: {
|
|
128
129
|
name: "John Doe",
|
|
129
130
|
email: "john@example.com",
|
|
130
131
|
bio: "Software developer"
|
|
@@ -156,11 +157,11 @@ const resources = {
|
|
|
156
157
|
```typescript
|
|
157
158
|
const resources = {
|
|
158
159
|
"DELETE /api/data/:id": {
|
|
159
|
-
accepts: {
|
|
160
|
-
scheme: "exact",
|
|
161
|
-
price: "$0.001",
|
|
162
|
-
network: "eip155:84532",
|
|
163
|
-
payTo: "0xYourAddress"
|
|
160
|
+
accepts: {
|
|
161
|
+
scheme: "exact",
|
|
162
|
+
price: "$0.001",
|
|
163
|
+
network: "eip155:84532",
|
|
164
|
+
payTo: "0xYourAddress"
|
|
164
165
|
},
|
|
165
166
|
extensions: {
|
|
166
167
|
...declareDiscoveryExtension({
|
|
@@ -269,17 +270,17 @@ import { validateDiscoveryExtension, extractDiscoveryInfo } from "@x402/extensio
|
|
|
269
270
|
|
|
270
271
|
function processPayment(paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements) {
|
|
271
272
|
const discovered = extractDiscoveryInfo(paymentPayload, paymentRequirements);
|
|
272
|
-
|
|
273
|
+
|
|
273
274
|
if (discovered && paymentPayload.extensions?.bazaar) {
|
|
274
275
|
// Validate the extension schema
|
|
275
276
|
const validation = validateDiscoveryExtension(paymentPayload.extensions.bazaar);
|
|
276
|
-
|
|
277
|
+
|
|
277
278
|
if (!validation.valid) {
|
|
278
279
|
console.warn("Invalid discovery extension:", validation.errors);
|
|
279
280
|
// Handle invalid extension (log, reject, etc.)
|
|
280
281
|
return;
|
|
281
282
|
}
|
|
282
|
-
|
|
283
|
+
|
|
283
284
|
// Extension is valid, proceed with cataloging
|
|
284
285
|
catalogResource(discovered);
|
|
285
286
|
}
|
|
@@ -300,9 +301,9 @@ const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
|
300
301
|
.registerExtension(bazaarResourceServerExtension);
|
|
301
302
|
```
|
|
302
303
|
|
|
303
|
-
|
|
304
|
+
### Bazaar API Reference
|
|
304
305
|
|
|
305
|
-
|
|
306
|
+
#### `declareDiscoveryExtension(config)`
|
|
306
307
|
|
|
307
308
|
Creates a discovery extension object for resource servers.
|
|
308
309
|
|
|
@@ -333,7 +334,7 @@ const extension = declareDiscoveryExtension({
|
|
|
333
334
|
// Returns: { bazaar: { info: {...}, schema: {...} } }
|
|
334
335
|
```
|
|
335
336
|
|
|
336
|
-
|
|
337
|
+
#### `extractDiscoveryInfo(paymentPayload, paymentRequirements, validate?)`
|
|
337
338
|
|
|
338
339
|
Extracts discovery information from a payment request (for facilitators).
|
|
339
340
|
|
|
@@ -353,88 +354,314 @@ interface DiscoveredResource {
|
|
|
353
354
|
}
|
|
354
355
|
```
|
|
355
356
|
|
|
356
|
-
|
|
357
|
+
#### `validateDiscoveryExtension(extension)`
|
|
358
|
+
|
|
359
|
+
Validates a discovery extension's info against its schema.
|
|
360
|
+
|
|
361
|
+
**Returns:** `{ valid: boolean, errors?: string[] }`
|
|
362
|
+
|
|
363
|
+
#### `validateAndExtract(extension)`
|
|
364
|
+
|
|
365
|
+
Validates and extracts discovery info in one step.
|
|
366
|
+
|
|
367
|
+
**Returns:** `{ valid: boolean, info?: DiscoveryInfo, errors?: string[] }`
|
|
368
|
+
|
|
369
|
+
#### `bazaarResourceServerExtension`
|
|
370
|
+
|
|
371
|
+
A server extension that automatically enriches discovery extensions with HTTP method information from the request context.
|
|
372
|
+
|
|
373
|
+
**Usage:**
|
|
357
374
|
```typescript
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
console.log(info.discoveryInfo); // { input: {...}, output: {...} }
|
|
363
|
-
}
|
|
375
|
+
import { bazaarResourceServerExtension } from "@x402/extensions/bazaar";
|
|
376
|
+
|
|
377
|
+
const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
378
|
+
.registerExtension(bazaarResourceServerExtension);
|
|
364
379
|
```
|
|
365
380
|
|
|
366
|
-
### `
|
|
381
|
+
### `BAZAAR`
|
|
367
382
|
|
|
368
|
-
|
|
383
|
+
The extension identifier constant (`"bazaar"`).
|
|
369
384
|
|
|
370
|
-
|
|
371
|
-
- `extension`: A discovery extension object
|
|
385
|
+
## Sign-In-With-X Extension
|
|
372
386
|
|
|
373
|
-
|
|
387
|
+
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.
|
|
388
|
+
|
|
389
|
+
### How It Works
|
|
390
|
+
|
|
391
|
+
1. Server returns 402 with `sign-in-with-x` extension containing challenge parameters
|
|
392
|
+
2. Client signs the CAIP-122 message with their wallet
|
|
393
|
+
3. Client sends signed proof in `SIGN-IN-WITH-X` header
|
|
394
|
+
4. Server verifies signature and grants access if wallet has previous payment
|
|
395
|
+
|
|
396
|
+
### Server Usage
|
|
397
|
+
|
|
398
|
+
#### Recommended: Hooks (Automatic)
|
|
374
399
|
|
|
375
|
-
**Example:**
|
|
376
400
|
```typescript
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
401
|
+
import {
|
|
402
|
+
declareSIWxExtension,
|
|
403
|
+
siwxResourceServerExtension,
|
|
404
|
+
createSIWxSettleHook,
|
|
405
|
+
createSIWxRequestHook,
|
|
406
|
+
InMemorySIWxStorage,
|
|
407
|
+
} from '@x402/extensions/sign-in-with-x';
|
|
408
|
+
|
|
409
|
+
// Storage for tracking paid addresses
|
|
410
|
+
const storage = new InMemorySIWxStorage();
|
|
411
|
+
|
|
412
|
+
// 1. Register extension for time-based field refreshment
|
|
413
|
+
const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
414
|
+
.register(NETWORK, new ExactEvmScheme())
|
|
415
|
+
.registerExtension(siwxResourceServerExtension) // Refreshes nonce/timestamps per request
|
|
416
|
+
.onAfterSettle(createSIWxSettleHook({ storage })); // Records payments
|
|
417
|
+
|
|
418
|
+
// 2. Declare SIWX support in routes (network/domain/uri derived automatically)
|
|
419
|
+
const routes = {
|
|
420
|
+
"GET /data": {
|
|
421
|
+
accepts: [{scheme: "exact", price: "$0.01", network: "eip155:8453", payTo}],
|
|
422
|
+
extensions: declareSIWxExtension({
|
|
423
|
+
statement: 'Sign in to access your purchased content',
|
|
424
|
+
}),
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// 3. Verify incoming SIWX proofs
|
|
429
|
+
const httpServer = new x402HTTPResourceServer(resourceServer, routes)
|
|
430
|
+
.onProtectedRequest(createSIWxRequestHook({ storage })); // Grants access if paid
|
|
431
|
+
|
|
432
|
+
// Optional: Enable smart wallet support (EIP-1271/EIP-6492)
|
|
433
|
+
import { createPublicClient, http } from 'viem';
|
|
434
|
+
import { base } from 'viem/chains';
|
|
435
|
+
|
|
436
|
+
const publicClient = createPublicClient({ chain: base, transport: http() });
|
|
437
|
+
const httpServerWithSmartWallets = new x402HTTPResourceServer(resourceServer, routes)
|
|
438
|
+
.onProtectedRequest(createSIWxRequestHook({
|
|
439
|
+
storage,
|
|
440
|
+
verifyOptions: { evmVerifier: publicClient.verifyMessage },
|
|
441
|
+
}));
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
The hooks automatically:
|
|
445
|
+
- **siwxResourceServerExtension**: Derives `network` from `accepts`, `domain`/`uri` from request URL, refreshes `nonce`/`issuedAt`/`expirationTime` per request
|
|
446
|
+
- **createSIWxSettleHook**: Records payment when settlement succeeds
|
|
447
|
+
- **createSIWxRequestHook**: Validates and verifies SIWX proofs, grants access if wallet has paid
|
|
448
|
+
|
|
449
|
+
#### Manual Usage (Advanced)
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
import {
|
|
453
|
+
declareSIWxExtension,
|
|
454
|
+
parseSIWxHeader,
|
|
455
|
+
validateSIWxMessage,
|
|
456
|
+
verifySIWxSignature,
|
|
457
|
+
SIGN_IN_WITH_X,
|
|
458
|
+
} from '@x402/extensions/sign-in-with-x';
|
|
459
|
+
|
|
460
|
+
// 1. Declare in PaymentRequired response
|
|
461
|
+
const extensions = {
|
|
462
|
+
[SIGN_IN_WITH_X]: declareSIWxExtension({
|
|
463
|
+
domain: 'api.example.com',
|
|
464
|
+
resourceUri: 'https://api.example.com/data',
|
|
465
|
+
network: 'eip155:8453',
|
|
466
|
+
statement: 'Sign in to access your purchased content',
|
|
467
|
+
}),
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// 2. Verify incoming proof
|
|
471
|
+
async function handleRequest(request: Request) {
|
|
472
|
+
const header = request.headers.get('SIGN-IN-WITH-X');
|
|
473
|
+
if (!header) return; // No auth provided
|
|
474
|
+
|
|
475
|
+
// Parse the header
|
|
476
|
+
const payload = parseSIWxHeader(header);
|
|
477
|
+
|
|
478
|
+
// Validate message fields (expiry, nonce, domain, etc.)
|
|
479
|
+
const validation = await validateSIWxMessage(
|
|
480
|
+
payload,
|
|
481
|
+
'https://api.example.com/data'
|
|
482
|
+
);
|
|
483
|
+
if (!validation.valid) {
|
|
484
|
+
return { error: validation.error };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Verify signature and recover address
|
|
488
|
+
const verification = await verifySIWxSignature(payload);
|
|
489
|
+
if (!verification.valid) {
|
|
490
|
+
return { error: verification.error };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// verification.address is the verified wallet
|
|
494
|
+
// Check if this wallet has paid before
|
|
495
|
+
const hasPaid = await checkPaymentHistory(verification.address);
|
|
496
|
+
if (hasPaid) {
|
|
497
|
+
// Grant access without payment
|
|
498
|
+
}
|
|
380
499
|
}
|
|
381
500
|
```
|
|
382
501
|
|
|
383
|
-
###
|
|
502
|
+
### Client Usage
|
|
384
503
|
|
|
385
|
-
|
|
504
|
+
#### Recommended: Client Hook (Automatic)
|
|
386
505
|
|
|
387
|
-
|
|
388
|
-
|
|
506
|
+
```typescript
|
|
507
|
+
import { createSIWxClientHook } from '@x402/extensions/sign-in-with-x';
|
|
508
|
+
import { x402HTTPClient } from '@x402/fetch';
|
|
389
509
|
|
|
390
|
-
|
|
510
|
+
// Configure client with SIWX hook - automatically tries SIWX auth before payment
|
|
511
|
+
const httpClient = new x402HTTPClient(client)
|
|
512
|
+
.onPaymentRequired(createSIWxClientHook(signer));
|
|
513
|
+
|
|
514
|
+
// Requests automatically use SIWX auth when server supports it
|
|
515
|
+
const response = await httpClient.fetch(url);
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The client hook automatically:
|
|
519
|
+
- Detects SIWX support in 402 responses
|
|
520
|
+
- Matches your wallet's chain with server's `supportedChains`
|
|
521
|
+
- Signs and sends the authentication proof
|
|
522
|
+
- Falls back to payment if SIWX auth fails
|
|
523
|
+
|
|
524
|
+
#### Manual Usage (Advanced)
|
|
391
525
|
|
|
392
|
-
**Example:**
|
|
393
526
|
```typescript
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
527
|
+
import {
|
|
528
|
+
createSIWxPayload,
|
|
529
|
+
encodeSIWxHeader,
|
|
530
|
+
} from '@x402/extensions/sign-in-with-x';
|
|
531
|
+
|
|
532
|
+
// 1. Get extension and network from 402 response
|
|
533
|
+
const paymentRequired = await response.json();
|
|
534
|
+
const extension = paymentRequired.extensions['sign-in-with-x'];
|
|
535
|
+
const paymentNetwork = paymentRequired.accepts[0].network; // e.g., "eip155:8453"
|
|
536
|
+
|
|
537
|
+
// 2. Find matching chain in supportedChains
|
|
538
|
+
const matchingChain = extension.supportedChains.find(
|
|
539
|
+
chain => chain.chainId === paymentNetwork
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
if (!matchingChain) {
|
|
543
|
+
// Payment network not supported for SIWX
|
|
544
|
+
throw new Error('Chain not supported');
|
|
397
545
|
}
|
|
546
|
+
|
|
547
|
+
// 3. Build complete info with selected chain
|
|
548
|
+
const completeInfo = {
|
|
549
|
+
...extension.info,
|
|
550
|
+
chainId: matchingChain.chainId,
|
|
551
|
+
type: matchingChain.type,
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
// 4. Create signed payload
|
|
555
|
+
const payload = await createSIWxPayload(completeInfo, signer);
|
|
556
|
+
|
|
557
|
+
// 5. Encode and send
|
|
558
|
+
const header = encodeSIWxHeader(payload);
|
|
559
|
+
const response = await fetch(url, {
|
|
560
|
+
headers: { 'SIGN-IN-WITH-X': header }
|
|
561
|
+
});
|
|
398
562
|
```
|
|
399
563
|
|
|
400
|
-
###
|
|
564
|
+
### SIWx API Reference
|
|
401
565
|
|
|
402
|
-
|
|
566
|
+
#### `declareSIWxExtension(options?)`
|
|
567
|
+
|
|
568
|
+
Creates the extension object for servers to include in PaymentRequired. Most fields are derived automatically from request context when using `siwxResourceServerExtension`.
|
|
403
569
|
|
|
404
|
-
**Usage:**
|
|
405
570
|
```typescript
|
|
406
|
-
|
|
571
|
+
declareSIWxExtension({
|
|
572
|
+
// All fields optional - derived from context if omitted
|
|
573
|
+
domain?: string; // Server domain (derived from request URL)
|
|
574
|
+
resourceUri?: string; // Full resource URI (derived from request URL)
|
|
575
|
+
network?: string | string[]; // CAIP-2 network(s) (derived from accepts[].network)
|
|
576
|
+
statement?: string; // Human-readable purpose
|
|
577
|
+
version?: string; // CAIP-122 version (default: "1")
|
|
578
|
+
expirationSeconds?: number; // Challenge TTL in seconds
|
|
579
|
+
})
|
|
580
|
+
```
|
|
407
581
|
|
|
408
|
-
|
|
409
|
-
|
|
582
|
+
**Automatic derivation:** When using `siwxResourceServerExtension`, omitted fields are derived:
|
|
583
|
+
- `network` → from `accepts[].network` in route config
|
|
584
|
+
- `resourceUri` → from request URL
|
|
585
|
+
- `domain` → parsed from resourceUri
|
|
586
|
+
|
|
587
|
+
**Multi-chain support:** When `network` is an array (or multiple networks in `accepts`), `supportedChains` will contain one entry per network.
|
|
588
|
+
|
|
589
|
+
#### `parseSIWxHeader(header)`
|
|
590
|
+
|
|
591
|
+
Parses a base64-encoded SIGN-IN-WITH-X header into a payload object.
|
|
592
|
+
|
|
593
|
+
#### `validateSIWxMessage(payload, resourceUri, options?)`
|
|
594
|
+
|
|
595
|
+
Validates message fields (expiry, domain binding, nonce, etc.).
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
validateSIWxMessage(payload, resourceUri, {
|
|
599
|
+
maxAge?: number; // Max age for issuedAt (default: 5 min)
|
|
600
|
+
checkNonce?: (nonce) => boolean; // Custom nonce validation
|
|
601
|
+
})
|
|
602
|
+
// Returns: { valid: boolean; error?: string }
|
|
410
603
|
```
|
|
411
604
|
|
|
412
|
-
|
|
605
|
+
#### `verifySIWxSignature(payload, options?)`
|
|
413
606
|
|
|
414
|
-
|
|
607
|
+
Verifies the cryptographic signature and recovers the signer address.
|
|
415
608
|
|
|
416
609
|
```typescript
|
|
417
|
-
|
|
418
|
-
//
|
|
610
|
+
verifySIWxSignature(payload, {
|
|
611
|
+
evmVerifier?: EVMMessageVerifier; // For smart wallet support
|
|
612
|
+
})
|
|
613
|
+
// Returns: { valid: boolean; address?: string; error?: string }
|
|
419
614
|
```
|
|
420
615
|
|
|
421
|
-
|
|
616
|
+
**Smart Wallet Support (EIP-1271 / EIP-6492):**
|
|
617
|
+
|
|
618
|
+
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:
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
import { createPublicClient, http } from 'viem';
|
|
622
|
+
import { base } from 'viem/chains';
|
|
623
|
+
|
|
624
|
+
const publicClient = createPublicClient({
|
|
625
|
+
chain: base,
|
|
626
|
+
transport: http()
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
// In your request hook
|
|
630
|
+
const result = await verifySIWxSignature(payload, {
|
|
631
|
+
evmVerifier: publicClient.verifyMessage,
|
|
632
|
+
});
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
This enables:
|
|
636
|
+
- **EIP-1271**: Verification of deployed smart contract wallets
|
|
637
|
+
- **EIP-6492**: Verification of counterfactual (not-yet-deployed) wallets
|
|
638
|
+
|
|
639
|
+
Note: Smart wallet verification requires RPC calls, while EOA verification is purely local.
|
|
640
|
+
|
|
641
|
+
#### `createSIWxPayload(serverInfo, signer)`
|
|
642
|
+
|
|
643
|
+
Client helper that creates and signs a complete payload.
|
|
644
|
+
|
|
645
|
+
#### `encodeSIWxHeader(payload)`
|
|
646
|
+
|
|
647
|
+
Encodes a payload as base64 for the SIGN-IN-WITH-X header.
|
|
422
648
|
|
|
423
|
-
|
|
424
|
-
Enable users to discover paid APIs through facilitator catalogs. Servers declare their endpoints, and facilitators index them for easy discovery.
|
|
649
|
+
#### `SIGN_IN_WITH_X`
|
|
425
650
|
|
|
426
|
-
|
|
427
|
-
Build tools that automatically generate API documentation or client SDKs from discovery metadata.
|
|
651
|
+
Extension identifier constant (`"sign-in-with-x"`).
|
|
428
652
|
|
|
429
|
-
###
|
|
430
|
-
Facilitators can maintain catalogs of available paid resources, making it easier for users to find services.
|
|
653
|
+
### Supported Signature Schemes
|
|
431
654
|
|
|
432
|
-
|
|
433
|
-
|
|
655
|
+
| Scheme | Description |
|
|
656
|
+
|--------|-------------|
|
|
657
|
+
| `eip191` | personal_sign (default for EVM EOAs) |
|
|
658
|
+
| `eip1271` | Smart contract wallet verification |
|
|
659
|
+
| `eip6492` | Counterfactual smart wallet verification |
|
|
660
|
+
| `siws` | Sign-In-With-Solana |
|
|
434
661
|
|
|
435
662
|
## Troubleshooting
|
|
436
663
|
|
|
437
|
-
### Extension Not Being Extracted
|
|
664
|
+
### Bazaar Extension Not Being Extracted
|
|
438
665
|
|
|
439
666
|
**Problem:** `extractDiscoveryInfo` returns `null`.
|
|
440
667
|
|
|
@@ -443,7 +670,7 @@ Use discovery schemas to validate API requests and responses during development.
|
|
|
443
670
|
- Check that `paymentPayload.extensions.bazaar` exists
|
|
444
671
|
- Verify you're using x402 v2 (v1 uses a different format in `outputSchema`)
|
|
445
672
|
|
|
446
|
-
### Schema Validation Fails
|
|
673
|
+
### Bazaar Schema Validation Fails
|
|
447
674
|
|
|
448
675
|
**Problem:** `validateDiscoveryExtension` returns `valid: false`.
|
|
449
676
|
|
|
@@ -452,27 +679,29 @@ Use discovery schemas to validate API requests and responses during development.
|
|
|
452
679
|
- Check that required fields are marked in `inputSchema.required`
|
|
453
680
|
- Verify JSON Schema syntax is correct
|
|
454
681
|
|
|
455
|
-
###
|
|
682
|
+
### SIWx Signature Verification Fails
|
|
456
683
|
|
|
457
|
-
**Problem:**
|
|
684
|
+
**Problem:** `verifySIWxSignature` returns `valid: false`.
|
|
458
685
|
|
|
459
686
|
**Solutions:**
|
|
460
|
-
- Ensure
|
|
461
|
-
-
|
|
462
|
-
-
|
|
687
|
+
- Ensure the message was signed with the correct wallet
|
|
688
|
+
- Check that the signature scheme matches the wallet type
|
|
689
|
+
- For smart wallets, enable `checkSmartWallet` option with a provider
|
|
463
690
|
|
|
464
|
-
###
|
|
691
|
+
### SIWx Message Validation Fails
|
|
465
692
|
|
|
466
|
-
**Problem:**
|
|
693
|
+
**Problem:** `validateSIWxMessage` returns `valid: false`.
|
|
467
694
|
|
|
468
695
|
**Solutions:**
|
|
469
|
-
-
|
|
470
|
-
-
|
|
696
|
+
- Check that `issuedAt` is recent (within `maxAge`, default 5 minutes)
|
|
697
|
+
- Verify `expirationTime` hasn't passed
|
|
698
|
+
- Ensure `domain` matches the server's domain
|
|
699
|
+
- Confirm `uri` matches the resource URI
|
|
471
700
|
|
|
472
701
|
## Related Resources
|
|
473
702
|
|
|
474
703
|
- [x402 Core Package](../core/README.md) - Core x402 protocol implementation
|
|
475
|
-
- [
|
|
704
|
+
- [CAIP-122 Specification](https://chainagnostic.org/CAIPs/caip-122) - Sign-In-With-X standard
|
|
476
705
|
|
|
477
706
|
## Version Support
|
|
478
707
|
|