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