@x402/extensions 2.6.0 → 2.8.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 +27 -22
  2. package/dist/cjs/bazaar/index.d.ts +1 -1
  3. package/dist/cjs/bazaar/index.js +137 -7
  4. package/dist/cjs/bazaar/index.js.map +1 -1
  5. package/dist/cjs/{index-CtOzXcjN.d.ts → index-DihVrF7v.d.ts} +49 -9
  6. package/dist/cjs/index.d.ts +33 -23
  7. package/dist/cjs/index.js +1015 -20
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/offer-receipt/index.d.ts +702 -0
  10. package/dist/cjs/offer-receipt/index.js +909 -0
  11. package/dist/cjs/offer-receipt/index.js.map +1 -0
  12. package/dist/cjs/sign-in-with-x/index.d.ts +7 -1
  13. package/dist/cjs/sign-in-with-x/index.js +5 -5
  14. package/dist/cjs/sign-in-with-x/index.js.map +1 -1
  15. package/dist/esm/bazaar/index.d.mts +1 -1
  16. package/dist/esm/bazaar/index.mjs +5 -1
  17. package/dist/esm/{chunk-O34SGKEP.mjs → chunk-5UYCX7A7.mjs} +6 -6
  18. package/dist/esm/chunk-5UYCX7A7.mjs.map +1 -0
  19. package/dist/esm/{chunk-ANAQVNUK.mjs → chunk-I2JLOI4L.mjs} +136 -8
  20. package/dist/esm/chunk-I2JLOI4L.mjs.map +1 -0
  21. package/dist/esm/chunk-TYR4QHVX.mjs +828 -0
  22. package/dist/esm/chunk-TYR4QHVX.mjs.map +1 -0
  23. package/dist/esm/{index-CtOzXcjN.d.mts → index-DihVrF7v.d.mts} +49 -9
  24. package/dist/esm/index.d.mts +33 -23
  25. package/dist/esm/index.mjs +102 -10
  26. package/dist/esm/index.mjs.map +1 -1
  27. package/dist/esm/offer-receipt/index.d.mts +702 -0
  28. package/dist/esm/offer-receipt/index.mjs +97 -0
  29. package/dist/esm/offer-receipt/index.mjs.map +1 -0
  30. package/dist/esm/sign-in-with-x/index.d.mts +7 -1
  31. package/dist/esm/sign-in-with-x/index.mjs +1 -1
  32. package/package.json +14 -2
  33. package/dist/esm/chunk-ANAQVNUK.mjs.map +0 -1
  34. package/dist/esm/chunk-O34SGKEP.mjs.map +0 -1
package/dist/cjs/index.js CHANGED
@@ -35,10 +35,13 @@ __export(src_exports, {
35
35
  ERC20_APPROVAL_GAS_SPONSORING: () => ERC20_APPROVAL_GAS_SPONSORING,
36
36
  ERC20_APPROVAL_GAS_SPONSORING_VERSION: () => ERC20_APPROVAL_GAS_SPONSORING_VERSION,
37
37
  InMemorySIWxStorage: () => InMemorySIWxStorage,
38
+ OFFER_RECEIPT: () => OFFER_RECEIPT,
39
+ OFFER_TYPES: () => OFFER_TYPES,
38
40
  PAYMENT_IDENTIFIER: () => PAYMENT_IDENTIFIER,
39
41
  PAYMENT_ID_MAX_LENGTH: () => PAYMENT_ID_MAX_LENGTH,
40
42
  PAYMENT_ID_MIN_LENGTH: () => PAYMENT_ID_MIN_LENGTH,
41
43
  PAYMENT_ID_PATTERN: () => PAYMENT_ID_PATTERN,
44
+ RECEIPT_TYPES: () => RECEIPT_TYPES,
42
45
  SIGN_IN_WITH_X: () => SIGN_IN_WITH_X,
43
46
  SIWxPayloadSchema: () => SIWxPayloadSchema,
44
47
  SOLANA_DEVNET: () => SOLANA_DEVNET,
@@ -47,7 +50,19 @@ __export(src_exports, {
47
50
  appendPaymentIdentifierToExtensions: () => appendPaymentIdentifierToExtensions,
48
51
  bazaarResourceServerExtension: () => bazaarResourceServerExtension,
49
52
  buildSIWxSchema: () => buildSIWxSchema,
53
+ canonicalize: () => canonicalize,
54
+ convertNetworkStringToCAIP2: () => convertNetworkStringToCAIP2,
55
+ createEIP712OfferReceiptIssuer: () => createEIP712OfferReceiptIssuer,
50
56
  createErc20ApprovalGasSponsoringExtension: () => createErc20ApprovalGasSponsoringExtension,
57
+ createJWS: () => createJWS,
58
+ createJWSOfferReceiptIssuer: () => createJWSOfferReceiptIssuer,
59
+ createOfferDomain: () => createOfferDomain,
60
+ createOfferEIP712: () => createOfferEIP712,
61
+ createOfferJWS: () => createOfferJWS,
62
+ createOfferReceiptExtension: () => createOfferReceiptExtension,
63
+ createReceiptDomain: () => createReceiptDomain,
64
+ createReceiptEIP712: () => createReceiptEIP712,
65
+ createReceiptJWS: () => createReceiptJWS,
51
66
  createSIWxClientHook: () => createSIWxClientHook,
52
67
  createSIWxMessage: () => createSIWxMessage,
53
68
  createSIWxPayload: () => createSIWxPayload,
@@ -56,41 +71,68 @@ __export(src_exports, {
56
71
  declareDiscoveryExtension: () => declareDiscoveryExtension,
57
72
  declareEip2612GasSponsoringExtension: () => declareEip2612GasSponsoringExtension,
58
73
  declareErc20ApprovalGasSponsoringExtension: () => declareErc20ApprovalGasSponsoringExtension,
74
+ declareOfferReceiptExtension: () => declareOfferReceiptExtension,
59
75
  declarePaymentIdentifierExtension: () => declarePaymentIdentifierExtension,
60
76
  declareSIWxExtension: () => declareSIWxExtension,
61
77
  decodeBase58: () => decodeBase58,
78
+ decodeSignedOffers: () => decodeSignedOffers,
62
79
  encodeBase58: () => encodeBase58,
63
80
  encodeSIWxHeader: () => encodeSIWxHeader,
64
81
  erc20ApprovalGasSponsoringSchema: () => erc20ApprovalGasSponsoringSchema,
65
82
  extractAndValidatePaymentIdentifier: () => extractAndValidatePaymentIdentifier,
83
+ extractChainIdFromCAIP2: () => extractChainIdFromCAIP2,
66
84
  extractDiscoveryInfo: () => extractDiscoveryInfo,
67
85
  extractDiscoveryInfoFromExtension: () => extractDiscoveryInfoFromExtension,
68
86
  extractDiscoveryInfoV1: () => extractDiscoveryInfoV1,
87
+ extractEIP155ChainId: () => extractEIP155ChainId,
69
88
  extractEVMChainId: () => extractEVMChainId,
70
89
  extractEip2612GasSponsoringInfo: () => extractEip2612GasSponsoringInfo,
71
90
  extractErc20ApprovalGasSponsoringInfo: () => extractErc20ApprovalGasSponsoringInfo,
91
+ extractJWSHeader: () => extractJWSHeader,
92
+ extractJWSPayload: () => extractJWSPayload,
93
+ extractOfferPayload: () => extractOfferPayload,
94
+ extractOffersFromPaymentRequired: () => extractOffersFromPaymentRequired,
72
95
  extractPaymentIdentifier: () => extractPaymentIdentifier,
96
+ extractPublicKeyFromKid: () => extractPublicKeyFromKid,
97
+ extractReceiptFromResponse: () => extractReceiptFromResponse,
98
+ extractReceiptPayload: () => extractReceiptPayload,
73
99
  extractResourceMetadataV1: () => extractResourceMetadataV1,
74
100
  extractSolanaChainReference: () => extractSolanaChainReference,
101
+ findAcceptsObjectFromSignedOffer: () => findAcceptsObjectFromSignedOffer,
75
102
  formatSIWEMessage: () => formatSIWEMessage,
76
103
  formatSIWSMessage: () => formatSIWSMessage,
77
104
  generatePaymentId: () => generatePaymentId,
105
+ getCanonicalBytes: () => getCanonicalBytes,
78
106
  getEVMAddress: () => getEVMAddress,
79
107
  getSolanaAddress: () => getSolanaAddress,
80
108
  hasPaymentIdentifier: () => hasPaymentIdentifier,
109
+ hashCanonical: () => hashCanonical,
110
+ hashOfferTypedData: () => hashOfferTypedData,
111
+ hashReceiptTypedData: () => hashReceiptTypedData,
81
112
  isBodyExtensionConfig: () => isBodyExtensionConfig,
82
113
  isDiscoverableV1: () => isDiscoverableV1,
114
+ isEIP712SignedOffer: () => isEIP712SignedOffer,
115
+ isEIP712SignedReceipt: () => isEIP712SignedReceipt,
116
+ isEIP712Signer: () => isEIP712Signer,
83
117
  isEVMSigner: () => isEVMSigner,
118
+ isJWSSignedOffer: () => isJWSSignedOffer,
119
+ isJWSSignedReceipt: () => isJWSSignedReceipt,
120
+ isJWSSigner: () => isJWSSigner,
84
121
  isMcpExtensionConfig: () => isMcpExtensionConfig,
85
122
  isPaymentIdentifierExtension: () => isPaymentIdentifierExtension,
86
123
  isPaymentIdentifierRequired: () => isPaymentIdentifierRequired,
87
124
  isQueryExtensionConfig: () => isQueryExtensionConfig,
88
125
  isSolanaSigner: () => isSolanaSigner,
89
126
  isValidPaymentId: () => isValidPaymentId,
127
+ isValidRouteTemplate: () => isValidRouteTemplate,
90
128
  parseSIWxHeader: () => parseSIWxHeader,
91
129
  paymentIdentifierResourceServerExtension: () => paymentIdentifierResourceServerExtension,
92
130
  paymentIdentifierSchema: () => paymentIdentifierSchema,
131
+ prepareOfferForEIP712: () => prepareOfferForEIP712,
132
+ prepareReceiptForEIP712: () => prepareReceiptForEIP712,
93
133
  signEVMMessage: () => signEVMMessage,
134
+ signOfferEIP712: () => signOfferEIP712,
135
+ signReceiptEIP712: () => signReceiptEIP712,
94
136
  signSolanaMessage: () => signSolanaMessage,
95
137
  siwxResourceServerExtension: () => siwxResourceServerExtension,
96
138
  validateAndExtract: () => validateAndExtract,
@@ -99,8 +141,14 @@ __export(src_exports, {
99
141
  validateErc20ApprovalGasSponsoringInfo: () => validateErc20ApprovalGasSponsoringInfo,
100
142
  validatePaymentIdentifier: () => validatePaymentIdentifier,
101
143
  validatePaymentIdentifierRequirement: () => validatePaymentIdentifierRequirement,
144
+ validateRouteTemplate: () => validateRouteTemplate,
102
145
  validateSIWxMessage: () => validateSIWxMessage,
103
146
  verifyEVMSignature: () => verifyEVMSignature,
147
+ verifyOfferSignatureEIP712: () => verifyOfferSignatureEIP712,
148
+ verifyOfferSignatureJWS: () => verifyOfferSignatureJWS,
149
+ verifyReceiptMatchesOffer: () => verifyReceiptMatchesOffer,
150
+ verifyReceiptSignatureEIP712: () => verifyReceiptSignatureEIP712,
151
+ verifyReceiptSignatureJWS: () => verifyReceiptSignatureJWS,
104
152
  verifySIWxSignature: () => verifySIWxSignature,
105
153
  verifySolanaSignature: () => verifySolanaSignature,
106
154
  withBazaar: () => withBazaar,
@@ -129,6 +177,8 @@ function createQueryDiscoveryExtension({
129
177
  method,
130
178
  input = {},
131
179
  inputSchema = { properties: {} },
180
+ pathParams,
181
+ pathParamsSchema,
132
182
  output
133
183
  }) {
134
184
  return {
@@ -136,7 +186,8 @@ function createQueryDiscoveryExtension({
136
186
  input: {
137
187
  type: "http",
138
188
  ...method ? { method } : {},
139
- ...input ? { queryParams: input } : {}
189
+ ...input ? { queryParams: input } : {},
190
+ ...pathParams ? { pathParams } : {}
140
191
  },
141
192
  ...output?.example ? {
142
193
  output: {
@@ -165,9 +216,18 @@ function createQueryDiscoveryExtension({
165
216
  type: "object",
166
217
  ...typeof inputSchema === "object" ? inputSchema : {}
167
218
  }
219
+ } : {},
220
+ ...pathParamsSchema ? {
221
+ pathParams: {
222
+ type: "object",
223
+ ...typeof pathParamsSchema === "object" ? pathParamsSchema : {}
224
+ }
168
225
  } : {}
169
226
  },
170
227
  required: ["type"],
228
+ // pathParams and method are not declared here at schema build time --
229
+ // the server extension's enrichDeclaration adds them to both info and schema
230
+ // atomically at request time, keeping data and schema consistent.
171
231
  additionalProperties: false
172
232
  },
173
233
  ...output?.example ? {
@@ -194,6 +254,8 @@ function createBodyDiscoveryExtension({
194
254
  method,
195
255
  input = {},
196
256
  inputSchema = { properties: {} },
257
+ pathParams,
258
+ pathParamsSchema,
197
259
  bodyType,
198
260
  output
199
261
  }) {
@@ -203,7 +265,8 @@ function createBodyDiscoveryExtension({
203
265
  type: "http",
204
266
  ...method ? { method } : {},
205
267
  bodyType,
206
- body: input
268
+ body: input,
269
+ ...pathParams ? { pathParams } : {}
207
270
  },
208
271
  ...output?.example ? {
209
272
  output: {
@@ -231,9 +294,18 @@ function createBodyDiscoveryExtension({
231
294
  type: "string",
232
295
  enum: ["json", "form-data", "text"]
233
296
  },
234
- body: inputSchema
297
+ body: inputSchema,
298
+ ...pathParamsSchema ? {
299
+ pathParams: {
300
+ type: "object",
301
+ ...typeof pathParamsSchema === "object" ? pathParamsSchema : {}
302
+ }
303
+ } : {}
235
304
  },
236
305
  required: ["type", "bodyType", "body"],
306
+ // pathParams and method are not declared here at schema build time --
307
+ // the server extension's enrichDeclaration adds them to both info and schema
308
+ // atomically at request time, keeping data and schema consistent.
237
309
  additionalProperties: false
238
310
  },
239
311
  ...output?.example ? {
@@ -354,9 +426,57 @@ function declareDiscoveryExtension(config) {
354
426
  }
355
427
 
356
428
  // src/bazaar/server.ts
429
+ var BRACKET_PARAM_REGEX = /\[([^\]]+)\]/;
430
+ var BRACKET_PARAM_REGEX_ALL = /\[([^\]]+)\]/g;
431
+ var COLON_PARAM_REGEX = /:([a-zA-Z_][a-zA-Z0-9_]*)/;
357
432
  function isHTTPRequestContext(ctx) {
358
433
  return ctx !== null && typeof ctx === "object" && "method" in ctx && "adapter" in ctx;
359
434
  }
435
+ function normalizeWildcardPattern(pattern) {
436
+ if (!pattern.includes("*")) {
437
+ return pattern;
438
+ }
439
+ let counter = 0;
440
+ return pattern.split("/").map((seg) => {
441
+ if (seg === "*") {
442
+ counter++;
443
+ return `:var${counter}`;
444
+ }
445
+ return seg;
446
+ }).join("/");
447
+ }
448
+ function extractDynamicRouteInfo(routePattern, urlPath) {
449
+ const hasBracket = BRACKET_PARAM_REGEX.test(routePattern);
450
+ const hasColon = COLON_PARAM_REGEX.test(routePattern);
451
+ if (!hasBracket && !hasColon) {
452
+ return null;
453
+ }
454
+ const normalizedPattern = hasBracket ? routePattern.replace(BRACKET_PARAM_REGEX_ALL, ":$1") : routePattern;
455
+ const pathParams = extractPathParams(normalizedPattern, urlPath, false);
456
+ return { routeTemplate: normalizedPattern, pathParams };
457
+ }
458
+ function extractPathParams(routePattern, urlPath, isBracket) {
459
+ const paramNames = [];
460
+ const splitRegex = isBracket ? BRACKET_PARAM_REGEX : COLON_PARAM_REGEX;
461
+ const parts = routePattern.split(splitRegex);
462
+ const regexParts = [];
463
+ parts.forEach((part, i) => {
464
+ if (i % 2 === 0) {
465
+ regexParts.push(part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
466
+ } else {
467
+ paramNames.push(part);
468
+ regexParts.push("([^/]+)");
469
+ }
470
+ });
471
+ const regex = new RegExp(`^${regexParts.join("")}$`);
472
+ const match = urlPath.match(regex);
473
+ if (!match) return {};
474
+ const result = {};
475
+ paramNames.forEach((name, idx) => {
476
+ result[name] = match[idx + 1];
477
+ });
478
+ return result;
479
+ }
360
480
  var bazaarResourceServerExtension = {
361
481
  key: BAZAAR.key,
362
482
  enrichDeclaration: (declaration, transportContext) => {
@@ -376,7 +496,7 @@ var bazaarResourceServerExtension = {
376
496
  enum: [method]
377
497
  }
378
498
  };
379
- return {
499
+ const enrichedResult = {
380
500
  ...extension,
381
501
  info: {
382
502
  ...extension.info || {},
@@ -400,6 +520,37 @@ var bazaarResourceServerExtension = {
400
520
  }
401
521
  }
402
522
  };
523
+ const rawRoutePattern = transportContext.routePattern;
524
+ const routePattern = rawRoutePattern ? normalizeWildcardPattern(rawRoutePattern) : void 0;
525
+ const dynamicRoute = routePattern ? extractDynamicRouteInfo(routePattern, transportContext.adapter.getPath()) : null;
526
+ if (dynamicRoute) {
527
+ const inputSchemaProps = enrichedResult.schema?.properties?.input?.properties || {};
528
+ const hasPathParamsInSchema = "pathParams" in inputSchemaProps;
529
+ return {
530
+ ...enrichedResult,
531
+ routeTemplate: dynamicRoute.routeTemplate,
532
+ info: {
533
+ ...enrichedResult.info,
534
+ input: { ...enrichedResult.info.input, pathParams: dynamicRoute.pathParams }
535
+ },
536
+ ...!hasPathParamsInSchema ? {
537
+ schema: {
538
+ ...enrichedResult.schema,
539
+ properties: {
540
+ ...enrichedResult.schema?.properties,
541
+ input: {
542
+ ...enrichedResult.schema?.properties?.input,
543
+ properties: {
544
+ ...inputSchemaProps,
545
+ pathParams: { type: "object" }
546
+ }
547
+ }
548
+ }
549
+ }
550
+ } : {}
551
+ };
552
+ }
553
+ return enrichedResult;
403
554
  }
404
555
  };
405
556
 
@@ -521,6 +672,23 @@ function extractResourceMetadataV1(paymentRequirements) {
521
672
  }
522
673
 
523
674
  // src/bazaar/facilitator.ts
675
+ var ROUTE_TEMPLATE_REGEX = /^\/[a-zA-Z0-9_/:.\-~%]+$/;
676
+ function isValidRouteTemplate(value) {
677
+ if (!value) return false;
678
+ if (!ROUTE_TEMPLATE_REGEX.test(value)) return false;
679
+ let decoded;
680
+ try {
681
+ decoded = decodeURIComponent(value);
682
+ } catch {
683
+ return false;
684
+ }
685
+ if (decoded.includes("..")) return false;
686
+ if (decoded.includes("://")) return false;
687
+ return true;
688
+ }
689
+ function validateRouteTemplate(value) {
690
+ return isValidRouteTemplate(value) ? value : void 0;
691
+ }
524
692
  function validateDiscoveryExtension(extension) {
525
693
  try {
526
694
  const ajv = new import__.default({ strict: false, allErrors: true });
@@ -546,12 +714,18 @@ function validateDiscoveryExtension(extension) {
546
714
  function extractDiscoveryInfo(paymentPayload, paymentRequirements, validate = true) {
547
715
  let discoveryInfo = null;
548
716
  let resourceUrl;
717
+ let routeTemplate;
549
718
  if (paymentPayload.x402Version === 2) {
550
719
  resourceUrl = paymentPayload.resource?.url ?? "";
551
720
  if (paymentPayload.extensions) {
552
721
  const bazaarExtension = paymentPayload.extensions[BAZAAR.key];
553
722
  if (bazaarExtension && typeof bazaarExtension === "object") {
554
723
  try {
724
+ const rawExt = bazaarExtension;
725
+ const rawTemplate = typeof rawExt.routeTemplate === "string" ? rawExt.routeTemplate : void 0;
726
+ if (isValidRouteTemplate(rawTemplate)) {
727
+ routeTemplate = rawTemplate;
728
+ }
555
729
  const extension = bazaarExtension;
556
730
  if (validate) {
557
731
  const result = validateDiscoveryExtension(extension);
@@ -581,7 +755,7 @@ function extractDiscoveryInfo(paymentPayload, paymentRequirements, validate = tr
581
755
  return null;
582
756
  }
583
757
  const url = new URL(resourceUrl);
584
- const normalizedResourceUrl = `${url.origin}${url.pathname}`;
758
+ const canonicalUrl = routeTemplate ? `${url.origin}${routeTemplate}` : `${url.origin}${url.pathname}`;
585
759
  let description;
586
760
  let mimeType;
587
761
  if (paymentPayload.x402Version === 2) {
@@ -593,7 +767,7 @@ function extractDiscoveryInfo(paymentPayload, paymentRequirements, validate = tr
593
767
  mimeType = requirementsV1.mimeType;
594
768
  }
595
769
  const base = {
596
- resourceUrl: normalizedResourceUrl,
770
+ resourceUrl: canonicalUrl,
597
771
  description,
598
772
  mimeType,
599
773
  x402Version: paymentPayload.x402Version,
@@ -602,7 +776,7 @@ function extractDiscoveryInfo(paymentPayload, paymentRequirements, validate = tr
602
776
  if (discoveryInfo.input.type === "mcp") {
603
777
  return { ...base, toolName: discoveryInfo.input.toolName };
604
778
  }
605
- return { ...base, method: discoveryInfo.input.method };
779
+ return { ...base, routeTemplate, method: discoveryInfo.input.method };
606
780
  }
607
781
  function extractDiscoveryInfoFromExtension(extension, validate = true) {
608
782
  if (validate) {
@@ -832,7 +1006,6 @@ function declareSIWxExtension(options = {}) {
832
1006
  }
833
1007
 
834
1008
  // src/sign-in-with-x/server.ts
835
- var import_crypto = require("crypto");
836
1009
  var siwxResourceServerExtension = {
837
1010
  key: SIGN_IN_WITH_X,
838
1011
  enrichPaymentRequiredResponse: async (declaration, context) => {
@@ -852,7 +1025,7 @@ var siwxResourceServerExtension = {
852
1025
  } else {
853
1026
  networks = [...new Set(context.requirements.map((r) => r.network))];
854
1027
  }
855
- const nonce = (0, import_crypto.randomBytes)(16).toString("hex");
1028
+ const nonce = Array.from(globalThis.crypto.getRandomValues(new Uint8Array(16))).map((b) => b.toString(16).padStart(2, "0")).join("");
856
1029
  const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
857
1030
  const expirationSeconds = opts.expirationSeconds;
858
1031
  const expirationTime = expirationSeconds !== void 0 ? new Date(Date.now() + expirationSeconds * 1e3).toISOString() : void 0;
@@ -1341,7 +1514,7 @@ function createSIWxRequestHook(options) {
1341
1514
  "SIWxStorage nonce tracking requires both hasUsedNonce and recordNonce to be implemented"
1342
1515
  );
1343
1516
  }
1344
- return async (context) => {
1517
+ return async (context, routeConfig) => {
1345
1518
  const header = context.adapter.getHeader(SIGN_IN_WITH_X) || context.adapter.getHeader(SIGN_IN_WITH_X.toLowerCase());
1346
1519
  if (!header) return;
1347
1520
  try {
@@ -1364,8 +1537,9 @@ function createSIWxRequestHook(options) {
1364
1537
  return;
1365
1538
  }
1366
1539
  }
1367
- const hasPaid = await storage.hasPaid(context.path, verification.address);
1368
- if (hasPaid) {
1540
+ const isAuthOnly = Array.isArray(routeConfig?.accepts) && routeConfig.accepts.length === 0;
1541
+ const shouldGrant = isAuthOnly || await storage.hasPaid(context.path, verification.address);
1542
+ if (shouldGrant) {
1369
1543
  if (storage.recordNonce) {
1370
1544
  await storage.recordNonce(payload.nonce);
1371
1545
  }
@@ -1412,6 +1586,785 @@ function createSIWxClientHook(signer) {
1412
1586
  };
1413
1587
  }
1414
1588
 
1589
+ // src/offer-receipt/types.ts
1590
+ var OFFER_RECEIPT = "offer-receipt";
1591
+ function isJWSSignedOffer(offer) {
1592
+ return offer.format === "jws";
1593
+ }
1594
+ function isEIP712SignedOffer(offer) {
1595
+ return offer.format === "eip712";
1596
+ }
1597
+ function isJWSSignedReceipt(receipt) {
1598
+ return receipt.format === "jws";
1599
+ }
1600
+ function isEIP712SignedReceipt(receipt) {
1601
+ return receipt.format === "eip712";
1602
+ }
1603
+ function isJWSSigner(signer) {
1604
+ return signer.format === "jws";
1605
+ }
1606
+ function isEIP712Signer(signer) {
1607
+ return signer.format === "eip712";
1608
+ }
1609
+
1610
+ // src/offer-receipt/signing.ts
1611
+ var jose2 = __toESM(require("jose"));
1612
+ var import_viem2 = require("viem");
1613
+
1614
+ // src/offer-receipt/did.ts
1615
+ var jose = __toESM(require("jose"));
1616
+ var import_base2 = require("@scure/base");
1617
+ var import_secp256k1 = require("@noble/curves/secp256k1");
1618
+ var import_nist = require("@noble/curves/nist");
1619
+ var MULTICODEC_ED25519_PUB = 237;
1620
+ var MULTICODEC_SECP256K1_PUB = 231;
1621
+ var MULTICODEC_P256_PUB = 4608;
1622
+ async function extractPublicKeyFromKid(kid) {
1623
+ const [didPart, fragment] = kid.split("#");
1624
+ const parts = didPart.split(":");
1625
+ if (parts.length < 3 || parts[0] !== "did") {
1626
+ throw new Error(`Invalid DID format: ${kid}`);
1627
+ }
1628
+ const method = parts[1];
1629
+ const identifier = parts.slice(2).join(":");
1630
+ switch (method) {
1631
+ case "key":
1632
+ return extractKeyFromDidKey(identifier);
1633
+ case "jwk":
1634
+ return extractKeyFromDidJwk(identifier);
1635
+ case "web":
1636
+ return resolveDidWeb(identifier, fragment);
1637
+ default:
1638
+ throw new Error(
1639
+ `Unsupported DID method "${method}". Supported: did:key, did:jwk, did:web. Provide the public key directly for other methods.`
1640
+ );
1641
+ }
1642
+ }
1643
+ async function extractKeyFromDidKey(identifier) {
1644
+ if (!identifier.startsWith("z")) {
1645
+ throw new Error(`Unsupported multibase encoding. Expected 'z' (base58-btc).`);
1646
+ }
1647
+ const decoded = import_base2.base58.decode(identifier.slice(1));
1648
+ const { codec, keyBytes } = readMulticodec(decoded);
1649
+ switch (codec) {
1650
+ case MULTICODEC_ED25519_PUB:
1651
+ return importAsymmetricJWK({
1652
+ kty: "OKP",
1653
+ crv: "Ed25519",
1654
+ x: jose.base64url.encode(keyBytes)
1655
+ });
1656
+ case MULTICODEC_SECP256K1_PUB: {
1657
+ const point = import_secp256k1.secp256k1.Point.fromHex(keyBytes);
1658
+ const uncompressed = point.toBytes(false);
1659
+ return importAsymmetricJWK({
1660
+ kty: "EC",
1661
+ crv: "secp256k1",
1662
+ x: jose.base64url.encode(uncompressed.slice(1, 33)),
1663
+ y: jose.base64url.encode(uncompressed.slice(33, 65))
1664
+ });
1665
+ }
1666
+ case MULTICODEC_P256_PUB: {
1667
+ const point = import_nist.p256.Point.fromHex(keyBytes);
1668
+ const uncompressed = point.toBytes(false);
1669
+ return importAsymmetricJWK({
1670
+ kty: "EC",
1671
+ crv: "P-256",
1672
+ x: jose.base64url.encode(uncompressed.slice(1, 33)),
1673
+ y: jose.base64url.encode(uncompressed.slice(33, 65))
1674
+ });
1675
+ }
1676
+ default:
1677
+ throw new Error(
1678
+ `Unsupported key type in did:key (multicodec: 0x${codec.toString(16)}). Supported: Ed25519, secp256k1, P-256.`
1679
+ );
1680
+ }
1681
+ }
1682
+ async function extractKeyFromDidJwk(identifier) {
1683
+ const jwkJson = new TextDecoder().decode(jose.base64url.decode(identifier));
1684
+ const jwk = JSON.parse(jwkJson);
1685
+ return importAsymmetricJWK(jwk);
1686
+ }
1687
+ async function resolveDidWeb(identifier, fragment) {
1688
+ const parts = identifier.split(":");
1689
+ const domain = decodeURIComponent(parts[0]);
1690
+ const path = parts.slice(1).map(decodeURIComponent).join("/");
1691
+ const host = domain.split(":")[0];
1692
+ const scheme = host === "localhost" || host === "127.0.0.1" ? "http" : "https";
1693
+ const url = path ? `${scheme}://${domain}/${path}/did.json` : `${scheme}://${domain}/.well-known/did.json`;
1694
+ let didDocument;
1695
+ try {
1696
+ const response = await fetch(url, {
1697
+ headers: { Accept: "application/did+json, application/json" }
1698
+ });
1699
+ if (!response.ok) {
1700
+ throw new Error(`HTTP ${response.status}`);
1701
+ }
1702
+ didDocument = await response.json();
1703
+ } catch (error) {
1704
+ throw new Error(
1705
+ `Failed to resolve did:web:${identifier}: ${error instanceof Error ? error.message : error}`
1706
+ );
1707
+ }
1708
+ const fullDid = `did:web:${identifier}`;
1709
+ const keyId = fragment ? `${fullDid}#${fragment}` : void 0;
1710
+ const method = findVerificationMethod(didDocument, keyId);
1711
+ if (!method) {
1712
+ throw new Error(`No verification method found for ${keyId || fullDid}`);
1713
+ }
1714
+ if (method.publicKeyJwk) {
1715
+ return importAsymmetricJWK(method.publicKeyJwk);
1716
+ }
1717
+ if (method.publicKeyMultibase) {
1718
+ return extractKeyFromDidKey(method.publicKeyMultibase);
1719
+ }
1720
+ throw new Error(`Verification method ${method.id} has no supported key format`);
1721
+ }
1722
+ function readMulticodec(bytes) {
1723
+ let codec = 0;
1724
+ let shift = 0;
1725
+ let offset = 0;
1726
+ for (const byte of bytes) {
1727
+ codec |= (byte & 127) << shift;
1728
+ offset++;
1729
+ if ((byte & 128) === 0) break;
1730
+ shift += 7;
1731
+ }
1732
+ return { codec, keyBytes: bytes.slice(offset) };
1733
+ }
1734
+ async function importAsymmetricJWK(jwk) {
1735
+ const key = await jose.importJWK(jwk);
1736
+ if (key instanceof Uint8Array) {
1737
+ throw new Error("Symmetric keys are not supported");
1738
+ }
1739
+ return key;
1740
+ }
1741
+ function findVerificationMethod(doc, keyId) {
1742
+ const methods = doc.verificationMethod || [];
1743
+ if (keyId) {
1744
+ return methods.find((m) => m.id === keyId);
1745
+ }
1746
+ for (const ref of doc.assertionMethod || []) {
1747
+ if (typeof ref === "string") {
1748
+ const m = methods.find((m2) => m2.id === ref);
1749
+ if (m) return m;
1750
+ } else {
1751
+ return ref;
1752
+ }
1753
+ }
1754
+ for (const ref of doc.authentication || []) {
1755
+ if (typeof ref === "string") {
1756
+ const m = methods.find((m2) => m2.id === ref);
1757
+ if (m) return m;
1758
+ } else {
1759
+ return ref;
1760
+ }
1761
+ }
1762
+ return methods[0];
1763
+ }
1764
+
1765
+ // src/offer-receipt/signing.ts
1766
+ function canonicalize(value) {
1767
+ return serializeValue(value);
1768
+ }
1769
+ function serializeValue(value) {
1770
+ if (value === null) return "null";
1771
+ if (value === void 0) return "null";
1772
+ const type = typeof value;
1773
+ if (type === "boolean") return value ? "true" : "false";
1774
+ if (type === "number") return serializeNumber(value);
1775
+ if (type === "string") return serializeString(value);
1776
+ if (Array.isArray(value)) return serializeArray(value);
1777
+ if (type === "object") return serializeObject(value);
1778
+ throw new Error(`Cannot canonicalize value of type ${type}`);
1779
+ }
1780
+ function serializeNumber(num) {
1781
+ if (!Number.isFinite(num)) throw new Error("Cannot canonicalize Infinity or NaN");
1782
+ if (Object.is(num, -0)) return "0";
1783
+ return String(num);
1784
+ }
1785
+ function serializeString(str) {
1786
+ let result = '"';
1787
+ for (let i = 0; i < str.length; i++) {
1788
+ const char = str[i];
1789
+ const code = str.charCodeAt(i);
1790
+ if (code < 32) {
1791
+ result += "\\u" + code.toString(16).padStart(4, "0");
1792
+ } else if (char === '"') {
1793
+ result += '\\"';
1794
+ } else if (char === "\\") {
1795
+ result += "\\\\";
1796
+ } else {
1797
+ result += char;
1798
+ }
1799
+ }
1800
+ return result + '"';
1801
+ }
1802
+ function serializeArray(arr) {
1803
+ return "[" + arr.map(serializeValue).join(",") + "]";
1804
+ }
1805
+ function serializeObject(obj) {
1806
+ const keys = Object.keys(obj).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
1807
+ const pairs = [];
1808
+ for (const key of keys) {
1809
+ const value = obj[key];
1810
+ if (value !== void 0) {
1811
+ pairs.push(serializeString(key) + ":" + serializeValue(value));
1812
+ }
1813
+ }
1814
+ return "{" + pairs.join(",") + "}";
1815
+ }
1816
+ async function hashCanonical(obj) {
1817
+ const canonical = canonicalize(obj);
1818
+ const data = new TextEncoder().encode(canonical);
1819
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1820
+ return new Uint8Array(hashBuffer);
1821
+ }
1822
+ function getCanonicalBytes(obj) {
1823
+ return new TextEncoder().encode(canonicalize(obj));
1824
+ }
1825
+ async function createJWS(payload, signer) {
1826
+ const headerObj = { alg: signer.algorithm, kid: signer.kid };
1827
+ const headerB64 = jose2.base64url.encode(new TextEncoder().encode(JSON.stringify(headerObj)));
1828
+ const canonical = canonicalize(payload);
1829
+ const payloadB64 = jose2.base64url.encode(new TextEncoder().encode(canonical));
1830
+ const signingInput = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
1831
+ const signatureB64 = await signer.sign(signingInput);
1832
+ return `${headerB64}.${payloadB64}.${signatureB64}`;
1833
+ }
1834
+ function extractJWSHeader(jws) {
1835
+ const parts = jws.split(".");
1836
+ if (parts.length !== 3) throw new Error("Invalid JWS format");
1837
+ const headerJson = jose2.base64url.decode(parts[0]);
1838
+ return JSON.parse(new TextDecoder().decode(headerJson));
1839
+ }
1840
+ function extractJWSPayload(jws) {
1841
+ const parts = jws.split(".");
1842
+ if (parts.length !== 3) throw new Error("Invalid JWS format");
1843
+ const payloadJson = jose2.base64url.decode(parts[1]);
1844
+ return JSON.parse(new TextDecoder().decode(payloadJson));
1845
+ }
1846
+ function createOfferDomain() {
1847
+ return { name: "x402 offer", version: "1", chainId: 1 };
1848
+ }
1849
+ function createReceiptDomain() {
1850
+ return { name: "x402 receipt", version: "1", chainId: 1 };
1851
+ }
1852
+ var OFFER_TYPES = {
1853
+ Offer: [
1854
+ { name: "version", type: "uint256" },
1855
+ { name: "resourceUrl", type: "string" },
1856
+ { name: "scheme", type: "string" },
1857
+ { name: "network", type: "string" },
1858
+ { name: "asset", type: "string" },
1859
+ { name: "payTo", type: "string" },
1860
+ { name: "amount", type: "string" },
1861
+ { name: "validUntil", type: "uint256" }
1862
+ ]
1863
+ };
1864
+ var RECEIPT_TYPES = {
1865
+ Receipt: [
1866
+ { name: "version", type: "uint256" },
1867
+ { name: "network", type: "string" },
1868
+ { name: "resourceUrl", type: "string" },
1869
+ { name: "payer", type: "string" },
1870
+ { name: "issuedAt", type: "uint256" },
1871
+ { name: "transaction", type: "string" }
1872
+ ]
1873
+ };
1874
+ function prepareOfferForEIP712(payload) {
1875
+ return {
1876
+ version: BigInt(payload.version),
1877
+ resourceUrl: payload.resourceUrl,
1878
+ scheme: payload.scheme,
1879
+ network: payload.network,
1880
+ asset: payload.asset,
1881
+ payTo: payload.payTo,
1882
+ amount: payload.amount,
1883
+ validUntil: BigInt(payload.validUntil)
1884
+ };
1885
+ }
1886
+ function prepareReceiptForEIP712(payload) {
1887
+ return {
1888
+ version: BigInt(payload.version),
1889
+ network: payload.network,
1890
+ resourceUrl: payload.resourceUrl,
1891
+ payer: payload.payer,
1892
+ issuedAt: BigInt(payload.issuedAt),
1893
+ transaction: payload.transaction
1894
+ };
1895
+ }
1896
+ function hashOfferTypedData(payload) {
1897
+ return (0, import_viem2.hashTypedData)({
1898
+ domain: createOfferDomain(),
1899
+ types: OFFER_TYPES,
1900
+ primaryType: "Offer",
1901
+ message: prepareOfferForEIP712(payload)
1902
+ });
1903
+ }
1904
+ function hashReceiptTypedData(payload) {
1905
+ return (0, import_viem2.hashTypedData)({
1906
+ domain: createReceiptDomain(),
1907
+ types: RECEIPT_TYPES,
1908
+ primaryType: "Receipt",
1909
+ message: prepareReceiptForEIP712(payload)
1910
+ });
1911
+ }
1912
+ async function signOfferEIP712(payload, signTypedData) {
1913
+ return signTypedData({
1914
+ domain: createOfferDomain(),
1915
+ types: OFFER_TYPES,
1916
+ primaryType: "Offer",
1917
+ message: prepareOfferForEIP712(payload)
1918
+ });
1919
+ }
1920
+ async function signReceiptEIP712(payload, signTypedData) {
1921
+ return signTypedData({
1922
+ domain: createReceiptDomain(),
1923
+ types: RECEIPT_TYPES,
1924
+ primaryType: "Receipt",
1925
+ message: prepareReceiptForEIP712(payload)
1926
+ });
1927
+ }
1928
+ function extractEIP155ChainId(network) {
1929
+ const match = network.match(/^eip155:(\d+)$/);
1930
+ if (!match) {
1931
+ throw new Error(`Invalid network format: ${network}. Expected "eip155:<chainId>"`);
1932
+ }
1933
+ return parseInt(match[1], 10);
1934
+ }
1935
+ var V1_EVM_NETWORK_CHAIN_IDS = {
1936
+ ethereum: 1,
1937
+ sepolia: 11155111,
1938
+ abstract: 2741,
1939
+ "abstract-testnet": 11124,
1940
+ "base-sepolia": 84532,
1941
+ base: 8453,
1942
+ "avalanche-fuji": 43113,
1943
+ avalanche: 43114,
1944
+ iotex: 4689,
1945
+ sei: 1329,
1946
+ "sei-testnet": 1328,
1947
+ polygon: 137,
1948
+ "polygon-amoy": 80002,
1949
+ peaq: 3338,
1950
+ story: 1514,
1951
+ educhain: 41923,
1952
+ "skale-base-sepolia": 324705682
1953
+ };
1954
+ var V1_SOLANA_NETWORKS = {
1955
+ solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
1956
+ "solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
1957
+ "solana-testnet": "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z"
1958
+ };
1959
+ function convertNetworkStringToCAIP2(network) {
1960
+ if (network.includes(":")) return network;
1961
+ const chainId = V1_EVM_NETWORK_CHAIN_IDS[network.toLowerCase()];
1962
+ if (chainId !== void 0) {
1963
+ return `eip155:${chainId}`;
1964
+ }
1965
+ const solanaNetwork = V1_SOLANA_NETWORKS[network.toLowerCase()];
1966
+ if (solanaNetwork) {
1967
+ return solanaNetwork;
1968
+ }
1969
+ throw new Error(
1970
+ `Unknown network identifier: "${network}". Expected CAIP-2 format (e.g., "eip155:8453") or v1 name (e.g., "base", "solana").`
1971
+ );
1972
+ }
1973
+ function extractChainIdFromCAIP2(network) {
1974
+ const [namespace, reference] = network.split(":");
1975
+ if (namespace === "eip155" && reference) {
1976
+ const chainId = parseInt(reference, 10);
1977
+ return isNaN(chainId) ? void 0 : chainId;
1978
+ }
1979
+ return void 0;
1980
+ }
1981
+ var DEFAULT_MAX_TIMEOUT_SECONDS = 300;
1982
+ var EXTENSION_VERSION = 1;
1983
+ function createOfferPayload(resourceUrl, input) {
1984
+ const now = Math.floor(Date.now() / 1e3);
1985
+ const offerValiditySeconds = input.offerValiditySeconds ?? DEFAULT_MAX_TIMEOUT_SECONDS;
1986
+ return {
1987
+ version: EXTENSION_VERSION,
1988
+ resourceUrl,
1989
+ scheme: input.scheme,
1990
+ network: input.network,
1991
+ asset: input.asset,
1992
+ payTo: input.payTo,
1993
+ amount: input.amount,
1994
+ validUntil: now + offerValiditySeconds
1995
+ };
1996
+ }
1997
+ async function createOfferJWS(resourceUrl, input, signer) {
1998
+ const payload = createOfferPayload(resourceUrl, input);
1999
+ const jws = await createJWS(payload, signer);
2000
+ return {
2001
+ format: "jws",
2002
+ acceptIndex: input.acceptIndex,
2003
+ signature: jws
2004
+ };
2005
+ }
2006
+ async function createOfferEIP712(resourceUrl, input, signTypedData) {
2007
+ const payload = createOfferPayload(resourceUrl, input);
2008
+ const signature = await signOfferEIP712(payload, signTypedData);
2009
+ return {
2010
+ format: "eip712",
2011
+ acceptIndex: input.acceptIndex,
2012
+ payload,
2013
+ signature
2014
+ };
2015
+ }
2016
+ function extractOfferPayload(offer) {
2017
+ if (isJWSSignedOffer(offer)) {
2018
+ return extractJWSPayload(offer.signature);
2019
+ } else if (isEIP712SignedOffer(offer)) {
2020
+ return offer.payload;
2021
+ }
2022
+ throw new Error(`Unknown offer format: ${offer.format}`);
2023
+ }
2024
+ function createReceiptPayloadForEIP712(input) {
2025
+ return {
2026
+ version: EXTENSION_VERSION,
2027
+ network: input.network,
2028
+ resourceUrl: input.resourceUrl,
2029
+ payer: input.payer,
2030
+ issuedAt: Math.floor(Date.now() / 1e3),
2031
+ transaction: input.transaction ?? ""
2032
+ };
2033
+ }
2034
+ function createReceiptPayloadForJWS(input) {
2035
+ const payload = {
2036
+ version: EXTENSION_VERSION,
2037
+ network: input.network,
2038
+ resourceUrl: input.resourceUrl,
2039
+ payer: input.payer,
2040
+ issuedAt: Math.floor(Date.now() / 1e3)
2041
+ };
2042
+ if (input.transaction) {
2043
+ payload.transaction = input.transaction;
2044
+ }
2045
+ return payload;
2046
+ }
2047
+ async function createReceiptJWS(input, signer) {
2048
+ const payload = createReceiptPayloadForJWS(input);
2049
+ const jws = await createJWS(payload, signer);
2050
+ return { format: "jws", signature: jws };
2051
+ }
2052
+ async function createReceiptEIP712(input, signTypedData) {
2053
+ const payload = createReceiptPayloadForEIP712(input);
2054
+ const signature = await signReceiptEIP712(payload, signTypedData);
2055
+ return { format: "eip712", payload, signature };
2056
+ }
2057
+ function extractReceiptPayload(receipt) {
2058
+ if (isJWSSignedReceipt(receipt)) {
2059
+ return extractJWSPayload(receipt.signature);
2060
+ } else if (isEIP712SignedReceipt(receipt)) {
2061
+ return receipt.payload;
2062
+ }
2063
+ throw new Error(`Unknown receipt format: ${receipt.format}`);
2064
+ }
2065
+ async function verifyOfferSignatureEIP712(offer) {
2066
+ if (offer.format !== "eip712") {
2067
+ throw new Error(`Expected eip712 format, got ${offer.format}`);
2068
+ }
2069
+ if (!offer.payload || !("scheme" in offer.payload)) {
2070
+ throw new Error("Invalid offer: missing or malformed payload");
2071
+ }
2072
+ const signer = await (0, import_viem2.recoverTypedDataAddress)({
2073
+ domain: createOfferDomain(),
2074
+ types: OFFER_TYPES,
2075
+ primaryType: "Offer",
2076
+ message: prepareOfferForEIP712(offer.payload),
2077
+ signature: offer.signature
2078
+ });
2079
+ return { signer, payload: offer.payload };
2080
+ }
2081
+ async function verifyReceiptSignatureEIP712(receipt) {
2082
+ if (receipt.format !== "eip712") {
2083
+ throw new Error(`Expected eip712 format, got ${receipt.format}`);
2084
+ }
2085
+ if (!receipt.payload || !("payer" in receipt.payload)) {
2086
+ throw new Error("Invalid receipt: missing or malformed payload");
2087
+ }
2088
+ const signer = await (0, import_viem2.recoverTypedDataAddress)({
2089
+ domain: createReceiptDomain(),
2090
+ types: RECEIPT_TYPES,
2091
+ primaryType: "Receipt",
2092
+ message: prepareReceiptForEIP712(receipt.payload),
2093
+ signature: receipt.signature
2094
+ });
2095
+ return { signer, payload: receipt.payload };
2096
+ }
2097
+ async function verifyOfferSignatureJWS(offer, publicKey) {
2098
+ if (offer.format !== "jws") {
2099
+ throw new Error(`Expected jws format, got ${offer.format}`);
2100
+ }
2101
+ const key = await resolveVerificationKey(offer.signature, publicKey);
2102
+ const { payload } = await jose2.compactVerify(offer.signature, key);
2103
+ return JSON.parse(new TextDecoder().decode(payload));
2104
+ }
2105
+ async function verifyReceiptSignatureJWS(receipt, publicKey) {
2106
+ if (receipt.format !== "jws") {
2107
+ throw new Error(`Expected jws format, got ${receipt.format}`);
2108
+ }
2109
+ const key = await resolveVerificationKey(receipt.signature, publicKey);
2110
+ const { payload } = await jose2.compactVerify(receipt.signature, key);
2111
+ return JSON.parse(new TextDecoder().decode(payload));
2112
+ }
2113
+ async function resolveVerificationKey(jws, providedKey) {
2114
+ if (providedKey) {
2115
+ if ("kty" in providedKey) {
2116
+ const key = await jose2.importJWK(providedKey);
2117
+ if (key instanceof Uint8Array) {
2118
+ throw new Error("Symmetric keys are not supported for JWS verification");
2119
+ }
2120
+ return key;
2121
+ }
2122
+ return providedKey;
2123
+ }
2124
+ const header = extractJWSHeader(jws);
2125
+ if (!header.kid) {
2126
+ throw new Error("No public key provided and JWS header missing kid");
2127
+ }
2128
+ return extractPublicKeyFromKid(header.kid);
2129
+ }
2130
+
2131
+ // src/offer-receipt/server.ts
2132
+ var OFFER_SCHEMA = {
2133
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2134
+ type: "object",
2135
+ properties: {
2136
+ offers: {
2137
+ type: "array",
2138
+ items: {
2139
+ type: "object",
2140
+ properties: {
2141
+ format: { type: "string" },
2142
+ acceptIndex: { type: "integer" },
2143
+ payload: {
2144
+ type: "object",
2145
+ properties: {
2146
+ version: { type: "integer" },
2147
+ resourceUrl: { type: "string" },
2148
+ scheme: { type: "string" },
2149
+ network: { type: "string" },
2150
+ asset: { type: "string" },
2151
+ payTo: { type: "string" },
2152
+ amount: { type: "string" },
2153
+ validUntil: { type: "integer" }
2154
+ },
2155
+ required: ["version", "resourceUrl", "scheme", "network", "asset", "payTo", "amount"]
2156
+ },
2157
+ signature: { type: "string" }
2158
+ },
2159
+ required: ["format", "signature"]
2160
+ }
2161
+ }
2162
+ },
2163
+ required: ["offers"]
2164
+ };
2165
+ var RECEIPT_SCHEMA = {
2166
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2167
+ type: "object",
2168
+ properties: {
2169
+ receipt: {
2170
+ type: "object",
2171
+ properties: {
2172
+ format: { type: "string" },
2173
+ payload: {
2174
+ type: "object",
2175
+ properties: {
2176
+ version: { type: "integer" },
2177
+ network: { type: "string" },
2178
+ resourceUrl: { type: "string" },
2179
+ payer: { type: "string" },
2180
+ issuedAt: { type: "integer" },
2181
+ transaction: { type: "string" }
2182
+ },
2183
+ required: ["version", "network", "resourceUrl", "payer", "issuedAt"]
2184
+ },
2185
+ signature: { type: "string" }
2186
+ },
2187
+ required: ["format", "signature"]
2188
+ }
2189
+ },
2190
+ required: ["receipt"]
2191
+ };
2192
+ function requirementsToOfferInput(requirements, acceptIndex, offerValiditySeconds) {
2193
+ return {
2194
+ acceptIndex,
2195
+ scheme: requirements.scheme,
2196
+ network: requirements.network,
2197
+ asset: requirements.asset,
2198
+ payTo: requirements.payTo,
2199
+ amount: requirements.amount,
2200
+ offerValiditySeconds: offerValiditySeconds ?? requirements.maxTimeoutSeconds
2201
+ };
2202
+ }
2203
+ function createOfferReceiptExtension(issuer) {
2204
+ return {
2205
+ key: OFFER_RECEIPT,
2206
+ // Add signed offers to 402 PaymentRequired response
2207
+ enrichPaymentRequiredResponse: async (declaration, context) => {
2208
+ const config = declaration;
2209
+ const resourceUrl = context.paymentRequiredResponse.resource?.url || context.transportContext?.request?.adapter?.getUrl?.();
2210
+ if (!resourceUrl) {
2211
+ console.warn("[offer-receipt] No resource URL available for signing offers");
2212
+ return void 0;
2213
+ }
2214
+ const offers = [];
2215
+ for (let i = 0; i < context.requirements.length; i++) {
2216
+ const requirement = context.requirements[i];
2217
+ try {
2218
+ const offerInput = requirementsToOfferInput(requirement, i, config?.offerValiditySeconds);
2219
+ const signedOffer = await issuer.issueOffer(resourceUrl, offerInput);
2220
+ offers.push(signedOffer);
2221
+ } catch (error) {
2222
+ console.error(`[offer-receipt] Failed to sign offer for requirement ${i}:`, error);
2223
+ }
2224
+ }
2225
+ if (offers.length === 0) {
2226
+ return void 0;
2227
+ }
2228
+ return {
2229
+ info: {
2230
+ offers
2231
+ },
2232
+ schema: OFFER_SCHEMA
2233
+ };
2234
+ },
2235
+ // Add signed receipt to settlement response
2236
+ enrichSettlementResponse: async (declaration, context) => {
2237
+ const config = declaration;
2238
+ if (!context.result.success) {
2239
+ return void 0;
2240
+ }
2241
+ const payer = context.result.payer;
2242
+ if (!payer) {
2243
+ console.warn("[offer-receipt] No payer available for signing receipt");
2244
+ return void 0;
2245
+ }
2246
+ const network = context.result.network;
2247
+ if (!network) {
2248
+ console.warn("[offer-receipt] No network available for signing receipt");
2249
+ return void 0;
2250
+ }
2251
+ const transaction = context.result.transaction;
2252
+ const resourceUrl = context.transportContext?.request?.adapter?.getUrl?.();
2253
+ if (!resourceUrl) {
2254
+ console.warn("[offer-receipt] No resource URL available for signing receipt");
2255
+ return void 0;
2256
+ }
2257
+ const includeTxHash = config?.includeTxHash === true;
2258
+ try {
2259
+ const signedReceipt = await issuer.issueReceipt(
2260
+ resourceUrl,
2261
+ payer,
2262
+ network,
2263
+ includeTxHash ? transaction || void 0 : void 0
2264
+ );
2265
+ return {
2266
+ info: {
2267
+ receipt: signedReceipt
2268
+ },
2269
+ schema: RECEIPT_SCHEMA
2270
+ };
2271
+ } catch (error) {
2272
+ console.error("[offer-receipt] Failed to sign receipt:", error);
2273
+ return void 0;
2274
+ }
2275
+ }
2276
+ };
2277
+ }
2278
+ function declareOfferReceiptExtension(config) {
2279
+ return {
2280
+ [OFFER_RECEIPT]: {
2281
+ includeTxHash: config?.includeTxHash,
2282
+ offerValiditySeconds: config?.offerValiditySeconds
2283
+ }
2284
+ };
2285
+ }
2286
+ function createJWSOfferReceiptIssuer(kid, jwsSigner) {
2287
+ return {
2288
+ kid,
2289
+ format: "jws",
2290
+ async issueOffer(resourceUrl, input) {
2291
+ return createOfferJWS(resourceUrl, input, jwsSigner);
2292
+ },
2293
+ async issueReceipt(resourceUrl, payer, network, transaction) {
2294
+ return createReceiptJWS({ resourceUrl, payer, network, transaction }, jwsSigner);
2295
+ }
2296
+ };
2297
+ }
2298
+ function createEIP712OfferReceiptIssuer(kid, signTypedData) {
2299
+ return {
2300
+ kid,
2301
+ format: "eip712",
2302
+ async issueOffer(resourceUrl, input) {
2303
+ return createOfferEIP712(resourceUrl, input, signTypedData);
2304
+ },
2305
+ async issueReceipt(resourceUrl, payer, network, transaction) {
2306
+ return createReceiptEIP712({ resourceUrl, payer, network, transaction }, signTypedData);
2307
+ }
2308
+ };
2309
+ }
2310
+
2311
+ // src/offer-receipt/client.ts
2312
+ var import_http2 = require("@x402/core/http");
2313
+ function verifyReceiptMatchesOffer(receipt, offer, payerAddresses, maxAgeSeconds = 3600) {
2314
+ const payload = extractReceiptPayload(receipt);
2315
+ const resourceUrlMatch = payload.resourceUrl === offer.resourceUrl;
2316
+ const networkMatch = payload.network === offer.network;
2317
+ const payerMatch = payerAddresses.some(
2318
+ (addr) => payload.payer.toLowerCase() === addr.toLowerCase()
2319
+ );
2320
+ const issuedRecently = Math.floor(Date.now() / 1e3) - payload.issuedAt < maxAgeSeconds;
2321
+ return resourceUrlMatch && networkMatch && payerMatch && issuedRecently;
2322
+ }
2323
+ function extractOffersFromPaymentRequired(paymentRequired) {
2324
+ const extData = paymentRequired.extensions?.[OFFER_RECEIPT];
2325
+ return extData?.info?.offers ?? [];
2326
+ }
2327
+ function decodeSignedOffers(offers) {
2328
+ return offers.map((offer) => {
2329
+ const payload = extractOfferPayload(offer);
2330
+ return {
2331
+ // Spread payload fields at top level
2332
+ ...payload,
2333
+ // Include metadata
2334
+ signedOffer: offer,
2335
+ format: offer.format,
2336
+ acceptIndex: offer.acceptIndex
2337
+ };
2338
+ });
2339
+ }
2340
+ function findAcceptsObjectFromSignedOffer(offer, accepts) {
2341
+ const isDecoded = "signedOffer" in offer;
2342
+ const payload = isDecoded ? offer : extractOfferPayload(offer);
2343
+ const acceptIndex = isDecoded ? offer.acceptIndex : offer.acceptIndex;
2344
+ if (acceptIndex !== void 0 && acceptIndex < accepts.length) {
2345
+ const hinted = accepts[acceptIndex];
2346
+ if (hinted.network === payload.network && hinted.scheme === payload.scheme && hinted.asset === payload.asset && hinted.payTo === payload.payTo && hinted.amount === payload.amount) {
2347
+ return hinted;
2348
+ }
2349
+ }
2350
+ return accepts.find(
2351
+ (req) => req.network === payload.network && req.scheme === payload.scheme && req.asset === payload.asset && req.payTo === payload.payTo && req.amount === payload.amount
2352
+ );
2353
+ }
2354
+ function extractReceiptFromResponse(response) {
2355
+ const paymentResponseHeader = response.headers.get("PAYMENT-RESPONSE") || response.headers.get("X-PAYMENT-RESPONSE");
2356
+ if (!paymentResponseHeader) {
2357
+ return void 0;
2358
+ }
2359
+ try {
2360
+ const settlementResponse = (0, import_http2.decodePaymentResponseHeader)(paymentResponseHeader);
2361
+ const receiptExtData = settlementResponse.extensions?.[OFFER_RECEIPT];
2362
+ return receiptExtData?.info?.receipt;
2363
+ } catch {
2364
+ return void 0;
2365
+ }
2366
+ }
2367
+
1415
2368
  // src/payment-identifier/types.ts
1416
2369
  var PAYMENT_IDENTIFIER = "payment-identifier";
1417
2370
  var PAYMENT_ID_MIN_LENGTH = 16;
@@ -1717,14 +2670,8 @@ var ERC20_APPROVAL_GAS_SPONSORING = {
1717
2670
  key: "erc20ApprovalGasSponsoring"
1718
2671
  };
1719
2672
  var ERC20_APPROVAL_GAS_SPONSORING_VERSION = "1";
1720
- function createErc20ApprovalGasSponsoringExtension(signer, client) {
1721
- return {
1722
- ...ERC20_APPROVAL_GAS_SPONSORING,
1723
- signer: {
1724
- ...signer,
1725
- sendRawTransaction: client.sendRawTransaction.bind(client)
1726
- }
1727
- };
2673
+ function createErc20ApprovalGasSponsoringExtension(signer, signerForNetwork) {
2674
+ return { ...ERC20_APPROVAL_GAS_SPONSORING, signer, signerForNetwork };
1728
2675
  }
1729
2676
 
1730
2677
  // src/erc20-approval-gas-sponsoring/resourceService.ts
@@ -1806,10 +2753,13 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
1806
2753
  ERC20_APPROVAL_GAS_SPONSORING,
1807
2754
  ERC20_APPROVAL_GAS_SPONSORING_VERSION,
1808
2755
  InMemorySIWxStorage,
2756
+ OFFER_RECEIPT,
2757
+ OFFER_TYPES,
1809
2758
  PAYMENT_IDENTIFIER,
1810
2759
  PAYMENT_ID_MAX_LENGTH,
1811
2760
  PAYMENT_ID_MIN_LENGTH,
1812
2761
  PAYMENT_ID_PATTERN,
2762
+ RECEIPT_TYPES,
1813
2763
  SIGN_IN_WITH_X,
1814
2764
  SIWxPayloadSchema,
1815
2765
  SOLANA_DEVNET,
@@ -1818,7 +2768,19 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
1818
2768
  appendPaymentIdentifierToExtensions,
1819
2769
  bazaarResourceServerExtension,
1820
2770
  buildSIWxSchema,
2771
+ canonicalize,
2772
+ convertNetworkStringToCAIP2,
2773
+ createEIP712OfferReceiptIssuer,
1821
2774
  createErc20ApprovalGasSponsoringExtension,
2775
+ createJWS,
2776
+ createJWSOfferReceiptIssuer,
2777
+ createOfferDomain,
2778
+ createOfferEIP712,
2779
+ createOfferJWS,
2780
+ createOfferReceiptExtension,
2781
+ createReceiptDomain,
2782
+ createReceiptEIP712,
2783
+ createReceiptJWS,
1822
2784
  createSIWxClientHook,
1823
2785
  createSIWxMessage,
1824
2786
  createSIWxPayload,
@@ -1827,41 +2789,68 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
1827
2789
  declareDiscoveryExtension,
1828
2790
  declareEip2612GasSponsoringExtension,
1829
2791
  declareErc20ApprovalGasSponsoringExtension,
2792
+ declareOfferReceiptExtension,
1830
2793
  declarePaymentIdentifierExtension,
1831
2794
  declareSIWxExtension,
1832
2795
  decodeBase58,
2796
+ decodeSignedOffers,
1833
2797
  encodeBase58,
1834
2798
  encodeSIWxHeader,
1835
2799
  erc20ApprovalGasSponsoringSchema,
1836
2800
  extractAndValidatePaymentIdentifier,
2801
+ extractChainIdFromCAIP2,
1837
2802
  extractDiscoveryInfo,
1838
2803
  extractDiscoveryInfoFromExtension,
1839
2804
  extractDiscoveryInfoV1,
2805
+ extractEIP155ChainId,
1840
2806
  extractEVMChainId,
1841
2807
  extractEip2612GasSponsoringInfo,
1842
2808
  extractErc20ApprovalGasSponsoringInfo,
2809
+ extractJWSHeader,
2810
+ extractJWSPayload,
2811
+ extractOfferPayload,
2812
+ extractOffersFromPaymentRequired,
1843
2813
  extractPaymentIdentifier,
2814
+ extractPublicKeyFromKid,
2815
+ extractReceiptFromResponse,
2816
+ extractReceiptPayload,
1844
2817
  extractResourceMetadataV1,
1845
2818
  extractSolanaChainReference,
2819
+ findAcceptsObjectFromSignedOffer,
1846
2820
  formatSIWEMessage,
1847
2821
  formatSIWSMessage,
1848
2822
  generatePaymentId,
2823
+ getCanonicalBytes,
1849
2824
  getEVMAddress,
1850
2825
  getSolanaAddress,
1851
2826
  hasPaymentIdentifier,
2827
+ hashCanonical,
2828
+ hashOfferTypedData,
2829
+ hashReceiptTypedData,
1852
2830
  isBodyExtensionConfig,
1853
2831
  isDiscoverableV1,
2832
+ isEIP712SignedOffer,
2833
+ isEIP712SignedReceipt,
2834
+ isEIP712Signer,
1854
2835
  isEVMSigner,
2836
+ isJWSSignedOffer,
2837
+ isJWSSignedReceipt,
2838
+ isJWSSigner,
1855
2839
  isMcpExtensionConfig,
1856
2840
  isPaymentIdentifierExtension,
1857
2841
  isPaymentIdentifierRequired,
1858
2842
  isQueryExtensionConfig,
1859
2843
  isSolanaSigner,
1860
2844
  isValidPaymentId,
2845
+ isValidRouteTemplate,
1861
2846
  parseSIWxHeader,
1862
2847
  paymentIdentifierResourceServerExtension,
1863
2848
  paymentIdentifierSchema,
2849
+ prepareOfferForEIP712,
2850
+ prepareReceiptForEIP712,
1864
2851
  signEVMMessage,
2852
+ signOfferEIP712,
2853
+ signReceiptEIP712,
1865
2854
  signSolanaMessage,
1866
2855
  siwxResourceServerExtension,
1867
2856
  validateAndExtract,
@@ -1870,8 +2859,14 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
1870
2859
  validateErc20ApprovalGasSponsoringInfo,
1871
2860
  validatePaymentIdentifier,
1872
2861
  validatePaymentIdentifierRequirement,
2862
+ validateRouteTemplate,
1873
2863
  validateSIWxMessage,
1874
2864
  verifyEVMSignature,
2865
+ verifyOfferSignatureEIP712,
2866
+ verifyOfferSignatureJWS,
2867
+ verifyReceiptMatchesOffer,
2868
+ verifyReceiptSignatureEIP712,
2869
+ verifyReceiptSignatureJWS,
1875
2870
  verifySIWxSignature,
1876
2871
  verifySolanaSignature,
1877
2872
  withBazaar,