@bankofai/x402-extensions 1.0.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 (52) hide show
  1. package/README.md +28 -0
  2. package/dist/cjs/bazaar/index.d.ts +4 -0
  3. package/dist/cjs/bazaar/index.js +1048 -0
  4. package/dist/cjs/bazaar/index.js.map +1 -0
  5. package/dist/cjs/builder-code/index.d.ts +225 -0
  6. package/dist/cjs/builder-code/index.js +369 -0
  7. package/dist/cjs/builder-code/index.js.map +1 -0
  8. package/dist/cjs/index-B8S563vv.d.ts +929 -0
  9. package/dist/cjs/index-BUV9B9f8.d.ts +929 -0
  10. package/dist/cjs/index.d.ts +373 -0
  11. package/dist/cjs/index.js +3536 -0
  12. package/dist/cjs/index.js.map +1 -0
  13. package/dist/cjs/offer-receipt/index.d.ts +702 -0
  14. package/dist/cjs/offer-receipt/index.js +909 -0
  15. package/dist/cjs/offer-receipt/index.js.map +1 -0
  16. package/dist/cjs/payment-identifier/index.d.ts +345 -0
  17. package/dist/cjs/payment-identifier/index.js +285 -0
  18. package/dist/cjs/payment-identifier/index.js.map +1 -0
  19. package/dist/cjs/sign-in-with-x/index.d.ts +1117 -0
  20. package/dist/cjs/sign-in-with-x/index.js +876 -0
  21. package/dist/cjs/sign-in-with-x/index.js.map +1 -0
  22. package/dist/esm/bazaar/index.d.mts +4 -0
  23. package/dist/esm/bazaar/index.mjs +51 -0
  24. package/dist/esm/bazaar/index.mjs.map +1 -0
  25. package/dist/esm/builder-code/index.d.mts +225 -0
  26. package/dist/esm/builder-code/index.mjs +27 -0
  27. package/dist/esm/builder-code/index.mjs.map +1 -0
  28. package/dist/esm/chunk-4IPDE3NS.mjs +990 -0
  29. package/dist/esm/chunk-4IPDE3NS.mjs.map +1 -0
  30. package/dist/esm/chunk-N74HQTNO.mjs +807 -0
  31. package/dist/esm/chunk-N74HQTNO.mjs.map +1 -0
  32. package/dist/esm/chunk-OWZP4CUR.mjs +333 -0
  33. package/dist/esm/chunk-OWZP4CUR.mjs.map +1 -0
  34. package/dist/esm/chunk-RERA4OZZ.mjs +233 -0
  35. package/dist/esm/chunk-RERA4OZZ.mjs.map +1 -0
  36. package/dist/esm/chunk-TIVMC3ZS.mjs +828 -0
  37. package/dist/esm/chunk-TIVMC3ZS.mjs.map +1 -0
  38. package/dist/esm/index-B8S563vv.d.mts +929 -0
  39. package/dist/esm/index-BUV9B9f8.d.mts +929 -0
  40. package/dist/esm/index.d.mts +373 -0
  41. package/dist/esm/index.mjs +455 -0
  42. package/dist/esm/index.mjs.map +1 -0
  43. package/dist/esm/offer-receipt/index.d.mts +702 -0
  44. package/dist/esm/offer-receipt/index.mjs +97 -0
  45. package/dist/esm/offer-receipt/index.mjs.map +1 -0
  46. package/dist/esm/payment-identifier/index.d.mts +345 -0
  47. package/dist/esm/payment-identifier/index.mjs +39 -0
  48. package/dist/esm/payment-identifier/index.mjs.map +1 -0
  49. package/dist/esm/sign-in-with-x/index.d.mts +1117 -0
  50. package/dist/esm/sign-in-with-x/index.mjs +73 -0
  51. package/dist/esm/sign-in-with-x/index.mjs.map +1 -0
  52. package/package.json +124 -0
@@ -0,0 +1,876 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/sign-in-with-x/index.ts
31
+ var sign_in_with_x_exports = {};
32
+ __export(sign_in_with_x_exports, {
33
+ InMemorySIWxStorage: () => InMemorySIWxStorage,
34
+ SIGN_IN_WITH_X: () => SIGN_IN_WITH_X,
35
+ SIWxPayloadSchema: () => SIWxPayloadSchema,
36
+ SOLANA_DEVNET: () => SOLANA_DEVNET,
37
+ SOLANA_MAINNET: () => SOLANA_MAINNET,
38
+ SOLANA_TESTNET: () => SOLANA_TESTNET,
39
+ buildSIWxSchema: () => buildSIWxSchema,
40
+ createSIWxClientExtension: () => createSIWxClientExtension,
41
+ createSIWxClientHook: () => createSIWxClientHook,
42
+ createSIWxMessage: () => createSIWxMessage,
43
+ createSIWxPayload: () => createSIWxPayload,
44
+ createSIWxRequestHook: () => createSIWxRequestHook,
45
+ createSIWxResourceServerExtension: () => createSIWxResourceServerExtension,
46
+ createSIWxSettleHook: () => createSIWxSettleHook,
47
+ declareSIWxExtension: () => declareSIWxExtension,
48
+ decodeBase58: () => decodeBase58,
49
+ encodeBase58: () => encodeBase58,
50
+ encodeSIWxHeader: () => encodeSIWxHeader,
51
+ extractEVMChainId: () => extractEVMChainId,
52
+ extractSolanaChainReference: () => extractSolanaChainReference,
53
+ formatSIWEMessage: () => formatSIWEMessage,
54
+ formatSIWSMessage: () => formatSIWSMessage,
55
+ getEVMAddress: () => getEVMAddress,
56
+ getSolanaAddress: () => getSolanaAddress,
57
+ isEVMSigner: () => isEVMSigner,
58
+ isSolanaSigner: () => isSolanaSigner,
59
+ parseSIWxHeader: () => parseSIWxHeader,
60
+ signEVMMessage: () => signEVMMessage,
61
+ signSolanaMessage: () => signSolanaMessage,
62
+ validateSIWxMessage: () => validateSIWxMessage,
63
+ verifyEVMSignature: () => verifyEVMSignature,
64
+ verifySIWxSignature: () => verifySIWxSignature,
65
+ verifySolanaSignature: () => verifySolanaSignature,
66
+ wrapFetchWithSIWx: () => wrapFetchWithSIWx
67
+ });
68
+ module.exports = __toCommonJS(sign_in_with_x_exports);
69
+
70
+ // src/sign-in-with-x/types.ts
71
+ var import_zod = require("zod");
72
+ var SIGN_IN_WITH_X = "sign-in-with-x";
73
+ var SIWxPayloadSchema = import_zod.z.object({
74
+ domain: import_zod.z.string(),
75
+ address: import_zod.z.string(),
76
+ statement: import_zod.z.string().optional(),
77
+ uri: import_zod.z.string(),
78
+ version: import_zod.z.string(),
79
+ chainId: import_zod.z.string(),
80
+ type: import_zod.z.enum(["eip191", "ed25519"]),
81
+ nonce: import_zod.z.string(),
82
+ issuedAt: import_zod.z.string(),
83
+ expirationTime: import_zod.z.string().optional(),
84
+ notBefore: import_zod.z.string().optional(),
85
+ requestId: import_zod.z.string().optional(),
86
+ resources: import_zod.z.array(import_zod.z.string()).optional(),
87
+ signatureScheme: import_zod.z.enum(["eip191", "eip1271", "eip6492", "siws"]).optional(),
88
+ signature: import_zod.z.string()
89
+ });
90
+
91
+ // src/sign-in-with-x/solana.ts
92
+ var import_base = require("@scure/base");
93
+ var import_tweetnacl = __toESM(require("tweetnacl"));
94
+ var SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
95
+ var SOLANA_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
96
+ var SOLANA_TESTNET = "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";
97
+ function extractSolanaChainReference(chainId) {
98
+ const [, reference] = chainId.split(":");
99
+ return reference;
100
+ }
101
+ function formatSIWSMessage(info, address) {
102
+ const lines = [
103
+ `${info.domain} wants you to sign in with your Solana account:`,
104
+ address,
105
+ ""
106
+ ];
107
+ if (info.statement) {
108
+ lines.push(info.statement, "");
109
+ }
110
+ lines.push(
111
+ `URI: ${info.uri}`,
112
+ `Version: ${info.version}`,
113
+ `Chain ID: ${extractSolanaChainReference(info.chainId)}`,
114
+ `Nonce: ${info.nonce}`,
115
+ `Issued At: ${info.issuedAt}`
116
+ );
117
+ if (info.expirationTime) {
118
+ lines.push(`Expiration Time: ${info.expirationTime}`);
119
+ }
120
+ if (info.notBefore) {
121
+ lines.push(`Not Before: ${info.notBefore}`);
122
+ }
123
+ if (info.requestId) {
124
+ lines.push(`Request ID: ${info.requestId}`);
125
+ }
126
+ if (info.resources && info.resources.length > 0) {
127
+ lines.push("Resources:");
128
+ for (const resource of info.resources) {
129
+ lines.push(`- ${resource}`);
130
+ }
131
+ }
132
+ return lines.join("\n");
133
+ }
134
+ function verifySolanaSignature(message, signature, publicKey) {
135
+ const messageBytes = new TextEncoder().encode(message);
136
+ return import_tweetnacl.default.sign.detached.verify(messageBytes, signature, publicKey);
137
+ }
138
+ function decodeBase58(encoded) {
139
+ return import_base.base58.decode(encoded);
140
+ }
141
+ function encodeBase58(bytes) {
142
+ return import_base.base58.encode(bytes);
143
+ }
144
+ function isSolanaSigner(signer) {
145
+ if ("signMessages" in signer && typeof signer.signMessages === "function") {
146
+ return true;
147
+ }
148
+ if ("publicKey" in signer && signer.publicKey) {
149
+ const pk = signer.publicKey;
150
+ if (typeof pk === "object" && pk !== null && "toBase58" in pk) {
151
+ return true;
152
+ }
153
+ if (typeof pk === "string" && !pk.startsWith("0x")) {
154
+ return true;
155
+ }
156
+ }
157
+ return false;
158
+ }
159
+
160
+ // src/sign-in-with-x/schema.ts
161
+ function buildSIWxSchema() {
162
+ return {
163
+ $schema: "https://json-schema.org/draft/2020-12/schema",
164
+ type: "object",
165
+ properties: {
166
+ domain: { type: "string" },
167
+ address: { type: "string" },
168
+ statement: { type: "string" },
169
+ uri: { type: "string", format: "uri" },
170
+ version: { type: "string" },
171
+ chainId: { type: "string" },
172
+ type: { type: "string" },
173
+ nonce: { type: "string" },
174
+ issuedAt: { type: "string", format: "date-time" },
175
+ expirationTime: { type: "string", format: "date-time" },
176
+ notBefore: { type: "string", format: "date-time" },
177
+ requestId: { type: "string" },
178
+ resources: { type: "array", items: { type: "string", format: "uri" } },
179
+ signature: { type: "string" }
180
+ },
181
+ required: [
182
+ "domain",
183
+ "address",
184
+ "uri",
185
+ "version",
186
+ "chainId",
187
+ "type",
188
+ "nonce",
189
+ "issuedAt",
190
+ "signature"
191
+ ]
192
+ };
193
+ }
194
+
195
+ // src/sign-in-with-x/declare.ts
196
+ function getSignatureType(network) {
197
+ return network.startsWith("solana:") ? "ed25519" : "eip191";
198
+ }
199
+ function declareSIWxExtension(options = {}) {
200
+ const info = {
201
+ version: options.version ?? "1"
202
+ };
203
+ if (options.domain) {
204
+ info.domain = options.domain;
205
+ }
206
+ if (options.resourceUri) {
207
+ info.uri = options.resourceUri;
208
+ info.resources = [options.resourceUri];
209
+ }
210
+ if (options.statement) {
211
+ info.statement = options.statement;
212
+ }
213
+ let supportedChains = [];
214
+ if (options.network) {
215
+ const networks = Array.isArray(options.network) ? options.network : [options.network];
216
+ supportedChains = networks.map((network) => ({
217
+ chainId: network,
218
+ type: getSignatureType(network)
219
+ }));
220
+ }
221
+ const declaration = {
222
+ info,
223
+ supportedChains,
224
+ schema: buildSIWxSchema(),
225
+ _options: options
226
+ };
227
+ return { [SIGN_IN_WITH_X]: declaration };
228
+ }
229
+
230
+ // src/sign-in-with-x/parse.ts
231
+ var import_utils = require("@bankofai/x402-core/utils");
232
+ function parseSIWxHeader(header) {
233
+ if (!import_utils.Base64EncodedRegex.test(header)) {
234
+ throw new Error("Invalid SIWX header: not valid base64");
235
+ }
236
+ const jsonStr = (0, import_utils.safeBase64Decode)(header);
237
+ let rawPayload;
238
+ try {
239
+ rawPayload = JSON.parse(jsonStr);
240
+ } catch (error) {
241
+ if (error instanceof SyntaxError) {
242
+ throw new Error("Invalid SIWX header: not valid JSON");
243
+ }
244
+ throw error;
245
+ }
246
+ const parsed = SIWxPayloadSchema.safeParse(rawPayload);
247
+ if (!parsed.success) {
248
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
249
+ throw new Error(`Invalid SIWX header: ${issues}`);
250
+ }
251
+ return parsed.data;
252
+ }
253
+
254
+ // src/sign-in-with-x/validate.ts
255
+ var DEFAULT_MAX_AGE_MS = 5 * 60 * 1e3;
256
+ async function validateSIWxMessage(message, expectedResourceUri, options = {}) {
257
+ const expectedUrl = new URL(expectedResourceUri);
258
+ const maxAge = options.maxAge ?? DEFAULT_MAX_AGE_MS;
259
+ if (message.domain !== expectedUrl.hostname) {
260
+ return {
261
+ valid: false,
262
+ error: `Domain mismatch: expected "${expectedUrl.hostname}", got "${message.domain}"`
263
+ };
264
+ }
265
+ if (!message.uri.startsWith(expectedUrl.origin)) {
266
+ return {
267
+ valid: false,
268
+ error: `URI mismatch: expected origin "${expectedUrl.origin}", got "${message.uri}"`
269
+ };
270
+ }
271
+ const issuedAt = new Date(message.issuedAt);
272
+ if (isNaN(issuedAt.getTime())) {
273
+ return {
274
+ valid: false,
275
+ error: "Invalid issuedAt timestamp"
276
+ };
277
+ }
278
+ const age = Date.now() - issuedAt.getTime();
279
+ if (age > maxAge) {
280
+ return {
281
+ valid: false,
282
+ error: `Message too old: ${Math.round(age / 1e3)}s exceeds ${maxAge / 1e3}s limit`
283
+ };
284
+ }
285
+ if (age < 0) {
286
+ return {
287
+ valid: false,
288
+ error: "issuedAt is in the future"
289
+ };
290
+ }
291
+ if (message.expirationTime) {
292
+ const expiration = new Date(message.expirationTime);
293
+ if (isNaN(expiration.getTime())) {
294
+ return {
295
+ valid: false,
296
+ error: "Invalid expirationTime timestamp"
297
+ };
298
+ }
299
+ if (expiration < /* @__PURE__ */ new Date()) {
300
+ return {
301
+ valid: false,
302
+ error: "Message expired"
303
+ };
304
+ }
305
+ }
306
+ if (message.notBefore) {
307
+ const notBefore = new Date(message.notBefore);
308
+ if (isNaN(notBefore.getTime())) {
309
+ return {
310
+ valid: false,
311
+ error: "Invalid notBefore timestamp"
312
+ };
313
+ }
314
+ if (/* @__PURE__ */ new Date() < notBefore) {
315
+ return {
316
+ valid: false,
317
+ error: "Message not yet valid (notBefore is in the future)"
318
+ };
319
+ }
320
+ }
321
+ if (options.checkNonce) {
322
+ const nonceValid = await options.checkNonce(message.nonce);
323
+ if (!nonceValid) {
324
+ return {
325
+ valid: false,
326
+ error: "Nonce validation failed (possible replay attack)"
327
+ };
328
+ }
329
+ }
330
+ return { valid: true };
331
+ }
332
+
333
+ // src/sign-in-with-x/evm.ts
334
+ var import_viem = require("viem");
335
+ var import_siwe = require("@signinwithethereum/siwe");
336
+ function extractEVMChainId(chainId) {
337
+ const match = /^eip155:(\d+)$/.exec(chainId);
338
+ if (!match) {
339
+ throw new Error(`Invalid EVM chainId format: ${chainId}. Expected eip155:<number>`);
340
+ }
341
+ return parseInt(match[1], 10);
342
+ }
343
+ function formatSIWEMessage(info, address) {
344
+ const numericChainId = extractEVMChainId(info.chainId);
345
+ const siweMessage = new import_siwe.SiweMessage({
346
+ domain: info.domain,
347
+ address,
348
+ statement: info.statement,
349
+ uri: info.uri,
350
+ version: info.version,
351
+ chainId: numericChainId,
352
+ nonce: info.nonce,
353
+ issuedAt: info.issuedAt,
354
+ expirationTime: info.expirationTime,
355
+ notBefore: info.notBefore,
356
+ requestId: info.requestId,
357
+ resources: info.resources
358
+ });
359
+ return siweMessage.prepareMessage();
360
+ }
361
+ async function verifyEVMSignature(message, address, signature, verifier) {
362
+ const args = {
363
+ address,
364
+ message,
365
+ signature
366
+ };
367
+ if (verifier) {
368
+ return verifier(args);
369
+ }
370
+ return (0, import_viem.verifyMessage)(args);
371
+ }
372
+ function isEVMSigner(signer) {
373
+ if ("signMessages" in signer && typeof signer.signMessages === "function") {
374
+ return false;
375
+ }
376
+ if ("publicKey" in signer && signer.publicKey) {
377
+ const pk = signer.publicKey;
378
+ if (typeof pk === "object" && pk !== null && "toBase58" in pk) {
379
+ return false;
380
+ }
381
+ if (typeof pk === "string" && !pk.startsWith("0x")) {
382
+ return false;
383
+ }
384
+ }
385
+ if ("account" in signer && signer.account && typeof signer.account === "object") {
386
+ const account = signer.account;
387
+ if (account.address && account.address.startsWith("0x")) {
388
+ return true;
389
+ }
390
+ }
391
+ if ("address" in signer && typeof signer.address === "string" && signer.address.startsWith("0x")) {
392
+ return true;
393
+ }
394
+ return false;
395
+ }
396
+
397
+ // src/sign-in-with-x/verify.ts
398
+ async function verifySIWxSignature(payload, options) {
399
+ try {
400
+ if (payload.chainId.startsWith("eip155:")) {
401
+ return verifyEVMPayload(payload, options?.evmVerifier);
402
+ }
403
+ if (payload.chainId.startsWith("solana:")) {
404
+ return verifySolanaPayload(payload);
405
+ }
406
+ return {
407
+ valid: false,
408
+ error: `Unsupported chain namespace: ${payload.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`
409
+ };
410
+ } catch (error) {
411
+ return {
412
+ valid: false,
413
+ error: error instanceof Error ? error.message : "Verification failed"
414
+ };
415
+ }
416
+ }
417
+ async function verifyEVMPayload(payload, verifier) {
418
+ const message = formatSIWEMessage(
419
+ {
420
+ domain: payload.domain,
421
+ uri: payload.uri,
422
+ statement: payload.statement,
423
+ version: payload.version,
424
+ chainId: payload.chainId,
425
+ type: payload.type,
426
+ nonce: payload.nonce,
427
+ issuedAt: payload.issuedAt,
428
+ expirationTime: payload.expirationTime,
429
+ notBefore: payload.notBefore,
430
+ requestId: payload.requestId,
431
+ resources: payload.resources
432
+ },
433
+ payload.address
434
+ );
435
+ try {
436
+ const valid = await verifyEVMSignature(message, payload.address, payload.signature, verifier);
437
+ if (!valid) {
438
+ return {
439
+ valid: false,
440
+ error: "Signature verification failed"
441
+ };
442
+ }
443
+ return {
444
+ valid: true,
445
+ address: payload.address
446
+ };
447
+ } catch (error) {
448
+ return {
449
+ valid: false,
450
+ error: error instanceof Error ? error.message : "Signature verification failed"
451
+ };
452
+ }
453
+ }
454
+ function verifySolanaPayload(payload) {
455
+ const message = formatSIWSMessage(
456
+ {
457
+ domain: payload.domain,
458
+ uri: payload.uri,
459
+ statement: payload.statement,
460
+ version: payload.version,
461
+ chainId: payload.chainId,
462
+ type: payload.type,
463
+ nonce: payload.nonce,
464
+ issuedAt: payload.issuedAt,
465
+ expirationTime: payload.expirationTime,
466
+ notBefore: payload.notBefore,
467
+ requestId: payload.requestId,
468
+ resources: payload.resources
469
+ },
470
+ payload.address
471
+ );
472
+ let signature;
473
+ let publicKey;
474
+ try {
475
+ signature = decodeBase58(payload.signature);
476
+ publicKey = decodeBase58(payload.address);
477
+ } catch (error) {
478
+ return {
479
+ valid: false,
480
+ error: `Invalid Base58 encoding: ${error instanceof Error ? error.message : "decode failed"}`
481
+ };
482
+ }
483
+ if (signature.length !== 64) {
484
+ return {
485
+ valid: false,
486
+ error: `Invalid signature length: expected 64 bytes, got ${signature.length}`
487
+ };
488
+ }
489
+ if (publicKey.length !== 32) {
490
+ return {
491
+ valid: false,
492
+ error: `Invalid public key length: expected 32 bytes, got ${publicKey.length}`
493
+ };
494
+ }
495
+ const valid = verifySolanaSignature(message, signature, publicKey);
496
+ if (!valid) {
497
+ return {
498
+ valid: false,
499
+ error: "Solana signature verification failed"
500
+ };
501
+ }
502
+ return {
503
+ valid: true,
504
+ address: payload.address
505
+ };
506
+ }
507
+
508
+ // src/sign-in-with-x/sign.ts
509
+ function getEVMAddress(signer) {
510
+ if (signer.account?.address) {
511
+ return signer.account.address;
512
+ }
513
+ if (signer.address) {
514
+ return signer.address;
515
+ }
516
+ throw new Error("EVM signer missing address");
517
+ }
518
+ function getSolanaAddress(signer) {
519
+ if ("address" in signer && signer.address) {
520
+ return signer.address;
521
+ }
522
+ if ("publicKey" in signer) {
523
+ const pk = signer.publicKey;
524
+ return typeof pk === "string" ? pk : pk.toBase58();
525
+ }
526
+ throw new Error("Solana signer missing address or publicKey");
527
+ }
528
+ async function signEVMMessage(message, signer) {
529
+ if (signer.account) {
530
+ return signer.signMessage({ message, account: signer.account });
531
+ }
532
+ return signer.signMessage({ message });
533
+ }
534
+ async function signSolanaMessage(message, signer) {
535
+ const messageBytes = new TextEncoder().encode(message);
536
+ if ("signMessages" in signer) {
537
+ const results = await signer.signMessages([{ content: messageBytes, signatures: {} }]);
538
+ const sigDict = results[0];
539
+ const signatureBytes = Object.values(sigDict)[0];
540
+ return encodeBase58(signatureBytes);
541
+ }
542
+ if ("signMessage" in signer) {
543
+ const signatureBytes = await signer.signMessage(messageBytes);
544
+ return encodeBase58(signatureBytes);
545
+ }
546
+ throw new Error("Solana signer missing signMessage or signMessages method");
547
+ }
548
+
549
+ // src/sign-in-with-x/message.ts
550
+ function createSIWxMessage(serverInfo, address) {
551
+ if (serverInfo.chainId.startsWith("eip155:")) {
552
+ return formatSIWEMessage(serverInfo, address);
553
+ }
554
+ if (serverInfo.chainId.startsWith("solana:")) {
555
+ return formatSIWSMessage(serverInfo, address);
556
+ }
557
+ throw new Error(
558
+ `Unsupported chain namespace: ${serverInfo.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`
559
+ );
560
+ }
561
+
562
+ // src/sign-in-with-x/client.ts
563
+ async function createSIWxPayload(serverExtension, signer) {
564
+ const isSolana = serverExtension.chainId.startsWith("solana:");
565
+ const address = isSolana ? getSolanaAddress(signer) : getEVMAddress(signer);
566
+ const message = createSIWxMessage(serverExtension, address);
567
+ const signature = isSolana ? await signSolanaMessage(message, signer) : await signEVMMessage(message, signer);
568
+ return {
569
+ domain: serverExtension.domain,
570
+ address,
571
+ statement: serverExtension.statement,
572
+ uri: serverExtension.uri,
573
+ version: serverExtension.version,
574
+ chainId: serverExtension.chainId,
575
+ type: serverExtension.type,
576
+ nonce: serverExtension.nonce,
577
+ issuedAt: serverExtension.issuedAt,
578
+ expirationTime: serverExtension.expirationTime,
579
+ notBefore: serverExtension.notBefore,
580
+ requestId: serverExtension.requestId,
581
+ resources: serverExtension.resources,
582
+ signatureScheme: serverExtension.signatureScheme,
583
+ signature
584
+ };
585
+ }
586
+
587
+ // src/sign-in-with-x/encode.ts
588
+ var import_utils2 = require("@bankofai/x402-core/utils");
589
+ function encodeSIWxHeader(payload) {
590
+ return (0, import_utils2.safeBase64Encode)(JSON.stringify(payload));
591
+ }
592
+
593
+ // src/sign-in-with-x/hooks.ts
594
+ function createSIWxSettleHook(options) {
595
+ const { storage, onEvent } = options;
596
+ return async (ctx) => {
597
+ if (!ctx.result.success) return;
598
+ const address = ctx.result.payer;
599
+ if (!address) return;
600
+ const resourceUrl = ctx.paymentPayload.resource?.url;
601
+ if (!resourceUrl) return;
602
+ const resource = new URL(resourceUrl).pathname;
603
+ await storage.recordPayment(resource, address);
604
+ onEvent?.({ type: "payment_recorded", resource, address });
605
+ };
606
+ }
607
+ function createSIWxRequestHook(options) {
608
+ const { storage, verifyOptions, onEvent } = options;
609
+ const hasUsedNonce = typeof storage.hasUsedNonce === "function";
610
+ const hasRecordNonce = typeof storage.recordNonce === "function";
611
+ if (hasUsedNonce !== hasRecordNonce) {
612
+ throw new Error(
613
+ "SIWxStorage nonce tracking requires both hasUsedNonce and recordNonce to be implemented"
614
+ );
615
+ }
616
+ return async (context, routeConfig) => {
617
+ const header = context.adapter.getHeader(SIGN_IN_WITH_X) || context.adapter.getHeader(SIGN_IN_WITH_X.toLowerCase());
618
+ if (!header) return;
619
+ try {
620
+ const payload = parseSIWxHeader(header);
621
+ const resourceUri = context.adapter.getUrl();
622
+ const validation = await validateSIWxMessage(payload, resourceUri);
623
+ if (!validation.valid) {
624
+ onEvent?.({ type: "validation_failed", resource: context.path, error: validation.error });
625
+ return;
626
+ }
627
+ const verification = await verifySIWxSignature(payload, verifyOptions);
628
+ if (!verification.valid || !verification.address) {
629
+ onEvent?.({ type: "validation_failed", resource: context.path, error: verification.error });
630
+ return;
631
+ }
632
+ if (storage.hasUsedNonce) {
633
+ const nonceUsed = await storage.hasUsedNonce(payload.nonce);
634
+ if (nonceUsed) {
635
+ onEvent?.({ type: "nonce_reused", resource: context.path, nonce: payload.nonce });
636
+ return;
637
+ }
638
+ }
639
+ const isAuthOnly = Array.isArray(routeConfig?.accepts) && routeConfig.accepts.length === 0;
640
+ const shouldGrant = isAuthOnly || await storage.hasPaid(context.path, verification.address);
641
+ if (shouldGrant) {
642
+ if (storage.recordNonce) {
643
+ await storage.recordNonce(payload.nonce);
644
+ }
645
+ onEvent?.({
646
+ type: "access_granted",
647
+ resource: context.path,
648
+ address: verification.address
649
+ });
650
+ return { grantAccess: true };
651
+ }
652
+ } catch (err) {
653
+ onEvent?.({
654
+ type: "validation_failed",
655
+ resource: context.path,
656
+ error: err instanceof Error ? err.message : "Unknown error"
657
+ });
658
+ }
659
+ };
660
+ }
661
+ function createSIWxClientHook(signer) {
662
+ const signerIsSolana = isSolanaSigner(signer);
663
+ const expectedSignatureType = signerIsSolana ? "ed25519" : "eip191";
664
+ return async (context) => {
665
+ const extensions = context.paymentRequired.extensions ?? {};
666
+ const siwxExtension = extensions[SIGN_IN_WITH_X];
667
+ if (!siwxExtension?.supportedChains) return;
668
+ try {
669
+ const matchingChain = siwxExtension.supportedChains.find(
670
+ (chain) => chain.type === expectedSignatureType
671
+ );
672
+ if (!matchingChain) {
673
+ return;
674
+ }
675
+ const completeInfo = {
676
+ ...siwxExtension.info,
677
+ chainId: matchingChain.chainId,
678
+ type: matchingChain.type
679
+ };
680
+ const payload = await createSIWxPayload(completeInfo, signer);
681
+ const header = encodeSIWxHeader(payload);
682
+ return { headers: { [SIGN_IN_WITH_X]: header } };
683
+ } catch {
684
+ }
685
+ };
686
+ }
687
+ function createSIWxClientExtension(options) {
688
+ const hooks = options.signers.map(createSIWxClientHook);
689
+ return {
690
+ key: SIGN_IN_WITH_X,
691
+ transportHooks: {
692
+ http: {
693
+ onPaymentRequired: async (_declaration, context) => {
694
+ for (const hook of hooks) {
695
+ const result = await hook(context);
696
+ if (result?.headers) return result;
697
+ }
698
+ }
699
+ }
700
+ }
701
+ };
702
+ }
703
+
704
+ // src/sign-in-with-x/server.ts
705
+ async function enrichSIWxPaymentRequiredResponse(declaration, context) {
706
+ const decl = declaration;
707
+ const opts = decl._options ?? {};
708
+ const resourceUri = opts.resourceUri ?? context.resourceInfo.url;
709
+ let domain = opts.domain;
710
+ if (!domain && resourceUri) {
711
+ try {
712
+ domain = new URL(resourceUri).hostname;
713
+ } catch {
714
+ domain = void 0;
715
+ }
716
+ }
717
+ let supportedNetworks;
718
+ if (opts.network) {
719
+ supportedNetworks = Array.isArray(opts.network) ? opts.network : [opts.network];
720
+ } else {
721
+ supportedNetworks = [...new Set(context.requirements.map((r) => r.network))];
722
+ }
723
+ const nonce = Array.from(globalThis.crypto.getRandomValues(new Uint8Array(16))).map((b) => b.toString(16).padStart(2, "0")).join("");
724
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
725
+ const expirationTime = opts.expirationSeconds !== void 0 ? new Date(Date.now() + opts.expirationSeconds * 1e3).toISOString() : void 0;
726
+ const info = {
727
+ domain: domain ?? "",
728
+ uri: resourceUri,
729
+ version: opts.version ?? "1",
730
+ nonce,
731
+ issuedAt,
732
+ resources: [resourceUri]
733
+ };
734
+ if (expirationTime) {
735
+ info.expirationTime = expirationTime;
736
+ }
737
+ if (opts.statement) {
738
+ info.statement = opts.statement;
739
+ }
740
+ const supportedChains = supportedNetworks.map((network) => ({
741
+ chainId: network,
742
+ type: getSignatureType(network)
743
+ }));
744
+ return {
745
+ info,
746
+ supportedChains,
747
+ schema: buildSIWxSchema()
748
+ };
749
+ }
750
+ function createSIWxResourceServerExtension(options) {
751
+ const settleHook = createSIWxSettleHook(options);
752
+ const requestHook = createSIWxRequestHook(options);
753
+ return {
754
+ key: SIGN_IN_WITH_X,
755
+ enrichPaymentRequiredResponse: enrichSIWxPaymentRequiredResponse,
756
+ transportHooks: {
757
+ http: {
758
+ onProtectedRequest: async (_declaration, context, routeConfig) => requestHook(context, routeConfig)
759
+ }
760
+ },
761
+ hooks: {
762
+ onAfterSettle: async (_declaration, context) => settleHook(context)
763
+ }
764
+ };
765
+ }
766
+
767
+ // src/sign-in-with-x/fetch.ts
768
+ var import_http = require("@bankofai/x402-core/http");
769
+ function wrapFetchWithSIWx(fetch, signer) {
770
+ return async (input, init) => {
771
+ const request = new Request(input, init);
772
+ const clonedRequest = request.clone();
773
+ const response = await fetch(request);
774
+ if (response.status !== 402) {
775
+ return response;
776
+ }
777
+ const paymentRequiredHeader = response.headers.get("PAYMENT-REQUIRED");
778
+ if (!paymentRequiredHeader) {
779
+ return response;
780
+ }
781
+ const paymentRequired = (0, import_http.decodePaymentRequiredHeader)(paymentRequiredHeader);
782
+ const siwxExtension = paymentRequired.extensions?.[SIGN_IN_WITH_X];
783
+ if (!siwxExtension?.supportedChains) {
784
+ return response;
785
+ }
786
+ if (clonedRequest.headers.has(SIGN_IN_WITH_X)) {
787
+ throw new Error("SIWX authentication already attempted");
788
+ }
789
+ const paymentNetwork = paymentRequired.accepts?.[0]?.network;
790
+ if (!paymentNetwork) {
791
+ return response;
792
+ }
793
+ const matchingChain = siwxExtension.supportedChains.find(
794
+ (chain) => chain.chainId === paymentNetwork
795
+ );
796
+ if (!matchingChain) {
797
+ return response;
798
+ }
799
+ const completeInfo = {
800
+ ...siwxExtension.info,
801
+ chainId: matchingChain.chainId,
802
+ type: matchingChain.type
803
+ };
804
+ const payload = await createSIWxPayload(completeInfo, signer);
805
+ const siwxHeader = encodeSIWxHeader(payload);
806
+ clonedRequest.headers.set(SIGN_IN_WITH_X, siwxHeader);
807
+ return fetch(clonedRequest);
808
+ };
809
+ }
810
+
811
+ // src/sign-in-with-x/storage.ts
812
+ var InMemorySIWxStorage = class {
813
+ constructor() {
814
+ this.paidAddresses = /* @__PURE__ */ new Map();
815
+ }
816
+ /**
817
+ * Check if an address has paid for a resource.
818
+ *
819
+ * @param resource - The resource path
820
+ * @param address - The wallet address to check
821
+ * @returns True if the address has paid
822
+ */
823
+ hasPaid(resource, address) {
824
+ return this.paidAddresses.get(resource)?.has(address.toLowerCase()) ?? false;
825
+ }
826
+ /**
827
+ * Record that an address has paid for a resource.
828
+ *
829
+ * @param resource - The resource path
830
+ * @param address - The wallet address that paid
831
+ */
832
+ recordPayment(resource, address) {
833
+ if (!this.paidAddresses.has(resource)) {
834
+ this.paidAddresses.set(resource, /* @__PURE__ */ new Set());
835
+ }
836
+ this.paidAddresses.get(resource).add(address.toLowerCase());
837
+ }
838
+ };
839
+ // Annotate the CommonJS export names for ESM import in node:
840
+ 0 && (module.exports = {
841
+ InMemorySIWxStorage,
842
+ SIGN_IN_WITH_X,
843
+ SIWxPayloadSchema,
844
+ SOLANA_DEVNET,
845
+ SOLANA_MAINNET,
846
+ SOLANA_TESTNET,
847
+ buildSIWxSchema,
848
+ createSIWxClientExtension,
849
+ createSIWxClientHook,
850
+ createSIWxMessage,
851
+ createSIWxPayload,
852
+ createSIWxRequestHook,
853
+ createSIWxResourceServerExtension,
854
+ createSIWxSettleHook,
855
+ declareSIWxExtension,
856
+ decodeBase58,
857
+ encodeBase58,
858
+ encodeSIWxHeader,
859
+ extractEVMChainId,
860
+ extractSolanaChainReference,
861
+ formatSIWEMessage,
862
+ formatSIWSMessage,
863
+ getEVMAddress,
864
+ getSolanaAddress,
865
+ isEVMSigner,
866
+ isSolanaSigner,
867
+ parseSIWxHeader,
868
+ signEVMMessage,
869
+ signSolanaMessage,
870
+ validateSIWxMessage,
871
+ verifyEVMSignature,
872
+ verifySIWxSignature,
873
+ verifySolanaSignature,
874
+ wrapFetchWithSIWx
875
+ });
876
+ //# sourceMappingURL=index.js.map