@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.
Files changed (33) hide show
  1. package/README.md +322 -93
  2. package/dist/cjs/bazaar/index.d.ts +3 -562
  3. package/dist/cjs/bazaar/index.js +12 -0
  4. package/dist/cjs/bazaar/index.js.map +1 -1
  5. package/dist/cjs/index-DvDlinmy.d.ts +575 -0
  6. package/dist/cjs/index.d.ts +4 -1
  7. package/dist/cjs/index.js +1008 -2
  8. package/dist/cjs/index.js.map +1 -1
  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 +1054 -1
  13. package/dist/cjs/sign-in-with-x/index.js +766 -0
  14. package/dist/cjs/sign-in-with-x/index.js.map +1 -1
  15. package/dist/esm/bazaar/index.d.mts +3 -562
  16. package/dist/esm/bazaar/index.mjs +1 -1
  17. package/dist/esm/chunk-73HCOE6N.mjs +233 -0
  18. package/dist/esm/chunk-73HCOE6N.mjs.map +1 -0
  19. package/dist/esm/{chunk-WB72GLC2.mjs → chunk-DFJ4ZQFO.mjs} +13 -1
  20. package/dist/esm/chunk-DFJ4ZQFO.mjs.map +1 -0
  21. package/dist/esm/chunk-E3F2XHTI.mjs +719 -0
  22. package/dist/esm/chunk-E3F2XHTI.mjs.map +1 -0
  23. package/dist/esm/index-DvDlinmy.d.mts +575 -0
  24. package/dist/esm/index.d.mts +4 -1
  25. package/dist/esm/index.mjs +102 -3
  26. package/dist/esm/payment-identifier/index.d.mts +345 -0
  27. package/dist/esm/payment-identifier/index.mjs +39 -0
  28. package/dist/esm/sign-in-with-x/index.d.mts +1054 -1
  29. package/dist/esm/sign-in-with-x/index.mjs +66 -1
  30. package/package.json +16 -2
  31. package/dist/esm/chunk-MKFJ5AA3.mjs +0 -1
  32. package/dist/esm/chunk-WB72GLC2.mjs.map +0 -1
  33. /package/dist/esm/{chunk-MKFJ5AA3.mjs.map → payment-identifier/index.mjs.map} +0 -0
@@ -0,0 +1,719 @@
1
+ // src/sign-in-with-x/types.ts
2
+ import { z } from "zod";
3
+ var SIGN_IN_WITH_X = "sign-in-with-x";
4
+ var SIWxPayloadSchema = z.object({
5
+ domain: z.string(),
6
+ address: z.string(),
7
+ statement: z.string().optional(),
8
+ uri: z.string(),
9
+ version: z.string(),
10
+ chainId: z.string(),
11
+ type: z.enum(["eip191", "ed25519"]),
12
+ nonce: z.string(),
13
+ issuedAt: z.string(),
14
+ expirationTime: z.string().optional(),
15
+ notBefore: z.string().optional(),
16
+ requestId: z.string().optional(),
17
+ resources: z.array(z.string()).optional(),
18
+ signatureScheme: z.enum(["eip191", "eip1271", "eip6492", "siws"]).optional(),
19
+ signature: z.string()
20
+ });
21
+
22
+ // src/sign-in-with-x/solana.ts
23
+ import { base58 } from "@scure/base";
24
+ import nacl from "tweetnacl";
25
+ var SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
26
+ var SOLANA_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
27
+ var SOLANA_TESTNET = "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";
28
+ function extractSolanaChainReference(chainId) {
29
+ const [, reference] = chainId.split(":");
30
+ return reference;
31
+ }
32
+ function formatSIWSMessage(info, address) {
33
+ const lines = [
34
+ `${info.domain} wants you to sign in with your Solana account:`,
35
+ address,
36
+ ""
37
+ ];
38
+ if (info.statement) {
39
+ lines.push(info.statement, "");
40
+ }
41
+ lines.push(
42
+ `URI: ${info.uri}`,
43
+ `Version: ${info.version}`,
44
+ `Chain ID: ${extractSolanaChainReference(info.chainId)}`,
45
+ `Nonce: ${info.nonce}`,
46
+ `Issued At: ${info.issuedAt}`
47
+ );
48
+ if (info.expirationTime) {
49
+ lines.push(`Expiration Time: ${info.expirationTime}`);
50
+ }
51
+ if (info.notBefore) {
52
+ lines.push(`Not Before: ${info.notBefore}`);
53
+ }
54
+ if (info.requestId) {
55
+ lines.push(`Request ID: ${info.requestId}`);
56
+ }
57
+ if (info.resources && info.resources.length > 0) {
58
+ lines.push("Resources:");
59
+ for (const resource of info.resources) {
60
+ lines.push(`- ${resource}`);
61
+ }
62
+ }
63
+ return lines.join("\n");
64
+ }
65
+ function verifySolanaSignature(message, signature, publicKey) {
66
+ const messageBytes = new TextEncoder().encode(message);
67
+ return nacl.sign.detached.verify(messageBytes, signature, publicKey);
68
+ }
69
+ function decodeBase58(encoded) {
70
+ return base58.decode(encoded);
71
+ }
72
+ function encodeBase58(bytes) {
73
+ return base58.encode(bytes);
74
+ }
75
+
76
+ // src/sign-in-with-x/schema.ts
77
+ function buildSIWxSchema() {
78
+ return {
79
+ $schema: "https://json-schema.org/draft/2020-12/schema",
80
+ type: "object",
81
+ properties: {
82
+ domain: { type: "string" },
83
+ address: { type: "string" },
84
+ statement: { type: "string" },
85
+ uri: { type: "string", format: "uri" },
86
+ version: { type: "string" },
87
+ chainId: { type: "string" },
88
+ type: { type: "string" },
89
+ nonce: { type: "string" },
90
+ issuedAt: { type: "string", format: "date-time" },
91
+ expirationTime: { type: "string", format: "date-time" },
92
+ notBefore: { type: "string", format: "date-time" },
93
+ requestId: { type: "string" },
94
+ resources: { type: "array", items: { type: "string", format: "uri" } },
95
+ signature: { type: "string" }
96
+ },
97
+ required: [
98
+ "domain",
99
+ "address",
100
+ "uri",
101
+ "version",
102
+ "chainId",
103
+ "type",
104
+ "nonce",
105
+ "issuedAt",
106
+ "signature"
107
+ ]
108
+ };
109
+ }
110
+
111
+ // src/sign-in-with-x/declare.ts
112
+ function getSignatureType(network) {
113
+ return network.startsWith("solana:") ? "ed25519" : "eip191";
114
+ }
115
+ function declareSIWxExtension(options = {}) {
116
+ const info = {
117
+ version: options.version ?? "1"
118
+ };
119
+ if (options.domain) {
120
+ info.domain = options.domain;
121
+ }
122
+ if (options.resourceUri) {
123
+ info.uri = options.resourceUri;
124
+ info.resources = [options.resourceUri];
125
+ }
126
+ if (options.statement) {
127
+ info.statement = options.statement;
128
+ }
129
+ let supportedChains = [];
130
+ if (options.network) {
131
+ const networks = Array.isArray(options.network) ? options.network : [options.network];
132
+ supportedChains = networks.map((network) => ({
133
+ chainId: network,
134
+ type: getSignatureType(network)
135
+ }));
136
+ }
137
+ const declaration = {
138
+ info,
139
+ supportedChains,
140
+ schema: buildSIWxSchema(),
141
+ _options: options
142
+ };
143
+ return { [SIGN_IN_WITH_X]: declaration };
144
+ }
145
+
146
+ // src/sign-in-with-x/server.ts
147
+ import { randomBytes } from "crypto";
148
+ var siwxResourceServerExtension = {
149
+ key: SIGN_IN_WITH_X,
150
+ enrichPaymentRequiredResponse: async (declaration, context) => {
151
+ const decl = declaration;
152
+ const opts = decl._options ?? {};
153
+ const resourceUri = opts.resourceUri ?? context.resourceInfo.url;
154
+ let domain = opts.domain;
155
+ if (!domain && resourceUri) {
156
+ try {
157
+ domain = new URL(resourceUri).hostname;
158
+ } catch {
159
+ }
160
+ }
161
+ let networks;
162
+ if (opts.network) {
163
+ networks = Array.isArray(opts.network) ? opts.network : [opts.network];
164
+ } else {
165
+ networks = [...new Set(context.requirements.map((r) => r.network))];
166
+ }
167
+ const nonce = randomBytes(16).toString("hex");
168
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
169
+ const expirationSeconds = opts.expirationSeconds;
170
+ const expirationTime = expirationSeconds !== void 0 ? new Date(Date.now() + expirationSeconds * 1e3).toISOString() : void 0;
171
+ const info = {
172
+ domain: domain ?? "",
173
+ uri: resourceUri,
174
+ version: opts.version ?? "1",
175
+ nonce,
176
+ issuedAt,
177
+ resources: [resourceUri]
178
+ };
179
+ if (expirationTime) {
180
+ info.expirationTime = expirationTime;
181
+ }
182
+ if (opts.statement) {
183
+ info.statement = opts.statement;
184
+ }
185
+ const supportedChains = networks.map((network) => ({
186
+ chainId: network,
187
+ type: getSignatureType(network)
188
+ }));
189
+ return {
190
+ info,
191
+ supportedChains,
192
+ schema: buildSIWxSchema()
193
+ };
194
+ }
195
+ };
196
+
197
+ // src/sign-in-with-x/parse.ts
198
+ import { Base64EncodedRegex, safeBase64Decode } from "@x402/core/utils";
199
+ function parseSIWxHeader(header) {
200
+ if (!Base64EncodedRegex.test(header)) {
201
+ throw new Error("Invalid SIWX header: not valid base64");
202
+ }
203
+ const jsonStr = safeBase64Decode(header);
204
+ let rawPayload;
205
+ try {
206
+ rawPayload = JSON.parse(jsonStr);
207
+ } catch (error) {
208
+ if (error instanceof SyntaxError) {
209
+ throw new Error("Invalid SIWX header: not valid JSON");
210
+ }
211
+ throw error;
212
+ }
213
+ const parsed = SIWxPayloadSchema.safeParse(rawPayload);
214
+ if (!parsed.success) {
215
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
216
+ throw new Error(`Invalid SIWX header: ${issues}`);
217
+ }
218
+ return parsed.data;
219
+ }
220
+
221
+ // src/sign-in-with-x/validate.ts
222
+ var DEFAULT_MAX_AGE_MS = 5 * 60 * 1e3;
223
+ async function validateSIWxMessage(message, expectedResourceUri, options = {}) {
224
+ const expectedUrl = new URL(expectedResourceUri);
225
+ const maxAge = options.maxAge ?? DEFAULT_MAX_AGE_MS;
226
+ if (message.domain !== expectedUrl.hostname) {
227
+ return {
228
+ valid: false,
229
+ error: `Domain mismatch: expected "${expectedUrl.hostname}", got "${message.domain}"`
230
+ };
231
+ }
232
+ if (!message.uri.startsWith(expectedUrl.origin)) {
233
+ return {
234
+ valid: false,
235
+ error: `URI mismatch: expected origin "${expectedUrl.origin}", got "${message.uri}"`
236
+ };
237
+ }
238
+ const issuedAt = new Date(message.issuedAt);
239
+ if (isNaN(issuedAt.getTime())) {
240
+ return {
241
+ valid: false,
242
+ error: "Invalid issuedAt timestamp"
243
+ };
244
+ }
245
+ const age = Date.now() - issuedAt.getTime();
246
+ if (age > maxAge) {
247
+ return {
248
+ valid: false,
249
+ error: `Message too old: ${Math.round(age / 1e3)}s exceeds ${maxAge / 1e3}s limit`
250
+ };
251
+ }
252
+ if (age < 0) {
253
+ return {
254
+ valid: false,
255
+ error: "issuedAt is in the future"
256
+ };
257
+ }
258
+ if (message.expirationTime) {
259
+ const expiration = new Date(message.expirationTime);
260
+ if (isNaN(expiration.getTime())) {
261
+ return {
262
+ valid: false,
263
+ error: "Invalid expirationTime timestamp"
264
+ };
265
+ }
266
+ if (expiration < /* @__PURE__ */ new Date()) {
267
+ return {
268
+ valid: false,
269
+ error: "Message expired"
270
+ };
271
+ }
272
+ }
273
+ if (message.notBefore) {
274
+ const notBefore = new Date(message.notBefore);
275
+ if (isNaN(notBefore.getTime())) {
276
+ return {
277
+ valid: false,
278
+ error: "Invalid notBefore timestamp"
279
+ };
280
+ }
281
+ if (/* @__PURE__ */ new Date() < notBefore) {
282
+ return {
283
+ valid: false,
284
+ error: "Message not yet valid (notBefore is in the future)"
285
+ };
286
+ }
287
+ }
288
+ if (options.checkNonce) {
289
+ const nonceValid = await options.checkNonce(message.nonce);
290
+ if (!nonceValid) {
291
+ return {
292
+ valid: false,
293
+ error: "Nonce validation failed (possible replay attack)"
294
+ };
295
+ }
296
+ }
297
+ return { valid: true };
298
+ }
299
+
300
+ // src/sign-in-with-x/evm.ts
301
+ import { verifyMessage } from "viem";
302
+ import { SiweMessage } from "siwe";
303
+ function extractEVMChainId(chainId) {
304
+ const match = /^eip155:(\d+)$/.exec(chainId);
305
+ if (!match) {
306
+ throw new Error(`Invalid EVM chainId format: ${chainId}. Expected eip155:<number>`);
307
+ }
308
+ return parseInt(match[1], 10);
309
+ }
310
+ function formatSIWEMessage(info, address) {
311
+ const numericChainId = extractEVMChainId(info.chainId);
312
+ const siweMessage = new SiweMessage({
313
+ domain: info.domain,
314
+ address,
315
+ statement: info.statement,
316
+ uri: info.uri,
317
+ version: info.version,
318
+ chainId: numericChainId,
319
+ nonce: info.nonce,
320
+ issuedAt: info.issuedAt,
321
+ expirationTime: info.expirationTime,
322
+ notBefore: info.notBefore,
323
+ requestId: info.requestId,
324
+ resources: info.resources
325
+ });
326
+ return siweMessage.prepareMessage();
327
+ }
328
+ async function verifyEVMSignature(message, address, signature, verifier) {
329
+ const args = {
330
+ address,
331
+ message,
332
+ signature
333
+ };
334
+ if (verifier) {
335
+ return verifier(args);
336
+ }
337
+ return verifyMessage(args);
338
+ }
339
+
340
+ // src/sign-in-with-x/verify.ts
341
+ async function verifySIWxSignature(payload, options) {
342
+ try {
343
+ if (payload.chainId.startsWith("eip155:")) {
344
+ return verifyEVMPayload(payload, options?.evmVerifier);
345
+ }
346
+ if (payload.chainId.startsWith("solana:")) {
347
+ return verifySolanaPayload(payload);
348
+ }
349
+ return {
350
+ valid: false,
351
+ error: `Unsupported chain namespace: ${payload.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`
352
+ };
353
+ } catch (error) {
354
+ return {
355
+ valid: false,
356
+ error: error instanceof Error ? error.message : "Verification failed"
357
+ };
358
+ }
359
+ }
360
+ async function verifyEVMPayload(payload, verifier) {
361
+ const message = formatSIWEMessage(
362
+ {
363
+ domain: payload.domain,
364
+ uri: payload.uri,
365
+ statement: payload.statement,
366
+ version: payload.version,
367
+ chainId: payload.chainId,
368
+ type: payload.type,
369
+ nonce: payload.nonce,
370
+ issuedAt: payload.issuedAt,
371
+ expirationTime: payload.expirationTime,
372
+ notBefore: payload.notBefore,
373
+ requestId: payload.requestId,
374
+ resources: payload.resources
375
+ },
376
+ payload.address
377
+ );
378
+ try {
379
+ const valid = await verifyEVMSignature(message, payload.address, payload.signature, verifier);
380
+ if (!valid) {
381
+ return {
382
+ valid: false,
383
+ error: "Signature verification failed"
384
+ };
385
+ }
386
+ return {
387
+ valid: true,
388
+ address: payload.address
389
+ };
390
+ } catch (error) {
391
+ return {
392
+ valid: false,
393
+ error: error instanceof Error ? error.message : "Signature verification failed"
394
+ };
395
+ }
396
+ }
397
+ function verifySolanaPayload(payload) {
398
+ const message = formatSIWSMessage(
399
+ {
400
+ domain: payload.domain,
401
+ uri: payload.uri,
402
+ statement: payload.statement,
403
+ version: payload.version,
404
+ chainId: payload.chainId,
405
+ type: payload.type,
406
+ nonce: payload.nonce,
407
+ issuedAt: payload.issuedAt,
408
+ expirationTime: payload.expirationTime,
409
+ notBefore: payload.notBefore,
410
+ requestId: payload.requestId,
411
+ resources: payload.resources
412
+ },
413
+ payload.address
414
+ );
415
+ let signature;
416
+ let publicKey;
417
+ try {
418
+ signature = decodeBase58(payload.signature);
419
+ publicKey = decodeBase58(payload.address);
420
+ } catch (error) {
421
+ return {
422
+ valid: false,
423
+ error: `Invalid Base58 encoding: ${error instanceof Error ? error.message : "decode failed"}`
424
+ };
425
+ }
426
+ if (signature.length !== 64) {
427
+ return {
428
+ valid: false,
429
+ error: `Invalid signature length: expected 64 bytes, got ${signature.length}`
430
+ };
431
+ }
432
+ if (publicKey.length !== 32) {
433
+ return {
434
+ valid: false,
435
+ error: `Invalid public key length: expected 32 bytes, got ${publicKey.length}`
436
+ };
437
+ }
438
+ const valid = verifySolanaSignature(message, signature, publicKey);
439
+ if (!valid) {
440
+ return {
441
+ valid: false,
442
+ error: "Solana signature verification failed"
443
+ };
444
+ }
445
+ return {
446
+ valid: true,
447
+ address: payload.address
448
+ };
449
+ }
450
+
451
+ // src/sign-in-with-x/message.ts
452
+ function createSIWxMessage(serverInfo, address) {
453
+ if (serverInfo.chainId.startsWith("eip155:")) {
454
+ return formatSIWEMessage(serverInfo, address);
455
+ }
456
+ if (serverInfo.chainId.startsWith("solana:")) {
457
+ return formatSIWSMessage(serverInfo, address);
458
+ }
459
+ throw new Error(
460
+ `Unsupported chain namespace: ${serverInfo.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`
461
+ );
462
+ }
463
+
464
+ // src/sign-in-with-x/sign.ts
465
+ function getEVMAddress(signer) {
466
+ if (signer.account?.address) {
467
+ return signer.account.address;
468
+ }
469
+ if (signer.address) {
470
+ return signer.address;
471
+ }
472
+ throw new Error("EVM signer missing address");
473
+ }
474
+ function getSolanaAddress(signer) {
475
+ const pk = signer.publicKey;
476
+ return typeof pk === "string" ? pk : pk.toBase58();
477
+ }
478
+ async function signEVMMessage(message, signer) {
479
+ if (signer.account) {
480
+ return signer.signMessage({ message, account: signer.account });
481
+ }
482
+ return signer.signMessage({ message });
483
+ }
484
+ async function signSolanaMessage(message, signer) {
485
+ const messageBytes = new TextEncoder().encode(message);
486
+ const signatureBytes = await signer.signMessage(messageBytes);
487
+ return encodeBase58(signatureBytes);
488
+ }
489
+
490
+ // src/sign-in-with-x/client.ts
491
+ async function createSIWxPayload(serverExtension, signer) {
492
+ const isSolana = serverExtension.chainId.startsWith("solana:");
493
+ const address = isSolana ? getSolanaAddress(signer) : getEVMAddress(signer);
494
+ const message = createSIWxMessage(serverExtension, address);
495
+ const signature = isSolana ? await signSolanaMessage(message, signer) : await signEVMMessage(message, signer);
496
+ return {
497
+ domain: serverExtension.domain,
498
+ address,
499
+ statement: serverExtension.statement,
500
+ uri: serverExtension.uri,
501
+ version: serverExtension.version,
502
+ chainId: serverExtension.chainId,
503
+ type: serverExtension.type,
504
+ nonce: serverExtension.nonce,
505
+ issuedAt: serverExtension.issuedAt,
506
+ expirationTime: serverExtension.expirationTime,
507
+ notBefore: serverExtension.notBefore,
508
+ requestId: serverExtension.requestId,
509
+ resources: serverExtension.resources,
510
+ signatureScheme: serverExtension.signatureScheme,
511
+ signature
512
+ };
513
+ }
514
+
515
+ // src/sign-in-with-x/encode.ts
516
+ import { safeBase64Encode } from "@x402/core/utils";
517
+ function encodeSIWxHeader(payload) {
518
+ return safeBase64Encode(JSON.stringify(payload));
519
+ }
520
+
521
+ // src/sign-in-with-x/fetch.ts
522
+ import { decodePaymentRequiredHeader } from "@x402/core/http";
523
+ function wrapFetchWithSIWx(fetch, signer) {
524
+ return async (input, init) => {
525
+ const request = new Request(input, init);
526
+ const clonedRequest = request.clone();
527
+ const response = await fetch(request);
528
+ if (response.status !== 402) {
529
+ return response;
530
+ }
531
+ const paymentRequiredHeader = response.headers.get("PAYMENT-REQUIRED");
532
+ if (!paymentRequiredHeader) {
533
+ return response;
534
+ }
535
+ const paymentRequired = decodePaymentRequiredHeader(paymentRequiredHeader);
536
+ const siwxExtension = paymentRequired.extensions?.[SIGN_IN_WITH_X];
537
+ if (!siwxExtension?.supportedChains) {
538
+ return response;
539
+ }
540
+ if (clonedRequest.headers.has(SIGN_IN_WITH_X)) {
541
+ throw new Error("SIWX authentication already attempted");
542
+ }
543
+ const paymentNetwork = paymentRequired.accepts?.[0]?.network;
544
+ if (!paymentNetwork) {
545
+ return response;
546
+ }
547
+ const matchingChain = siwxExtension.supportedChains.find(
548
+ (chain) => chain.chainId === paymentNetwork
549
+ );
550
+ if (!matchingChain) {
551
+ return response;
552
+ }
553
+ const completeInfo = {
554
+ ...siwxExtension.info,
555
+ chainId: matchingChain.chainId,
556
+ type: matchingChain.type
557
+ };
558
+ const payload = await createSIWxPayload(completeInfo, signer);
559
+ const siwxHeader = encodeSIWxHeader(payload);
560
+ clonedRequest.headers.set(SIGN_IN_WITH_X, siwxHeader);
561
+ return fetch(clonedRequest);
562
+ };
563
+ }
564
+
565
+ // src/sign-in-with-x/storage.ts
566
+ var InMemorySIWxStorage = class {
567
+ constructor() {
568
+ this.paidAddresses = /* @__PURE__ */ new Map();
569
+ }
570
+ /**
571
+ * Check if an address has paid for a resource.
572
+ *
573
+ * @param resource - The resource path
574
+ * @param address - The wallet address to check
575
+ * @returns True if the address has paid
576
+ */
577
+ hasPaid(resource, address) {
578
+ return this.paidAddresses.get(resource)?.has(address.toLowerCase()) ?? false;
579
+ }
580
+ /**
581
+ * Record that an address has paid for a resource.
582
+ *
583
+ * @param resource - The resource path
584
+ * @param address - The wallet address that paid
585
+ */
586
+ recordPayment(resource, address) {
587
+ if (!this.paidAddresses.has(resource)) {
588
+ this.paidAddresses.set(resource, /* @__PURE__ */ new Set());
589
+ }
590
+ this.paidAddresses.get(resource).add(address.toLowerCase());
591
+ }
592
+ };
593
+
594
+ // src/sign-in-with-x/hooks.ts
595
+ function createSIWxSettleHook(options) {
596
+ const { storage, onEvent } = options;
597
+ return async (ctx) => {
598
+ if (!ctx.result.success) return;
599
+ const address = ctx.result.payer;
600
+ if (!address) return;
601
+ const resource = new URL(ctx.paymentPayload.resource.url).pathname;
602
+ await storage.recordPayment(resource, address);
603
+ onEvent?.({ type: "payment_recorded", resource, address });
604
+ };
605
+ }
606
+ function createSIWxRequestHook(options) {
607
+ const { storage, verifyOptions, onEvent } = options;
608
+ const hasUsedNonce = typeof storage.hasUsedNonce === "function";
609
+ const hasRecordNonce = typeof storage.recordNonce === "function";
610
+ if (hasUsedNonce !== hasRecordNonce) {
611
+ throw new Error(
612
+ "SIWxStorage nonce tracking requires both hasUsedNonce and recordNonce to be implemented"
613
+ );
614
+ }
615
+ return async (context) => {
616
+ const header = context.adapter.getHeader(SIGN_IN_WITH_X) || context.adapter.getHeader(SIGN_IN_WITH_X.toLowerCase());
617
+ if (!header) return;
618
+ try {
619
+ const payload = parseSIWxHeader(header);
620
+ const resourceUri = context.adapter.getUrl();
621
+ const validation = await validateSIWxMessage(payload, resourceUri);
622
+ if (!validation.valid) {
623
+ onEvent?.({ type: "validation_failed", resource: context.path, error: validation.error });
624
+ return;
625
+ }
626
+ const verification = await verifySIWxSignature(payload, verifyOptions);
627
+ if (!verification.valid || !verification.address) {
628
+ onEvent?.({ type: "validation_failed", resource: context.path, error: verification.error });
629
+ return;
630
+ }
631
+ if (storage.hasUsedNonce) {
632
+ const nonceUsed = await storage.hasUsedNonce(payload.nonce);
633
+ if (nonceUsed) {
634
+ onEvent?.({ type: "nonce_reused", resource: context.path, nonce: payload.nonce });
635
+ return;
636
+ }
637
+ }
638
+ const hasPaid = await storage.hasPaid(context.path, verification.address);
639
+ if (hasPaid) {
640
+ if (storage.recordNonce) {
641
+ await storage.recordNonce(payload.nonce);
642
+ }
643
+ onEvent?.({
644
+ type: "access_granted",
645
+ resource: context.path,
646
+ address: verification.address
647
+ });
648
+ return { grantAccess: true };
649
+ }
650
+ } catch (err) {
651
+ onEvent?.({
652
+ type: "validation_failed",
653
+ resource: context.path,
654
+ error: err instanceof Error ? err.message : "Unknown error"
655
+ });
656
+ }
657
+ };
658
+ }
659
+ function createSIWxClientHook(signer) {
660
+ return async (context) => {
661
+ const extensions = context.paymentRequired.extensions ?? {};
662
+ const siwxExtension = extensions[SIGN_IN_WITH_X];
663
+ if (!siwxExtension?.supportedChains) return;
664
+ try {
665
+ const paymentNetwork = context.paymentRequired.accepts?.[0]?.network;
666
+ if (!paymentNetwork) return;
667
+ const matchingChain = siwxExtension.supportedChains.find(
668
+ (chain) => chain.chainId === paymentNetwork
669
+ );
670
+ if (!matchingChain) {
671
+ return;
672
+ }
673
+ const completeInfo = {
674
+ ...siwxExtension.info,
675
+ chainId: matchingChain.chainId,
676
+ type: matchingChain.type
677
+ };
678
+ const payload = await createSIWxPayload(completeInfo, signer);
679
+ const header = encodeSIWxHeader(payload);
680
+ return { headers: { [SIGN_IN_WITH_X]: header } };
681
+ } catch {
682
+ }
683
+ };
684
+ }
685
+
686
+ export {
687
+ SIGN_IN_WITH_X,
688
+ SIWxPayloadSchema,
689
+ SOLANA_MAINNET,
690
+ SOLANA_DEVNET,
691
+ SOLANA_TESTNET,
692
+ extractSolanaChainReference,
693
+ formatSIWSMessage,
694
+ verifySolanaSignature,
695
+ decodeBase58,
696
+ encodeBase58,
697
+ buildSIWxSchema,
698
+ declareSIWxExtension,
699
+ siwxResourceServerExtension,
700
+ parseSIWxHeader,
701
+ validateSIWxMessage,
702
+ extractEVMChainId,
703
+ formatSIWEMessage,
704
+ verifyEVMSignature,
705
+ verifySIWxSignature,
706
+ createSIWxMessage,
707
+ getEVMAddress,
708
+ getSolanaAddress,
709
+ signEVMMessage,
710
+ signSolanaMessage,
711
+ createSIWxPayload,
712
+ encodeSIWxHeader,
713
+ wrapFetchWithSIWx,
714
+ InMemorySIWxStorage,
715
+ createSIWxSettleHook,
716
+ createSIWxRequestHook,
717
+ createSIWxClientHook
718
+ };
719
+ //# sourceMappingURL=chunk-E3F2XHTI.mjs.map