x402check 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1857 @@
1
+
2
+ //#region src/types/errors.ts
3
+ /**
4
+ * Error and warning code vocabulary for x402check
5
+ */
6
+ const ErrorCode = {
7
+ INVALID_JSON: "INVALID_JSON",
8
+ NOT_OBJECT: "NOT_OBJECT",
9
+ UNKNOWN_FORMAT: "UNKNOWN_FORMAT",
10
+ MISSING_VERSION: "MISSING_VERSION",
11
+ INVALID_VERSION: "INVALID_VERSION",
12
+ MISSING_ACCEPTS: "MISSING_ACCEPTS",
13
+ EMPTY_ACCEPTS: "EMPTY_ACCEPTS",
14
+ INVALID_ACCEPTS: "INVALID_ACCEPTS",
15
+ MISSING_SCHEME: "MISSING_SCHEME",
16
+ MISSING_NETWORK: "MISSING_NETWORK",
17
+ INVALID_NETWORK_FORMAT: "INVALID_NETWORK_FORMAT",
18
+ MISSING_AMOUNT: "MISSING_AMOUNT",
19
+ INVALID_AMOUNT: "INVALID_AMOUNT",
20
+ ZERO_AMOUNT: "ZERO_AMOUNT",
21
+ MISSING_ASSET: "MISSING_ASSET",
22
+ MISSING_PAY_TO: "MISSING_PAY_TO",
23
+ MISSING_RESOURCE: "MISSING_RESOURCE",
24
+ INVALID_URL: "INVALID_URL",
25
+ INVALID_TIMEOUT: "INVALID_TIMEOUT",
26
+ INVALID_EVM_ADDRESS: "INVALID_EVM_ADDRESS",
27
+ BAD_EVM_CHECKSUM: "BAD_EVM_CHECKSUM",
28
+ NO_EVM_CHECKSUM: "NO_EVM_CHECKSUM",
29
+ INVALID_SOLANA_ADDRESS: "INVALID_SOLANA_ADDRESS",
30
+ ADDRESS_NETWORK_MISMATCH: "ADDRESS_NETWORK_MISMATCH",
31
+ UNKNOWN_NETWORK: "UNKNOWN_NETWORK",
32
+ UNKNOWN_ASSET: "UNKNOWN_ASSET",
33
+ LEGACY_FORMAT: "LEGACY_FORMAT",
34
+ MISSING_MAX_TIMEOUT: "MISSING_MAX_TIMEOUT"
35
+ };
36
+ /**
37
+ * Human-readable error messages for all error codes
38
+ */
39
+ const ErrorMessages = {
40
+ INVALID_JSON: "Input is not valid JSON",
41
+ NOT_OBJECT: "Input must be an object",
42
+ UNKNOWN_FORMAT: "Missing required x402Version field (must be 1 or 2)",
43
+ MISSING_VERSION: "Missing required field: x402Version",
44
+ INVALID_VERSION: "Invalid x402Version value (must be 1 or 2)",
45
+ MISSING_ACCEPTS: "Missing required field: accepts",
46
+ EMPTY_ACCEPTS: "accepts array cannot be empty",
47
+ INVALID_ACCEPTS: "accepts must be an array",
48
+ MISSING_SCHEME: "Missing required field: scheme",
49
+ MISSING_NETWORK: "Missing required field: network",
50
+ INVALID_NETWORK_FORMAT: "Network must use CAIP-2 format (namespace:reference), e.g. eip155:8453",
51
+ MISSING_AMOUNT: "Missing required field: amount",
52
+ INVALID_AMOUNT: "Amount must be a numeric string in atomic units",
53
+ ZERO_AMOUNT: "Amount must be greater than zero",
54
+ MISSING_ASSET: "Missing required field: asset",
55
+ MISSING_PAY_TO: "Missing required field: payTo",
56
+ MISSING_RESOURCE: "Missing required field: resource",
57
+ INVALID_URL: "resource.url is not a valid URL format",
58
+ INVALID_TIMEOUT: "maxTimeoutSeconds must be a positive integer",
59
+ INVALID_EVM_ADDRESS: "Invalid EVM address format",
60
+ BAD_EVM_CHECKSUM: "EVM address has invalid checksum",
61
+ NO_EVM_CHECKSUM: "EVM address is all-lowercase with no checksum protection",
62
+ INVALID_SOLANA_ADDRESS: "Invalid Solana address format",
63
+ ADDRESS_NETWORK_MISMATCH: "Address format does not match network type",
64
+ UNKNOWN_NETWORK: "Network is not in the known registry -- config may still work but cannot be fully validated",
65
+ UNKNOWN_ASSET: "Asset is not in the known registry -- config may still work but cannot be fully validated",
66
+ LEGACY_FORMAT: "Config uses legacy flat format -- consider upgrading to x402 v2",
67
+ MISSING_MAX_TIMEOUT: "Consider adding maxTimeoutSeconds for better security"
68
+ };
69
+
70
+ //#endregion
71
+ //#region src/types/parse-input.ts
72
+ /**
73
+ * Parse input that may be either a JSON string or an object
74
+ * API-04: Accept string | object
75
+ */
76
+ function parseInput(input) {
77
+ if (typeof input === "string") try {
78
+ return { parsed: JSON.parse(input) };
79
+ } catch {
80
+ return {
81
+ parsed: null,
82
+ error: {
83
+ code: ErrorCode.INVALID_JSON,
84
+ field: "$",
85
+ message: ErrorMessages.INVALID_JSON,
86
+ severity: "error"
87
+ }
88
+ };
89
+ }
90
+ return { parsed: input };
91
+ }
92
+
93
+ //#endregion
94
+ //#region src/registries/networks.ts
95
+ const CAIP2_REGEX = /^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/;
96
+ const KNOWN_NETWORKS = {
97
+ "eip155:8453": {
98
+ name: "Base",
99
+ type: "evm",
100
+ testnet: false
101
+ },
102
+ "eip155:84532": {
103
+ name: "Base Sepolia",
104
+ type: "evm",
105
+ testnet: true
106
+ },
107
+ "eip155:43114": {
108
+ name: "Avalanche C-Chain",
109
+ type: "evm",
110
+ testnet: false
111
+ },
112
+ "eip155:43113": {
113
+ name: "Avalanche Fuji",
114
+ type: "evm",
115
+ testnet: true
116
+ },
117
+ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": {
118
+ name: "Solana Mainnet",
119
+ type: "solana",
120
+ testnet: false
121
+ },
122
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": {
123
+ name: "Solana Devnet",
124
+ type: "solana",
125
+ testnet: true
126
+ },
127
+ "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": {
128
+ name: "Solana Testnet",
129
+ type: "solana",
130
+ testnet: true
131
+ },
132
+ "stellar:pubnet": {
133
+ name: "Stellar Mainnet",
134
+ type: "stellar",
135
+ testnet: false
136
+ },
137
+ "stellar:testnet": {
138
+ name: "Stellar Testnet",
139
+ type: "stellar",
140
+ testnet: true
141
+ },
142
+ "aptos:1": {
143
+ name: "Aptos Mainnet",
144
+ type: "aptos",
145
+ testnet: false
146
+ },
147
+ "aptos:2": {
148
+ name: "Aptos Testnet",
149
+ type: "aptos",
150
+ testnet: true
151
+ }
152
+ };
153
+ function isValidCaip2(value) {
154
+ return CAIP2_REGEX.test(value);
155
+ }
156
+ function isKnownNetwork(caip2) {
157
+ return caip2 in KNOWN_NETWORKS;
158
+ }
159
+ function getNetworkInfo(caip2) {
160
+ return KNOWN_NETWORKS[caip2];
161
+ }
162
+ function getNetworkNamespace(caip2) {
163
+ if (!isValidCaip2(caip2)) return;
164
+ const colonIndex = caip2.indexOf(":");
165
+ return colonIndex > 0 ? caip2.substring(0, colonIndex) : void 0;
166
+ }
167
+
168
+ //#endregion
169
+ //#region src/registries/assets.ts
170
+ const KNOWN_ASSETS = {
171
+ "eip155:8453": { "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": {
172
+ symbol: "USDC",
173
+ name: "USD Coin",
174
+ decimals: 6
175
+ } },
176
+ "eip155:84532": { "0x036cbd53842c5426634e7929541ec2318f3dcf7e": {
177
+ symbol: "USDC",
178
+ name: "USD Coin",
179
+ decimals: 6
180
+ } },
181
+ "eip155:43114": { "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e": {
182
+ symbol: "USDC",
183
+ name: "USD Coin",
184
+ decimals: 6
185
+ } },
186
+ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": { EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: {
187
+ symbol: "USDC",
188
+ name: "USD Coin",
189
+ decimals: 6
190
+ } }
191
+ };
192
+ function isKnownAsset(network, address) {
193
+ const networkAssets = KNOWN_ASSETS[network];
194
+ if (!networkAssets) return false;
195
+ return (getNetworkNamespace(network) === "eip155" ? address.toLowerCase() : address) in networkAssets;
196
+ }
197
+ function getAssetInfo(network, address) {
198
+ const networkAssets = KNOWN_ASSETS[network];
199
+ if (!networkAssets) return;
200
+ return networkAssets[getNetworkNamespace(network) === "eip155" ? address.toLowerCase() : address];
201
+ }
202
+
203
+ //#endregion
204
+ //#region src/registries/simple-names.ts
205
+ const SIMPLE_NAME_TO_CAIP2 = {
206
+ base: "eip155:8453",
207
+ "base-sepolia": "eip155:84532",
208
+ base_sepolia: "eip155:84532",
209
+ avalanche: "eip155:43114",
210
+ "avalanche-fuji": "eip155:43113",
211
+ solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
212
+ "solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
213
+ "solana-testnet": "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z",
214
+ stellar: "stellar:pubnet",
215
+ "stellar-testnet": "stellar:testnet",
216
+ aptos: "aptos:1"
217
+ };
218
+ function getCanonicalNetwork(name) {
219
+ return SIMPLE_NAME_TO_CAIP2[name.toLowerCase()];
220
+ }
221
+
222
+ //#endregion
223
+ //#region src/detection/guards.ts
224
+ /**
225
+ * Check if value is a non-null, non-array object
226
+ */
227
+ function isRecord(value) {
228
+ return typeof value === "object" && value !== null && !Array.isArray(value);
229
+ }
230
+ /**
231
+ * Check if config has an accepts array
232
+ */
233
+ function hasAcceptsArray(config) {
234
+ return "accepts" in config && Array.isArray(config.accepts);
235
+ }
236
+ /**
237
+ * Type guard for v2 config
238
+ * Checks for accepts array + x402Version: 2
239
+ * Note: resource is required by spec but its absence is a validation error, not a detection failure
240
+ */
241
+ function isV2Config(value) {
242
+ if (!isRecord(value)) return false;
243
+ if (!hasAcceptsArray(value)) return false;
244
+ return "x402Version" in value && value.x402Version === 2;
245
+ }
246
+ /**
247
+ * Type guard for v1 config
248
+ * Checks for accepts array + x402Version: 1
249
+ */
250
+ function isV1Config(value) {
251
+ if (!isRecord(value)) return false;
252
+ if (!hasAcceptsArray(value)) return false;
253
+ return "x402Version" in value && value.x402Version === 1;
254
+ }
255
+
256
+ //#endregion
257
+ //#region src/detection/detect.ts
258
+ /**
259
+ * Detect the format of an x402 config
260
+ *
261
+ * @param input - JSON string or parsed object
262
+ * @returns ConfigFormat literal: 'v2' | 'v1' | 'unknown'
263
+ *
264
+ * Detection requires x402Version field:
265
+ * 1. v2: accepts array + x402Version: 2
266
+ * 2. v1: accepts array + x402Version: 1
267
+ * 3. unknown: anything else (including versionless configs)
268
+ */
269
+ function detect(input) {
270
+ const { parsed, error } = parseInput(input);
271
+ if (error) return "unknown";
272
+ if (isV2Config(parsed)) return "v2";
273
+ if (isV1Config(parsed)) return "v1";
274
+ return "unknown";
275
+ }
276
+
277
+ //#endregion
278
+ //#region src/detection/normalize.ts
279
+ /**
280
+ * Normalize any x402 config format to canonical v2 shape
281
+ *
282
+ * @param input - JSON string or parsed object
283
+ * @returns NormalizedConfig or null if format is unknown/invalid
284
+ *
285
+ * Normalization rules:
286
+ * - v2: Pass through with new object (FMT-07)
287
+ * - v1: Map maxAmountRequired → amount, lift per-entry resource (FMT-06)
288
+ * - unknown: Return null
289
+ *
290
+ * All transformations preserve extensions and extra fields (FMT-08)
291
+ */
292
+ function normalize(input) {
293
+ const { parsed, error } = parseInput(input);
294
+ if (error) return null;
295
+ const format = detect(parsed);
296
+ switch (format) {
297
+ case "v2": return normalizeV2(parsed);
298
+ case "v1": return normalizeV1ToV2(parsed);
299
+ case "unknown": return null;
300
+ default: return format;
301
+ }
302
+ }
303
+ /**
304
+ * Normalize v2 config (pass-through with new object)
305
+ * FMT-07: v2 configs are already canonical, just create new object
306
+ */
307
+ function normalizeV2(config) {
308
+ const result = {
309
+ x402Version: 2,
310
+ accepts: [...config.accepts],
311
+ resource: config.resource
312
+ };
313
+ if (config.error !== void 0) result.error = config.error;
314
+ if (config.extensions !== void 0) result.extensions = config.extensions;
315
+ return result;
316
+ }
317
+ /**
318
+ * Normalize v1 config to v2
319
+ * FMT-06: Map maxAmountRequired → amount, lift per-entry resource to top level
320
+ */
321
+ function normalizeV1ToV2(config) {
322
+ let topLevelResource = void 0;
323
+ const result = {
324
+ x402Version: 2,
325
+ accepts: config.accepts.map((entry) => {
326
+ if (entry.resource && !topLevelResource) topLevelResource = entry.resource;
327
+ const mapped = {
328
+ scheme: entry.scheme,
329
+ network: entry.network,
330
+ amount: entry.maxAmountRequired,
331
+ asset: entry.asset,
332
+ payTo: entry.payTo
333
+ };
334
+ if (entry.maxTimeoutSeconds !== void 0) mapped.maxTimeoutSeconds = entry.maxTimeoutSeconds;
335
+ if (entry.extra !== void 0) mapped.extra = entry.extra;
336
+ return mapped;
337
+ })
338
+ };
339
+ if (topLevelResource !== void 0) result.resource = topLevelResource;
340
+ if (config.error !== void 0) result.error = config.error;
341
+ if (config.extensions !== void 0) result.extensions = config.extensions;
342
+ return result;
343
+ }
344
+
345
+ //#endregion
346
+ //#region ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/_u64.js
347
+ /**
348
+ * Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array.
349
+ * @todo re-check https://issues.chromium.org/issues/42212588
350
+ * @module
351
+ */
352
+ const U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
353
+ const _32n = /* @__PURE__ */ BigInt(32);
354
+ function fromBig(n, le = false) {
355
+ if (le) return {
356
+ h: Number(n & U32_MASK64),
357
+ l: Number(n >> _32n & U32_MASK64)
358
+ };
359
+ return {
360
+ h: Number(n >> _32n & U32_MASK64) | 0,
361
+ l: Number(n & U32_MASK64) | 0
362
+ };
363
+ }
364
+ function split(lst, le = false) {
365
+ const len = lst.length;
366
+ let Ah = new Uint32Array(len);
367
+ let Al = new Uint32Array(len);
368
+ for (let i = 0; i < len; i++) {
369
+ const { h, l } = fromBig(lst[i], le);
370
+ [Ah[i], Al[i]] = [h, l];
371
+ }
372
+ return [Ah, Al];
373
+ }
374
+ const rotlSH = (h, l, s) => h << s | l >>> 32 - s;
375
+ const rotlSL = (h, l, s) => l << s | h >>> 32 - s;
376
+ const rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
377
+ const rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
378
+
379
+ //#endregion
380
+ //#region ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/utils.js
381
+ /**
382
+ * Utilities for hex, bytes, CSPRNG.
383
+ * @module
384
+ */
385
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
386
+ /** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
387
+ function isBytes$1(a) {
388
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
389
+ }
390
+ /** Asserts something is positive integer. */
391
+ function anumber$1(n, title = "") {
392
+ if (!Number.isSafeInteger(n) || n < 0) {
393
+ const prefix = title && `"${title}" `;
394
+ throw new Error(`${prefix}expected integer >= 0, got ${n}`);
395
+ }
396
+ }
397
+ /** Asserts something is Uint8Array. */
398
+ function abytes$1(value, length, title = "") {
399
+ const bytes = isBytes$1(value);
400
+ const len = value?.length;
401
+ const needsLen = length !== void 0;
402
+ if (!bytes || needsLen && len !== length) {
403
+ const prefix = title && `"${title}" `;
404
+ const ofLen = needsLen ? ` of length ${length}` : "";
405
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
406
+ throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
407
+ }
408
+ return value;
409
+ }
410
+ /** Asserts a hash instance has not been destroyed / finished */
411
+ function aexists(instance, checkFinished = true) {
412
+ if (instance.destroyed) throw new Error("Hash instance has been destroyed");
413
+ if (checkFinished && instance.finished) throw new Error("Hash#digest() has already been called");
414
+ }
415
+ /** Asserts output is properly-sized byte array */
416
+ function aoutput(out, instance) {
417
+ abytes$1(out, void 0, "digestInto() output");
418
+ const min = instance.outputLen;
419
+ if (out.length < min) throw new Error("\"digestInto() output\" expected to be of length >=" + min);
420
+ }
421
+ /** Cast u8 / u16 / u32 to u32. */
422
+ function u32(arr) {
423
+ return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
424
+ }
425
+ /** Zeroize a byte array. Warning: JS provides no guarantees. */
426
+ function clean(...arrays) {
427
+ for (let i = 0; i < arrays.length; i++) arrays[i].fill(0);
428
+ }
429
+ /** Is current platform little-endian? Most are. Big-Endian platform: IBM */
430
+ const isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
431
+ /** The byte swap operation for uint32 */
432
+ function byteSwap(word) {
433
+ return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
434
+ }
435
+ /** In place byte swap for Uint32Array */
436
+ function byteSwap32(arr) {
437
+ for (let i = 0; i < arr.length; i++) arr[i] = byteSwap(arr[i]);
438
+ return arr;
439
+ }
440
+ const swap32IfBE = isLE ? (u) => u : byteSwap32;
441
+ const hasHexBuiltin$1 = typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function";
442
+ /** Creates function with outputLen, blockLen, create properties from a class constructor. */
443
+ function createHasher(hashCons, info = {}) {
444
+ const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
445
+ const tmp = hashCons(void 0);
446
+ hashC.outputLen = tmp.outputLen;
447
+ hashC.blockLen = tmp.blockLen;
448
+ hashC.create = (opts) => hashCons(opts);
449
+ Object.assign(hashC, info);
450
+ return Object.freeze(hashC);
451
+ }
452
+
453
+ //#endregion
454
+ //#region ../../node_modules/.pnpm/@noble+hashes@2.0.1/node_modules/@noble/hashes/sha3.js
455
+ /**
456
+ * SHA3 (keccak) hash function, based on a new "Sponge function" design.
457
+ * Different from older hashes, the internal state is bigger than output size.
458
+ *
459
+ * Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
460
+ * [Website](https://keccak.team/keccak.html),
461
+ * [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub).
462
+ *
463
+ * Check out `sha3-addons` module for cSHAKE, k12, and others.
464
+ * @module
465
+ */
466
+ const _0n = BigInt(0);
467
+ const _1n = BigInt(1);
468
+ const _2n = BigInt(2);
469
+ const _7n = BigInt(7);
470
+ const _256n = BigInt(256);
471
+ const _0x71n = BigInt(113);
472
+ const SHA3_PI = [];
473
+ const SHA3_ROTL = [];
474
+ const _SHA3_IOTA = [];
475
+ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
476
+ [x, y] = [y, (2 * x + 3 * y) % 5];
477
+ SHA3_PI.push(2 * (5 * y + x));
478
+ SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
479
+ let t = _0n;
480
+ for (let j = 0; j < 7; j++) {
481
+ R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
482
+ if (R & _2n) t ^= _1n << (_1n << BigInt(j)) - _1n;
483
+ }
484
+ _SHA3_IOTA.push(t);
485
+ }
486
+ const IOTAS = split(_SHA3_IOTA, true);
487
+ const SHA3_IOTA_H = IOTAS[0];
488
+ const SHA3_IOTA_L = IOTAS[1];
489
+ const rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
490
+ const rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
491
+ /** `keccakf1600` internal function, additionally allows to adjust round count. */
492
+ function keccakP(s, rounds = 24) {
493
+ const B = new Uint32Array(10);
494
+ for (let round = 24 - rounds; round < 24; round++) {
495
+ for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
496
+ for (let x = 0; x < 10; x += 2) {
497
+ const idx1 = (x + 8) % 10;
498
+ const idx0 = (x + 2) % 10;
499
+ const B0 = B[idx0];
500
+ const B1 = B[idx0 + 1];
501
+ const Th = rotlH(B0, B1, 1) ^ B[idx1];
502
+ const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
503
+ for (let y = 0; y < 50; y += 10) {
504
+ s[x + y] ^= Th;
505
+ s[x + y + 1] ^= Tl;
506
+ }
507
+ }
508
+ let curH = s[2];
509
+ let curL = s[3];
510
+ for (let t = 0; t < 24; t++) {
511
+ const shift = SHA3_ROTL[t];
512
+ const Th = rotlH(curH, curL, shift);
513
+ const Tl = rotlL(curH, curL, shift);
514
+ const PI = SHA3_PI[t];
515
+ curH = s[PI];
516
+ curL = s[PI + 1];
517
+ s[PI] = Th;
518
+ s[PI + 1] = Tl;
519
+ }
520
+ for (let y = 0; y < 50; y += 10) {
521
+ for (let x = 0; x < 10; x++) B[x] = s[y + x];
522
+ for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
523
+ }
524
+ s[0] ^= SHA3_IOTA_H[round];
525
+ s[1] ^= SHA3_IOTA_L[round];
526
+ }
527
+ clean(B);
528
+ }
529
+ /** Keccak sponge function. */
530
+ var Keccak = class Keccak {
531
+ state;
532
+ pos = 0;
533
+ posOut = 0;
534
+ finished = false;
535
+ state32;
536
+ destroyed = false;
537
+ blockLen;
538
+ suffix;
539
+ outputLen;
540
+ enableXOF = false;
541
+ rounds;
542
+ constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
543
+ this.blockLen = blockLen;
544
+ this.suffix = suffix;
545
+ this.outputLen = outputLen;
546
+ this.enableXOF = enableXOF;
547
+ this.rounds = rounds;
548
+ anumber$1(outputLen, "outputLen");
549
+ if (!(0 < blockLen && blockLen < 200)) throw new Error("only keccak-f1600 function is supported");
550
+ this.state = new Uint8Array(200);
551
+ this.state32 = u32(this.state);
552
+ }
553
+ clone() {
554
+ return this._cloneInto();
555
+ }
556
+ keccak() {
557
+ swap32IfBE(this.state32);
558
+ keccakP(this.state32, this.rounds);
559
+ swap32IfBE(this.state32);
560
+ this.posOut = 0;
561
+ this.pos = 0;
562
+ }
563
+ update(data) {
564
+ aexists(this);
565
+ abytes$1(data);
566
+ const { blockLen, state } = this;
567
+ const len = data.length;
568
+ for (let pos = 0; pos < len;) {
569
+ const take = Math.min(blockLen - this.pos, len - pos);
570
+ for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++];
571
+ if (this.pos === blockLen) this.keccak();
572
+ }
573
+ return this;
574
+ }
575
+ finish() {
576
+ if (this.finished) return;
577
+ this.finished = true;
578
+ const { state, suffix, pos, blockLen } = this;
579
+ state[pos] ^= suffix;
580
+ if ((suffix & 128) !== 0 && pos === blockLen - 1) this.keccak();
581
+ state[blockLen - 1] ^= 128;
582
+ this.keccak();
583
+ }
584
+ writeInto(out) {
585
+ aexists(this, false);
586
+ abytes$1(out);
587
+ this.finish();
588
+ const bufferOut = this.state;
589
+ const { blockLen } = this;
590
+ for (let pos = 0, len = out.length; pos < len;) {
591
+ if (this.posOut >= blockLen) this.keccak();
592
+ const take = Math.min(blockLen - this.posOut, len - pos);
593
+ out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
594
+ this.posOut += take;
595
+ pos += take;
596
+ }
597
+ return out;
598
+ }
599
+ xofInto(out) {
600
+ if (!this.enableXOF) throw new Error("XOF is not possible for this instance");
601
+ return this.writeInto(out);
602
+ }
603
+ xof(bytes) {
604
+ anumber$1(bytes);
605
+ return this.xofInto(new Uint8Array(bytes));
606
+ }
607
+ digestInto(out) {
608
+ aoutput(out, this);
609
+ if (this.finished) throw new Error("digest() was already called");
610
+ this.writeInto(out);
611
+ this.destroy();
612
+ return out;
613
+ }
614
+ digest() {
615
+ return this.digestInto(new Uint8Array(this.outputLen));
616
+ }
617
+ destroy() {
618
+ this.destroyed = true;
619
+ clean(this.state);
620
+ }
621
+ _cloneInto(to) {
622
+ const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
623
+ to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
624
+ to.state32.set(this.state32);
625
+ to.pos = this.pos;
626
+ to.posOut = this.posOut;
627
+ to.finished = this.finished;
628
+ to.rounds = rounds;
629
+ to.suffix = suffix;
630
+ to.outputLen = outputLen;
631
+ to.enableXOF = enableXOF;
632
+ to.destroyed = this.destroyed;
633
+ return to;
634
+ }
635
+ };
636
+ const genKeccak = (suffix, blockLen, outputLen, info = {}) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info);
637
+ /** keccak-256 hash function. Different from SHA3-256. */
638
+ const keccak_256 = /* @__PURE__ */ genKeccak(1, 136, 32);
639
+
640
+ //#endregion
641
+ //#region src/crypto/keccak256.ts
642
+ /**
643
+ * Keccak-256 hash function wrapper
644
+ * Uses @noble/hashes for audited, tree-shakeable implementation
645
+ */
646
+ /**
647
+ * Compute Keccak-256 hash (NOT SHA-3)
648
+ *
649
+ * @param input - String or Uint8Array to hash
650
+ * @returns Lowercase hex string (64 chars, no 0x prefix)
651
+ */
652
+ function keccak256(input) {
653
+ const hash = keccak_256(typeof input === "string" ? new TextEncoder().encode(input) : input);
654
+ return Array.from(hash).map((b) => b.toString(16).padStart(2, "0")).join("");
655
+ }
656
+
657
+ //#endregion
658
+ //#region ../../node_modules/.pnpm/@scure+base@2.0.0/node_modules/@scure/base/index.js
659
+ /*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
660
+ function isBytes(a) {
661
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
662
+ }
663
+ /** Asserts something is Uint8Array. */
664
+ function abytes(b) {
665
+ if (!isBytes(b)) throw new Error("Uint8Array expected");
666
+ }
667
+ function isArrayOf(isString, arr) {
668
+ if (!Array.isArray(arr)) return false;
669
+ if (arr.length === 0) return true;
670
+ if (isString) return arr.every((item) => typeof item === "string");
671
+ else return arr.every((item) => Number.isSafeInteger(item));
672
+ }
673
+ function afn(input) {
674
+ if (typeof input !== "function") throw new Error("function expected");
675
+ return true;
676
+ }
677
+ function astr(label, input) {
678
+ if (typeof input !== "string") throw new Error(`${label}: string expected`);
679
+ return true;
680
+ }
681
+ function anumber(n) {
682
+ if (!Number.isSafeInteger(n)) throw new Error(`invalid integer: ${n}`);
683
+ }
684
+ function aArr(input) {
685
+ if (!Array.isArray(input)) throw new Error("array expected");
686
+ }
687
+ function astrArr(label, input) {
688
+ if (!isArrayOf(true, input)) throw new Error(`${label}: array of strings expected`);
689
+ }
690
+ function anumArr(label, input) {
691
+ if (!isArrayOf(false, input)) throw new Error(`${label}: array of numbers expected`);
692
+ }
693
+ /**
694
+ * @__NO_SIDE_EFFECTS__
695
+ */
696
+ function chain(...args) {
697
+ const id = (a) => a;
698
+ const wrap = (a, b) => (c) => a(b(c));
699
+ return {
700
+ encode: args.map((x) => x.encode).reduceRight(wrap, id),
701
+ decode: args.map((x) => x.decode).reduce(wrap, id)
702
+ };
703
+ }
704
+ /**
705
+ * Encodes integer radix representation to array of strings using alphabet and back.
706
+ * Could also be array of strings.
707
+ * @__NO_SIDE_EFFECTS__
708
+ */
709
+ function alphabet(letters) {
710
+ const lettersA = typeof letters === "string" ? letters.split("") : letters;
711
+ const len = lettersA.length;
712
+ astrArr("alphabet", lettersA);
713
+ const indexes = new Map(lettersA.map((l, i) => [l, i]));
714
+ return {
715
+ encode: (digits) => {
716
+ aArr(digits);
717
+ return digits.map((i) => {
718
+ if (!Number.isSafeInteger(i) || i < 0 || i >= len) throw new Error(`alphabet.encode: digit index outside alphabet "${i}". Allowed: ${letters}`);
719
+ return lettersA[i];
720
+ });
721
+ },
722
+ decode: (input) => {
723
+ aArr(input);
724
+ return input.map((letter) => {
725
+ astr("alphabet.decode", letter);
726
+ const i = indexes.get(letter);
727
+ if (i === void 0) throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`);
728
+ return i;
729
+ });
730
+ }
731
+ };
732
+ }
733
+ /**
734
+ * @__NO_SIDE_EFFECTS__
735
+ */
736
+ function join(separator = "") {
737
+ astr("join", separator);
738
+ return {
739
+ encode: (from) => {
740
+ astrArr("join.decode", from);
741
+ return from.join(separator);
742
+ },
743
+ decode: (to) => {
744
+ astr("join.decode", to);
745
+ return to.split(separator);
746
+ }
747
+ };
748
+ }
749
+ /**
750
+ * Pad strings array so it has integer number of bits
751
+ * @__NO_SIDE_EFFECTS__
752
+ */
753
+ function padding(bits, chr = "=") {
754
+ anumber(bits);
755
+ astr("padding", chr);
756
+ return {
757
+ encode(data) {
758
+ astrArr("padding.encode", data);
759
+ while (data.length * bits % 8) data.push(chr);
760
+ return data;
761
+ },
762
+ decode(input) {
763
+ astrArr("padding.decode", input);
764
+ let end = input.length;
765
+ if (end * bits % 8) throw new Error("padding: invalid, string should have whole number of bytes");
766
+ for (; end > 0 && input[end - 1] === chr; end--) if ((end - 1) * bits % 8 === 0) throw new Error("padding: invalid, string has too much padding");
767
+ return input.slice(0, end);
768
+ }
769
+ };
770
+ }
771
+ /**
772
+ * @__NO_SIDE_EFFECTS__
773
+ */
774
+ function normalize$1(fn) {
775
+ afn(fn);
776
+ return {
777
+ encode: (from) => from,
778
+ decode: (to) => fn(to)
779
+ };
780
+ }
781
+ /**
782
+ * Slow: O(n^2) time complexity
783
+ */
784
+ function convertRadix(data, from, to) {
785
+ if (from < 2) throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`);
786
+ if (to < 2) throw new Error(`convertRadix: invalid to=${to}, base cannot be less than 2`);
787
+ aArr(data);
788
+ if (!data.length) return [];
789
+ let pos = 0;
790
+ const res = [];
791
+ const digits = Array.from(data, (d) => {
792
+ anumber(d);
793
+ if (d < 0 || d >= from) throw new Error(`invalid integer: ${d}`);
794
+ return d;
795
+ });
796
+ const dlen = digits.length;
797
+ while (true) {
798
+ let carry = 0;
799
+ let done = true;
800
+ for (let i = pos; i < dlen; i++) {
801
+ const digit = digits[i];
802
+ const fromCarry = from * carry;
803
+ const digitBase = fromCarry + digit;
804
+ if (!Number.isSafeInteger(digitBase) || fromCarry / from !== carry || digitBase - digit !== fromCarry) throw new Error("convertRadix: carry overflow");
805
+ const div = digitBase / to;
806
+ carry = digitBase % to;
807
+ const rounded = Math.floor(div);
808
+ digits[i] = rounded;
809
+ if (!Number.isSafeInteger(rounded) || rounded * to + carry !== digitBase) throw new Error("convertRadix: carry overflow");
810
+ if (!done) continue;
811
+ else if (!rounded) pos = i;
812
+ else done = false;
813
+ }
814
+ res.push(carry);
815
+ if (done) break;
816
+ }
817
+ for (let i = 0; i < data.length - 1 && data[i] === 0; i++) res.push(0);
818
+ return res.reverse();
819
+ }
820
+ const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
821
+ const radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to));
822
+ const powers = /* @__PURE__ */ (() => {
823
+ let res = [];
824
+ for (let i = 0; i < 40; i++) res.push(2 ** i);
825
+ return res;
826
+ })();
827
+ /**
828
+ * Implemented with numbers, because BigInt is 5x slower
829
+ */
830
+ function convertRadix2(data, from, to, padding) {
831
+ aArr(data);
832
+ if (from <= 0 || from > 32) throw new Error(`convertRadix2: wrong from=${from}`);
833
+ if (to <= 0 || to > 32) throw new Error(`convertRadix2: wrong to=${to}`);
834
+ if (/* @__PURE__ */ radix2carry(from, to) > 32) throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${/* @__PURE__ */ radix2carry(from, to)}`);
835
+ let carry = 0;
836
+ let pos = 0;
837
+ const max = powers[from];
838
+ const mask = powers[to] - 1;
839
+ const res = [];
840
+ for (const n of data) {
841
+ anumber(n);
842
+ if (n >= max) throw new Error(`convertRadix2: invalid data word=${n} from=${from}`);
843
+ carry = carry << from | n;
844
+ if (pos + from > 32) throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`);
845
+ pos += from;
846
+ for (; pos >= to; pos -= to) res.push((carry >> pos - to & mask) >>> 0);
847
+ const pow = powers[pos];
848
+ if (pow === void 0) throw new Error("invalid carry");
849
+ carry &= pow - 1;
850
+ }
851
+ carry = carry << to - pos & mask;
852
+ if (!padding && pos >= from) throw new Error("Excess padding");
853
+ if (!padding && carry > 0) throw new Error(`Non-zero padding: ${carry}`);
854
+ if (padding && pos > 0) res.push(carry >>> 0);
855
+ return res;
856
+ }
857
+ /**
858
+ * @__NO_SIDE_EFFECTS__
859
+ */
860
+ function radix(num) {
861
+ anumber(num);
862
+ const _256 = 2 ** 8;
863
+ return {
864
+ encode: (bytes) => {
865
+ if (!isBytes(bytes)) throw new Error("radix.encode input should be Uint8Array");
866
+ return convertRadix(Array.from(bytes), _256, num);
867
+ },
868
+ decode: (digits) => {
869
+ anumArr("radix.decode", digits);
870
+ return Uint8Array.from(convertRadix(digits, num, _256));
871
+ }
872
+ };
873
+ }
874
+ /**
875
+ * If both bases are power of same number (like `2**8 <-> 2**64`),
876
+ * there is a linear algorithm. For now we have implementation for power-of-two bases only.
877
+ * @__NO_SIDE_EFFECTS__
878
+ */
879
+ function radix2(bits, revPadding = false) {
880
+ anumber(bits);
881
+ if (bits <= 0 || bits > 32) throw new Error("radix2: bits should be in (0..32]");
882
+ if (/* @__PURE__ */ radix2carry(8, bits) > 32 || /* @__PURE__ */ radix2carry(bits, 8) > 32) throw new Error("radix2: carry overflow");
883
+ return {
884
+ encode: (bytes) => {
885
+ if (!isBytes(bytes)) throw new Error("radix2.encode input should be Uint8Array");
886
+ return convertRadix2(Array.from(bytes), 8, bits, !revPadding);
887
+ },
888
+ decode: (digits) => {
889
+ anumArr("radix2.decode", digits);
890
+ return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding));
891
+ }
892
+ };
893
+ }
894
+ function unsafeWrapper(fn) {
895
+ afn(fn);
896
+ return function(...args) {
897
+ try {
898
+ return fn.apply(null, args);
899
+ } catch (e) {}
900
+ };
901
+ }
902
+ /**
903
+ * base16 encoding from RFC 4648.
904
+ * @example
905
+ * ```js
906
+ * base16.encode(Uint8Array.from([0x12, 0xab]));
907
+ * // => '12AB'
908
+ * ```
909
+ */
910
+ const base16 = chain(radix2(4), alphabet("0123456789ABCDEF"), join(""));
911
+ /**
912
+ * base32 encoding from RFC 4648. Has padding.
913
+ * Use `base32nopad` for unpadded version.
914
+ * Also check out `base32hex`, `base32hexnopad`, `base32crockford`.
915
+ * @example
916
+ * ```js
917
+ * base32.encode(Uint8Array.from([0x12, 0xab]));
918
+ * // => 'CKVQ===='
919
+ * base32.decode('CKVQ====');
920
+ * // => Uint8Array.from([0x12, 0xab])
921
+ * ```
922
+ */
923
+ const base32 = chain(radix2(5), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding(5), join(""));
924
+ /**
925
+ * base32 encoding from RFC 4648. No padding.
926
+ * Use `base32` for padded version.
927
+ * Also check out `base32hex`, `base32hexnopad`, `base32crockford`.
928
+ * @example
929
+ * ```js
930
+ * base32nopad.encode(Uint8Array.from([0x12, 0xab]));
931
+ * // => 'CKVQ'
932
+ * base32nopad.decode('CKVQ');
933
+ * // => Uint8Array.from([0x12, 0xab])
934
+ * ```
935
+ */
936
+ const base32nopad = chain(radix2(5), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), join(""));
937
+ /**
938
+ * base32 encoding from RFC 4648. Padded. Compared to ordinary `base32`, slightly different alphabet.
939
+ * Use `base32hexnopad` for unpadded version.
940
+ * @example
941
+ * ```js
942
+ * base32hex.encode(Uint8Array.from([0x12, 0xab]));
943
+ * // => '2ALG===='
944
+ * base32hex.decode('2ALG====');
945
+ * // => Uint8Array.from([0x12, 0xab])
946
+ * ```
947
+ */
948
+ const base32hex = chain(radix2(5), alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding(5), join(""));
949
+ /**
950
+ * base32 encoding from RFC 4648. No padding. Compared to ordinary `base32`, slightly different alphabet.
951
+ * Use `base32hex` for padded version.
952
+ * @example
953
+ * ```js
954
+ * base32hexnopad.encode(Uint8Array.from([0x12, 0xab]));
955
+ * // => '2ALG'
956
+ * base32hexnopad.decode('2ALG');
957
+ * // => Uint8Array.from([0x12, 0xab])
958
+ * ```
959
+ */
960
+ const base32hexnopad = chain(radix2(5), alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), join(""));
961
+ /**
962
+ * base32 encoding from RFC 4648. Doug Crockford's version.
963
+ * https://www.crockford.com/base32.html
964
+ * @example
965
+ * ```js
966
+ * base32crockford.encode(Uint8Array.from([0x12, 0xab]));
967
+ * // => '2ANG'
968
+ * base32crockford.decode('2ANG');
969
+ * // => Uint8Array.from([0x12, 0xab])
970
+ * ```
971
+ */
972
+ const base32crockford = chain(radix2(5), alphabet("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join(""), normalize$1((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1")));
973
+ const hasBase64Builtin = typeof Uint8Array.from([]).toBase64 === "function" && typeof Uint8Array.fromBase64 === "function";
974
+ const decodeBase64Builtin = (s, isUrl) => {
975
+ astr("base64", s);
976
+ const re = isUrl ? /^[A-Za-z0-9=_-]+$/ : /^[A-Za-z0-9=+/]+$/;
977
+ const alphabet = isUrl ? "base64url" : "base64";
978
+ if (s.length > 0 && !re.test(s)) throw new Error("invalid base64");
979
+ return Uint8Array.fromBase64(s, {
980
+ alphabet,
981
+ lastChunkHandling: "strict"
982
+ });
983
+ };
984
+ /**
985
+ * base64 from RFC 4648. Padded.
986
+ * Use `base64nopad` for unpadded version.
987
+ * Also check out `base64url`, `base64urlnopad`.
988
+ * Falls back to built-in function, when available.
989
+ * @example
990
+ * ```js
991
+ * base64.encode(Uint8Array.from([0x12, 0xab]));
992
+ * // => 'Eqs='
993
+ * base64.decode('Eqs=');
994
+ * // => Uint8Array.from([0x12, 0xab])
995
+ * ```
996
+ */
997
+ const base64 = hasBase64Builtin ? {
998
+ encode(b) {
999
+ abytes(b);
1000
+ return b.toBase64();
1001
+ },
1002
+ decode(s) {
1003
+ return decodeBase64Builtin(s, false);
1004
+ }
1005
+ } : chain(radix2(6), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding(6), join(""));
1006
+ /**
1007
+ * base64 from RFC 4648. No padding.
1008
+ * Use `base64` for padded version.
1009
+ * @example
1010
+ * ```js
1011
+ * base64nopad.encode(Uint8Array.from([0x12, 0xab]));
1012
+ * // => 'Eqs'
1013
+ * base64nopad.decode('Eqs');
1014
+ * // => Uint8Array.from([0x12, 0xab])
1015
+ * ```
1016
+ */
1017
+ const base64nopad = chain(radix2(6), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), join(""));
1018
+ /**
1019
+ * base64 from RFC 4648, using URL-safe alphabet. Padded.
1020
+ * Use `base64urlnopad` for unpadded version.
1021
+ * Falls back to built-in function, when available.
1022
+ * @example
1023
+ * ```js
1024
+ * base64url.encode(Uint8Array.from([0x12, 0xab]));
1025
+ * // => 'Eqs='
1026
+ * base64url.decode('Eqs=');
1027
+ * // => Uint8Array.from([0x12, 0xab])
1028
+ * ```
1029
+ */
1030
+ const base64url = hasBase64Builtin ? {
1031
+ encode(b) {
1032
+ abytes(b);
1033
+ return b.toBase64({ alphabet: "base64url" });
1034
+ },
1035
+ decode(s) {
1036
+ return decodeBase64Builtin(s, true);
1037
+ }
1038
+ } : chain(radix2(6), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding(6), join(""));
1039
+ /**
1040
+ * base64 from RFC 4648, using URL-safe alphabet. No padding.
1041
+ * Use `base64url` for padded version.
1042
+ * @example
1043
+ * ```js
1044
+ * base64urlnopad.encode(Uint8Array.from([0x12, 0xab]));
1045
+ * // => 'Eqs'
1046
+ * base64urlnopad.decode('Eqs');
1047
+ * // => Uint8Array.from([0x12, 0xab])
1048
+ * ```
1049
+ */
1050
+ const base64urlnopad = chain(radix2(6), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), join(""));
1051
+ const genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => chain(radix(58), alphabet(abc), join(""));
1052
+ /**
1053
+ * base58: base64 without ambigous characters +, /, 0, O, I, l.
1054
+ * Quadratic (O(n^2)) - so, can't be used on large inputs.
1055
+ * @example
1056
+ * ```js
1057
+ * base58.decode('01abcdef');
1058
+ * // => '3UhJW'
1059
+ * ```
1060
+ */
1061
+ const base58 = /* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
1062
+ const BECH_ALPHABET = chain(alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join(""));
1063
+ const POLYMOD_GENERATORS = [
1064
+ 996825010,
1065
+ 642813549,
1066
+ 513874426,
1067
+ 1027748829,
1068
+ 705979059
1069
+ ];
1070
+ function bech32Polymod(pre) {
1071
+ const b = pre >> 25;
1072
+ let chk = (pre & 33554431) << 5;
1073
+ for (let i = 0; i < POLYMOD_GENERATORS.length; i++) if ((b >> i & 1) === 1) chk ^= POLYMOD_GENERATORS[i];
1074
+ return chk;
1075
+ }
1076
+ function bechChecksum(prefix, words, encodingConst = 1) {
1077
+ const len = prefix.length;
1078
+ let chk = 1;
1079
+ for (let i = 0; i < len; i++) {
1080
+ const c = prefix.charCodeAt(i);
1081
+ if (c < 33 || c > 126) throw new Error(`Invalid prefix (${prefix})`);
1082
+ chk = bech32Polymod(chk) ^ c >> 5;
1083
+ }
1084
+ chk = bech32Polymod(chk);
1085
+ for (let i = 0; i < len; i++) chk = bech32Polymod(chk) ^ prefix.charCodeAt(i) & 31;
1086
+ for (let v of words) chk = bech32Polymod(chk) ^ v;
1087
+ for (let i = 0; i < 6; i++) chk = bech32Polymod(chk);
1088
+ chk ^= encodingConst;
1089
+ return BECH_ALPHABET.encode(convertRadix2([chk % powers[30]], 30, 5, false));
1090
+ }
1091
+ /**
1092
+ * @__NO_SIDE_EFFECTS__
1093
+ */
1094
+ function genBech32(encoding) {
1095
+ const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939;
1096
+ const _words = radix2(5);
1097
+ const fromWords = _words.decode;
1098
+ const toWords = _words.encode;
1099
+ const fromWordsUnsafe = unsafeWrapper(fromWords);
1100
+ function encode(prefix, words, limit = 90) {
1101
+ astr("bech32.encode prefix", prefix);
1102
+ if (isBytes(words)) words = Array.from(words);
1103
+ anumArr("bech32.encode", words);
1104
+ const plen = prefix.length;
1105
+ if (plen === 0) throw new TypeError(`Invalid prefix length ${plen}`);
1106
+ const actualLength = plen + 7 + words.length;
1107
+ if (limit !== false && actualLength > limit) throw new TypeError(`Length ${actualLength} exceeds limit ${limit}`);
1108
+ const lowered = prefix.toLowerCase();
1109
+ const sum = bechChecksum(lowered, words, ENCODING_CONST);
1110
+ return `${lowered}1${BECH_ALPHABET.encode(words)}${sum}`;
1111
+ }
1112
+ function decode(str, limit = 90) {
1113
+ astr("bech32.decode input", str);
1114
+ const slen = str.length;
1115
+ if (slen < 8 || limit !== false && slen > limit) throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit})`);
1116
+ const lowered = str.toLowerCase();
1117
+ if (str !== lowered && str !== str.toUpperCase()) throw new Error(`String must be lowercase or uppercase`);
1118
+ const sepIndex = lowered.lastIndexOf("1");
1119
+ if (sepIndex === 0 || sepIndex === -1) throw new Error(`Letter "1" must be present between prefix and data only`);
1120
+ const prefix = lowered.slice(0, sepIndex);
1121
+ const data = lowered.slice(sepIndex + 1);
1122
+ if (data.length < 6) throw new Error("Data must be at least 6 characters long");
1123
+ const words = BECH_ALPHABET.decode(data).slice(0, -6);
1124
+ const sum = bechChecksum(prefix, words, ENCODING_CONST);
1125
+ if (!data.endsWith(sum)) throw new Error(`Invalid checksum in ${str}: expected "${sum}"`);
1126
+ return {
1127
+ prefix,
1128
+ words
1129
+ };
1130
+ }
1131
+ const decodeUnsafe = unsafeWrapper(decode);
1132
+ function decodeToBytes(str) {
1133
+ const { prefix, words } = decode(str, false);
1134
+ return {
1135
+ prefix,
1136
+ words,
1137
+ bytes: fromWords(words)
1138
+ };
1139
+ }
1140
+ function encodeFromBytes(prefix, bytes) {
1141
+ return encode(prefix, toWords(bytes));
1142
+ }
1143
+ return {
1144
+ encode,
1145
+ decode,
1146
+ encodeFromBytes,
1147
+ decodeToBytes,
1148
+ decodeUnsafe,
1149
+ fromWords,
1150
+ fromWordsUnsafe,
1151
+ toWords
1152
+ };
1153
+ }
1154
+ /**
1155
+ * bech32 from BIP 173. Operates on words.
1156
+ * For high-level, check out scure-btc-signer:
1157
+ * https://github.com/paulmillr/scure-btc-signer.
1158
+ */
1159
+ const bech32 = genBech32("bech32");
1160
+ /**
1161
+ * bech32m from BIP 350. Operates on words.
1162
+ * It was to mitigate `bech32` weaknesses.
1163
+ * For high-level, check out scure-btc-signer:
1164
+ * https://github.com/paulmillr/scure-btc-signer.
1165
+ */
1166
+ const bech32m = genBech32("bech32m");
1167
+ const hasHexBuiltin = typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function";
1168
+ const hexBuiltin = {
1169
+ encode(data) {
1170
+ abytes(data);
1171
+ return data.toHex();
1172
+ },
1173
+ decode(s) {
1174
+ astr("hex", s);
1175
+ return Uint8Array.fromHex(s);
1176
+ }
1177
+ };
1178
+ /**
1179
+ * hex string decoder. Uses built-in function, when available.
1180
+ * @example
1181
+ * ```js
1182
+ * const b = hex.decode("0102ff"); // => new Uint8Array([ 1, 2, 255 ])
1183
+ * const str = hex.encode(b); // "0102ff"
1184
+ * ```
1185
+ */
1186
+ const hex = hasHexBuiltin ? hexBuiltin : chain(radix2(4), alphabet("0123456789abcdef"), join(""), normalize$1((s) => {
1187
+ if (typeof s !== "string" || s.length % 2 !== 0) throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);
1188
+ return s.toLowerCase();
1189
+ }));
1190
+
1191
+ //#endregion
1192
+ //#region src/crypto/base58.ts
1193
+ /**
1194
+ * Base58 decoder wrapper
1195
+ * Uses @scure/base for audited, tree-shakeable implementation
1196
+ */
1197
+ /**
1198
+ * Decode a Base58-encoded string to bytes
1199
+ *
1200
+ * Preserves leading zero bytes (represented as leading '1' characters)
1201
+ *
1202
+ * @param input - Base58 string
1203
+ * @returns Decoded bytes
1204
+ * @throws Error if input contains invalid Base58 characters
1205
+ */
1206
+ function decodeBase58(input) {
1207
+ try {
1208
+ return base58.decode(input);
1209
+ } catch (error) {
1210
+ const message = error instanceof Error ? error.message : String(error);
1211
+ throw new Error(`Invalid Base58: ${message}`);
1212
+ }
1213
+ }
1214
+
1215
+ //#endregion
1216
+ //#region src/crypto/eip55.ts
1217
+ /**
1218
+ * EIP-55 mixed-case checksum address encoding
1219
+ * Spec: https://eips.ethereum.org/EIPS/eip-55
1220
+ */
1221
+ /**
1222
+ * Convert an Ethereum address to EIP-55 checksummed format
1223
+ *
1224
+ * @param address - 42-character hex address (0x-prefixed)
1225
+ * @returns Checksummed address with mixed case
1226
+ */
1227
+ function toChecksumAddress(address) {
1228
+ const lowerHex = address.slice(2).toLowerCase();
1229
+ const hash = keccak256(lowerHex);
1230
+ let result = "0x";
1231
+ for (let i = 0; i < lowerHex.length; i++) {
1232
+ const char = lowerHex[i];
1233
+ const hashChar = hash[i];
1234
+ if (!char || !hashChar) continue;
1235
+ if (char >= "a" && char <= "f") result += parseInt(hashChar, 16) >= 8 ? char.toUpperCase() : char;
1236
+ else result += char;
1237
+ }
1238
+ return result;
1239
+ }
1240
+ /**
1241
+ * Check if an address has valid EIP-55 checksum
1242
+ *
1243
+ * Returns false for all-lowercase or all-uppercase addresses
1244
+ * (these are valid formats but do not match their checksummed version)
1245
+ *
1246
+ * @param address - Address to validate
1247
+ * @returns True if checksum is valid
1248
+ */
1249
+ function isValidChecksum(address) {
1250
+ return address === toChecksumAddress(address);
1251
+ }
1252
+
1253
+ //#endregion
1254
+ //#region src/validation/evm-address.ts
1255
+ /**
1256
+ * EVM address validation with EIP-55 checksum verification
1257
+ */
1258
+ const EVM_ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
1259
+ /**
1260
+ * Validate an EVM address format and checksum
1261
+ *
1262
+ * Returns errors for invalid format, warnings for checksum issues
1263
+ *
1264
+ * @param address - Address to validate
1265
+ * @param field - Field path for error reporting
1266
+ * @returns Array of validation issues (empty if valid)
1267
+ */
1268
+ function validateEvmAddress(address, field) {
1269
+ if (!EVM_ADDRESS_REGEX.test(address)) return [{
1270
+ code: ErrorCode.INVALID_EVM_ADDRESS,
1271
+ field,
1272
+ message: "EVM address must be 42 hex characters with 0x prefix",
1273
+ severity: "error",
1274
+ fix: "Format: 0x followed by 40 hex digits (0-9, a-f, A-F)"
1275
+ }];
1276
+ const hexPart = address.slice(2);
1277
+ if (address === address.toLowerCase() && /[a-f]/.test(hexPart)) return [{
1278
+ code: ErrorCode.NO_EVM_CHECKSUM,
1279
+ field,
1280
+ message: "EVM address is all-lowercase with no checksum protection",
1281
+ severity: "warning",
1282
+ fix: `Use checksummed address to detect typos: ${toChecksumAddress(address)}`
1283
+ }];
1284
+ if (/^[0-9A-F]{40}$/.test(hexPart) && /[A-F]/.test(hexPart)) return [];
1285
+ if (/^[0-9]{40}$/.test(hexPart)) return [];
1286
+ if (!isValidChecksum(address)) return [{
1287
+ code: ErrorCode.BAD_EVM_CHECKSUM,
1288
+ field,
1289
+ message: "EVM address has invalid checksum (EIP-55)",
1290
+ severity: "warning",
1291
+ fix: `Expected: ${toChecksumAddress(address)}`
1292
+ }];
1293
+ return [];
1294
+ }
1295
+
1296
+ //#endregion
1297
+ //#region src/validation/solana-address.ts
1298
+ /**
1299
+ * Solana address validation (Base58 + 32-byte length)
1300
+ */
1301
+ const SOLANA_ADDRESS_REGEX = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
1302
+ /**
1303
+ * Validate a Solana address (Base58 encoded public key)
1304
+ *
1305
+ * Checks Base58 format and verifies decoded length is exactly 32 bytes
1306
+ *
1307
+ * @param address - Address to validate
1308
+ * @param field - Field path for error reporting
1309
+ * @returns Array of validation issues (empty if valid)
1310
+ */
1311
+ function validateSolanaAddress(address, field) {
1312
+ if (!SOLANA_ADDRESS_REGEX.test(address)) return [{
1313
+ code: ErrorCode.INVALID_SOLANA_ADDRESS,
1314
+ field,
1315
+ message: "Solana address must be 32-44 Base58 characters",
1316
+ severity: "error",
1317
+ fix: "Valid characters: 1-9, A-H, J-N, P-Z, a-k, m-z (no 0, O, I, l)"
1318
+ }];
1319
+ try {
1320
+ const decoded = decodeBase58(address);
1321
+ if (decoded.length !== 32) return [{
1322
+ code: ErrorCode.INVALID_SOLANA_ADDRESS,
1323
+ field,
1324
+ message: `Solana address must decode to 32 bytes, got ${decoded.length}`,
1325
+ severity: "error",
1326
+ fix: "Verify address is a valid Solana public key"
1327
+ }];
1328
+ } catch (error) {
1329
+ return [{
1330
+ code: ErrorCode.INVALID_SOLANA_ADDRESS,
1331
+ field,
1332
+ message: "Invalid Base58 encoding",
1333
+ severity: "error",
1334
+ fix: error instanceof Error ? error.message : "Check Base58 encoding"
1335
+ }];
1336
+ }
1337
+ return [];
1338
+ }
1339
+
1340
+ //#endregion
1341
+ //#region src/validation/address.ts
1342
+ /**
1343
+ * Address validation with CAIP-2 namespace dispatch
1344
+ *
1345
+ * Dispatches to chain-specific validators based on network namespace
1346
+ */
1347
+ /**
1348
+ * Validate an address for a specific network
1349
+ *
1350
+ * Dispatches to appropriate chain-specific validator based on CAIP-2 namespace:
1351
+ * - eip155:* → EVM address validation
1352
+ * - solana:* → Solana address validation
1353
+ * - stellar:*, aptos:* → Accept any string (deep validation deferred)
1354
+ * - Unknown namespaces → Accept any string (registry warnings handled elsewhere)
1355
+ *
1356
+ * Cross-chain mismatches are caught naturally by dispatch:
1357
+ * - EVM address (0x...) on Solana network → fails Solana Base58 validation
1358
+ * - Solana address on EVM network → fails EVM 0x-prefix validation
1359
+ *
1360
+ * @param address - Address to validate
1361
+ * @param network - CAIP-2 network identifier
1362
+ * @param field - Field path for error reporting
1363
+ * @returns Array of validation issues (empty if valid)
1364
+ */
1365
+ function validateAddress(address, network, field) {
1366
+ const namespace = getNetworkNamespace(network);
1367
+ if (namespace === void 0) return [];
1368
+ switch (namespace) {
1369
+ case "eip155": return validateEvmAddress(address, field);
1370
+ case "solana": return validateSolanaAddress(address, field);
1371
+ case "stellar":
1372
+ case "aptos": return [];
1373
+ default: return [];
1374
+ }
1375
+ }
1376
+
1377
+ //#endregion
1378
+ //#region src/validation/rules/structure.ts
1379
+ /**
1380
+ * Validate input structure: parse JSON, check object, detect format.
1381
+ *
1382
+ * @param input - Raw JSON string or object
1383
+ * @returns StructureResult with parsed object, detected format, and issues
1384
+ */
1385
+ function validateStructure(input) {
1386
+ const issues = [];
1387
+ const { parsed, error } = parseInput(input);
1388
+ if (error) return {
1389
+ parsed: null,
1390
+ format: "unknown",
1391
+ issues: [error]
1392
+ };
1393
+ if (!isRecord(parsed)) {
1394
+ issues.push({
1395
+ code: ErrorCode.NOT_OBJECT,
1396
+ field: "$",
1397
+ message: ErrorMessages.NOT_OBJECT,
1398
+ severity: "error"
1399
+ });
1400
+ return {
1401
+ parsed: null,
1402
+ format: "unknown",
1403
+ issues
1404
+ };
1405
+ }
1406
+ const format = detect(parsed);
1407
+ if (format === "unknown") issues.push({
1408
+ code: ErrorCode.UNKNOWN_FORMAT,
1409
+ field: "$",
1410
+ message: ErrorMessages.UNKNOWN_FORMAT,
1411
+ severity: "error"
1412
+ });
1413
+ return {
1414
+ parsed,
1415
+ format,
1416
+ issues
1417
+ };
1418
+ }
1419
+
1420
+ //#endregion
1421
+ //#region src/validation/rules/version.ts
1422
+ /**
1423
+ * Validate x402Version field.
1424
+ *
1425
+ * Since normalize() always sets x402Version: 2, this mainly validates
1426
+ * the original format's version field. If somehow the value isn't 1 or 2,
1427
+ * push INVALID_VERSION error.
1428
+ *
1429
+ * Note: No MISSING_VERSION check needed here because normalize() always
1430
+ * sets it. The orchestrator handles version-related warnings for legacy
1431
+ * formats via the legacy rule module.
1432
+ *
1433
+ * @param config - Normalized config
1434
+ * @param _detectedFormat - Detected format (reserved for future use)
1435
+ * @returns Array of validation issues
1436
+ */
1437
+ function validateVersion(config, _detectedFormat) {
1438
+ const issues = [];
1439
+ const version = config.x402Version;
1440
+ if (version !== 1 && version !== 2) issues.push({
1441
+ code: ErrorCode.INVALID_VERSION,
1442
+ field: "x402Version",
1443
+ message: ErrorMessages.INVALID_VERSION,
1444
+ severity: "error"
1445
+ });
1446
+ return issues;
1447
+ }
1448
+
1449
+ //#endregion
1450
+ //#region src/validation/rules/fields.ts
1451
+ /**
1452
+ * Validate required fields on a single accepts entry.
1453
+ *
1454
+ * @param entry - Accepts entry to validate
1455
+ * @param fieldPath - Dot-notation path for issue reporting (e.g. "accepts[0]")
1456
+ * @returns Array of validation issues
1457
+ */
1458
+ function validateFields(entry, fieldPath) {
1459
+ const issues = [];
1460
+ if (!entry.scheme) issues.push({
1461
+ code: ErrorCode.MISSING_SCHEME,
1462
+ field: `${fieldPath}.scheme`,
1463
+ message: ErrorMessages.MISSING_SCHEME,
1464
+ severity: "error"
1465
+ });
1466
+ if (!entry.network) issues.push({
1467
+ code: ErrorCode.MISSING_NETWORK,
1468
+ field: `${fieldPath}.network`,
1469
+ message: ErrorMessages.MISSING_NETWORK,
1470
+ severity: "error"
1471
+ });
1472
+ if (!entry.amount) issues.push({
1473
+ code: ErrorCode.MISSING_AMOUNT,
1474
+ field: `${fieldPath}.amount`,
1475
+ message: ErrorMessages.MISSING_AMOUNT,
1476
+ severity: "error"
1477
+ });
1478
+ if (!entry.asset) issues.push({
1479
+ code: ErrorCode.MISSING_ASSET,
1480
+ field: `${fieldPath}.asset`,
1481
+ message: ErrorMessages.MISSING_ASSET,
1482
+ severity: "error"
1483
+ });
1484
+ if (!entry.payTo) issues.push({
1485
+ code: ErrorCode.MISSING_PAY_TO,
1486
+ field: `${fieldPath}.payTo`,
1487
+ message: ErrorMessages.MISSING_PAY_TO,
1488
+ severity: "error"
1489
+ });
1490
+ return issues;
1491
+ }
1492
+ /**
1493
+ * Validate the accepts array itself (presence, type, emptiness).
1494
+ *
1495
+ * @param config - Normalized config
1496
+ * @returns Array of validation issues
1497
+ */
1498
+ function validateAccepts(config) {
1499
+ const issues = [];
1500
+ if (!Array.isArray(config.accepts)) {
1501
+ issues.push({
1502
+ code: ErrorCode.INVALID_ACCEPTS,
1503
+ field: "accepts",
1504
+ message: ErrorMessages.INVALID_ACCEPTS,
1505
+ severity: "error"
1506
+ });
1507
+ return issues;
1508
+ }
1509
+ if (config.accepts.length === 0) issues.push({
1510
+ code: ErrorCode.EMPTY_ACCEPTS,
1511
+ field: "accepts",
1512
+ message: ErrorMessages.EMPTY_ACCEPTS,
1513
+ severity: "error"
1514
+ });
1515
+ return issues;
1516
+ }
1517
+ /**
1518
+ * Validate resource object on normalized config.
1519
+ *
1520
+ * For v2 configs, resource is expected. Its absence is a warning, not an error,
1521
+ * since some v2 configs work without it.
1522
+ *
1523
+ * Also validates URL format via new URL() constructor (RULE-04).
1524
+ *
1525
+ * @param config - Normalized config
1526
+ * @param detectedFormat - Detected format
1527
+ * @returns Array of validation issues
1528
+ */
1529
+ function validateResource(config, detectedFormat) {
1530
+ const issues = [];
1531
+ if (!config.resource) {
1532
+ if (detectedFormat === "v2") issues.push({
1533
+ code: ErrorCode.MISSING_RESOURCE,
1534
+ field: "resource",
1535
+ message: ErrorMessages.MISSING_RESOURCE,
1536
+ severity: "warning"
1537
+ });
1538
+ return issues;
1539
+ }
1540
+ if (!config.resource.url) {
1541
+ issues.push({
1542
+ code: ErrorCode.MISSING_RESOURCE,
1543
+ field: "resource.url",
1544
+ message: ErrorMessages.MISSING_RESOURCE,
1545
+ severity: "warning"
1546
+ });
1547
+ return issues;
1548
+ }
1549
+ try {
1550
+ new URL(config.resource.url);
1551
+ } catch {
1552
+ issues.push({
1553
+ code: ErrorCode.INVALID_URL,
1554
+ field: "resource.url",
1555
+ message: "resource.url is not a valid URL format",
1556
+ severity: "warning"
1557
+ });
1558
+ }
1559
+ return issues;
1560
+ }
1561
+
1562
+ //#endregion
1563
+ //#region src/validation/rules/network.ts
1564
+ /**
1565
+ * Validate network field on a single accepts entry.
1566
+ *
1567
+ * Checks CAIP-2 format and known network registry. Provides fix
1568
+ * suggestions for simple chain names that have canonical CAIP-2 mappings.
1569
+ *
1570
+ * @param entry - Accepts entry to validate
1571
+ * @param fieldPath - Dot-notation path for issue reporting (e.g. "accepts[0]")
1572
+ * @returns Array of validation issues
1573
+ */
1574
+ function validateNetwork(entry, fieldPath) {
1575
+ const issues = [];
1576
+ if (!entry.network) return issues;
1577
+ if (!isValidCaip2(entry.network)) {
1578
+ const canonical = getCanonicalNetwork(entry.network);
1579
+ if (canonical) issues.push({
1580
+ code: ErrorCode.INVALID_NETWORK_FORMAT,
1581
+ field: `${fieldPath}.network`,
1582
+ message: ErrorMessages.INVALID_NETWORK_FORMAT,
1583
+ severity: "error",
1584
+ fix: `Use '${canonical}' instead of '${entry.network}'`
1585
+ });
1586
+ else issues.push({
1587
+ code: ErrorCode.INVALID_NETWORK_FORMAT,
1588
+ field: `${fieldPath}.network`,
1589
+ message: ErrorMessages.INVALID_NETWORK_FORMAT,
1590
+ severity: "error"
1591
+ });
1592
+ return issues;
1593
+ }
1594
+ if (!isKnownNetwork(entry.network)) issues.push({
1595
+ code: ErrorCode.UNKNOWN_NETWORK,
1596
+ field: `${fieldPath}.network`,
1597
+ message: ErrorMessages.UNKNOWN_NETWORK,
1598
+ severity: "warning"
1599
+ });
1600
+ return issues;
1601
+ }
1602
+ /**
1603
+ * Validate asset field on a single accepts entry.
1604
+ *
1605
+ * Checks if the asset is known for the given network. Only checks
1606
+ * when both network and asset are present and network is valid.
1607
+ *
1608
+ * @param entry - Accepts entry to validate
1609
+ * @param fieldPath - Dot-notation path for issue reporting (e.g. "accepts[0]")
1610
+ * @returns Array of validation issues
1611
+ */
1612
+ function validateAsset(entry, fieldPath) {
1613
+ const issues = [];
1614
+ if (!entry.asset) return issues;
1615
+ if (entry.network && isValidCaip2(entry.network) && !isKnownAsset(entry.network, entry.asset)) issues.push({
1616
+ code: ErrorCode.UNKNOWN_ASSET,
1617
+ field: `${fieldPath}.asset`,
1618
+ message: ErrorMessages.UNKNOWN_ASSET,
1619
+ severity: "warning"
1620
+ });
1621
+ return issues;
1622
+ }
1623
+
1624
+ //#endregion
1625
+ //#region src/validation/rules/amount.ts
1626
+ /**
1627
+ * Validate amount field on a single accepts entry.
1628
+ *
1629
+ * Amount must be a digit-only string (no decimals, signs, or scientific notation)
1630
+ * and must be greater than zero.
1631
+ *
1632
+ * @param entry - Accepts entry to validate
1633
+ * @param fieldPath - Dot-notation path for issue reporting (e.g. "accepts[0]")
1634
+ * @returns Array of validation issues
1635
+ */
1636
+ function validateAmount(entry, fieldPath) {
1637
+ const issues = [];
1638
+ if (!entry.amount) return issues;
1639
+ if (!/^\d+$/.test(entry.amount)) {
1640
+ issues.push({
1641
+ code: ErrorCode.INVALID_AMOUNT,
1642
+ field: `${fieldPath}.amount`,
1643
+ message: ErrorMessages.INVALID_AMOUNT,
1644
+ severity: "error"
1645
+ });
1646
+ return issues;
1647
+ }
1648
+ if (entry.amount === "0") issues.push({
1649
+ code: ErrorCode.ZERO_AMOUNT,
1650
+ field: `${fieldPath}.amount`,
1651
+ message: ErrorMessages.ZERO_AMOUNT,
1652
+ severity: "error"
1653
+ });
1654
+ return issues;
1655
+ }
1656
+ /**
1657
+ * Validate maxTimeoutSeconds on a single accepts entry.
1658
+ *
1659
+ * For v2 format, missing timeout produces a warning.
1660
+ * When present, timeout must be a positive integer (RULE-10).
1661
+ *
1662
+ * @param entry - Accepts entry to validate
1663
+ * @param fieldPath - Dot-notation path for issue reporting (e.g. "accepts[0]")
1664
+ * @param detectedFormat - Detected config format
1665
+ * @returns Array of validation issues
1666
+ */
1667
+ function validateTimeout(entry, fieldPath, detectedFormat) {
1668
+ const issues = [];
1669
+ if (entry.maxTimeoutSeconds === void 0) {
1670
+ if (detectedFormat === "v2") issues.push({
1671
+ code: ErrorCode.MISSING_MAX_TIMEOUT,
1672
+ field: `${fieldPath}.maxTimeoutSeconds`,
1673
+ message: ErrorMessages.MISSING_MAX_TIMEOUT,
1674
+ severity: "warning"
1675
+ });
1676
+ return issues;
1677
+ }
1678
+ if (typeof entry.maxTimeoutSeconds !== "number") {
1679
+ issues.push({
1680
+ code: ErrorCode.INVALID_TIMEOUT,
1681
+ field: `${fieldPath}.maxTimeoutSeconds`,
1682
+ message: ErrorMessages.INVALID_TIMEOUT,
1683
+ severity: "error"
1684
+ });
1685
+ return issues;
1686
+ }
1687
+ if (!Number.isInteger(entry.maxTimeoutSeconds)) {
1688
+ issues.push({
1689
+ code: ErrorCode.INVALID_TIMEOUT,
1690
+ field: `${fieldPath}.maxTimeoutSeconds`,
1691
+ message: ErrorMessages.INVALID_TIMEOUT,
1692
+ severity: "error"
1693
+ });
1694
+ return issues;
1695
+ }
1696
+ if (entry.maxTimeoutSeconds <= 0) issues.push({
1697
+ code: ErrorCode.INVALID_TIMEOUT,
1698
+ field: `${fieldPath}.maxTimeoutSeconds`,
1699
+ message: ErrorMessages.INVALID_TIMEOUT,
1700
+ severity: "error"
1701
+ });
1702
+ return issues;
1703
+ }
1704
+
1705
+ //#endregion
1706
+ //#region src/validation/rules/legacy.ts
1707
+ /**
1708
+ * Validate for legacy format usage and produce upgrade suggestions.
1709
+ *
1710
+ * @param _config - Normalized config (reserved for future use)
1711
+ * @param detectedFormat - Detected config format
1712
+ * @param _originalInput - Original input object (reserved for future use)
1713
+ * @returns Array of validation issues (warnings)
1714
+ */
1715
+ function validateLegacy(_config, detectedFormat, _originalInput) {
1716
+ const issues = [];
1717
+ if (detectedFormat === "v1") issues.push({
1718
+ code: ErrorCode.LEGACY_FORMAT,
1719
+ field: "$",
1720
+ message: ErrorMessages.LEGACY_FORMAT,
1721
+ severity: "warning",
1722
+ fix: "Upgrade to x402 v2 -- use amount instead of maxAmountRequired, add resource object"
1723
+ });
1724
+ return issues;
1725
+ }
1726
+
1727
+ //#endregion
1728
+ //#region src/validation/orchestrator.ts
1729
+ /**
1730
+ * Validate an x402 config through the full pipeline.
1731
+ *
1732
+ * Takes any input (JSON string or object), runs it through:
1733
+ * 1. Structure validation (parse, object check, format detection)
1734
+ * 2. Normalization to canonical v2 shape
1735
+ * 3. Version, accepts, resource validation
1736
+ * 4. Per-entry field, network, asset, amount, timeout, address validation
1737
+ * 5. Legacy format warnings
1738
+ * 6. Strict mode promotion (warnings -> errors)
1739
+ *
1740
+ * NEVER throws -- all invalid inputs produce structured error results.
1741
+ *
1742
+ * @param input - JSON string or parsed object to validate
1743
+ * @param options - Validation options (e.g. strict mode)
1744
+ * @returns Structured validation result
1745
+ */
1746
+ function validate(input, options) {
1747
+ try {
1748
+ return runPipeline(input, options);
1749
+ } catch {
1750
+ return {
1751
+ valid: false,
1752
+ version: "unknown",
1753
+ errors: [{
1754
+ code: ErrorCode.UNKNOWN_FORMAT,
1755
+ field: "$",
1756
+ message: "Unexpected validation error",
1757
+ severity: "error"
1758
+ }],
1759
+ warnings: [],
1760
+ normalized: null
1761
+ };
1762
+ }
1763
+ }
1764
+ /**
1765
+ * Internal pipeline implementation.
1766
+ * Separated from validate() so the try/catch safety net is clean.
1767
+ */
1768
+ function runPipeline(input, options) {
1769
+ const structure = validateStructure(input);
1770
+ if (structure.issues.length > 0) return {
1771
+ valid: false,
1772
+ version: structure.format || "unknown",
1773
+ errors: structure.issues,
1774
+ warnings: [],
1775
+ normalized: null
1776
+ };
1777
+ const parsed = structure.parsed;
1778
+ const format = structure.format;
1779
+ const normalized = normalize(parsed);
1780
+ if (normalized === null) return {
1781
+ valid: false,
1782
+ version: format,
1783
+ errors: [{
1784
+ code: ErrorCode.UNKNOWN_FORMAT,
1785
+ field: "$",
1786
+ message: ErrorMessages.UNKNOWN_FORMAT,
1787
+ severity: "error"
1788
+ }],
1789
+ warnings: [],
1790
+ normalized: null
1791
+ };
1792
+ const errors = [];
1793
+ const warnings = [];
1794
+ errors.push(...validateVersion(normalized, format));
1795
+ errors.push(...validateAccepts(normalized));
1796
+ warnings.push(...validateResource(normalized, format));
1797
+ if (Array.isArray(normalized.accepts) && normalized.accepts.length > 0) for (let i = 0; i < normalized.accepts.length; i++) {
1798
+ const entry = normalized.accepts[i];
1799
+ const fieldPath = `accepts[${i}]`;
1800
+ errors.push(...validateFields(entry, fieldPath));
1801
+ for (const issue of validateNetwork(entry, fieldPath)) if (issue.severity === "error") errors.push(issue);
1802
+ else warnings.push(issue);
1803
+ warnings.push(...validateAsset(entry, fieldPath));
1804
+ errors.push(...validateAmount(entry, fieldPath));
1805
+ for (const issue of validateTimeout(entry, fieldPath, format)) if (issue.severity === "error") errors.push(issue);
1806
+ else warnings.push(issue);
1807
+ if (entry.payTo && entry.network) for (const issue of validateAddress(entry.payTo, entry.network, `${fieldPath}.payTo`)) if (issue.severity === "error") errors.push(issue);
1808
+ else warnings.push(issue);
1809
+ }
1810
+ warnings.push(...validateLegacy(normalized, format, parsed));
1811
+ if (options?.strict === true) {
1812
+ for (const warning of warnings) errors.push({
1813
+ ...warning,
1814
+ severity: "error"
1815
+ });
1816
+ warnings.length = 0;
1817
+ }
1818
+ return {
1819
+ valid: errors.length === 0,
1820
+ version: format,
1821
+ errors,
1822
+ warnings,
1823
+ normalized
1824
+ };
1825
+ }
1826
+
1827
+ //#endregion
1828
+ //#region src/index.ts
1829
+ const VERSION = "0.0.1";
1830
+
1831
+ //#endregion
1832
+ exports.CAIP2_REGEX = CAIP2_REGEX;
1833
+ exports.ErrorCode = ErrorCode;
1834
+ exports.ErrorMessages = ErrorMessages;
1835
+ exports.KNOWN_ASSETS = KNOWN_ASSETS;
1836
+ exports.KNOWN_NETWORKS = KNOWN_NETWORKS;
1837
+ exports.SIMPLE_NAME_TO_CAIP2 = SIMPLE_NAME_TO_CAIP2;
1838
+ exports.VERSION = VERSION;
1839
+ exports.decodeBase58 = decodeBase58;
1840
+ exports.detect = detect;
1841
+ exports.getAssetInfo = getAssetInfo;
1842
+ exports.getCanonicalNetwork = getCanonicalNetwork;
1843
+ exports.getNetworkInfo = getNetworkInfo;
1844
+ exports.getNetworkNamespace = getNetworkNamespace;
1845
+ exports.isKnownAsset = isKnownAsset;
1846
+ exports.isKnownNetwork = isKnownNetwork;
1847
+ exports.isValidCaip2 = isValidCaip2;
1848
+ exports.isValidChecksum = isValidChecksum;
1849
+ exports.keccak256 = keccak256;
1850
+ exports.normalize = normalize;
1851
+ exports.parseInput = parseInput;
1852
+ exports.toChecksumAddress = toChecksumAddress;
1853
+ exports.validate = validate;
1854
+ exports.validateAddress = validateAddress;
1855
+ exports.validateEvmAddress = validateEvmAddress;
1856
+ exports.validateSolanaAddress = validateSolanaAddress;
1857
+ //# sourceMappingURL=index.cjs.map