uvd-x402-sdk 2.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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +782 -0
  3. package/dist/index-BrBqP1I8.d.ts +199 -0
  4. package/dist/index-D6Sr4ARD.d.mts +429 -0
  5. package/dist/index-D6Sr4ARD.d.ts +429 -0
  6. package/dist/index-DJ4Cvrev.d.mts +199 -0
  7. package/dist/index.d.mts +3 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.js +1178 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/index.mjs +1146 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/dist/providers/evm/index.d.mts +84 -0
  14. package/dist/providers/evm/index.d.ts +84 -0
  15. package/dist/providers/evm/index.js +740 -0
  16. package/dist/providers/evm/index.js.map +1 -0
  17. package/dist/providers/evm/index.mjs +735 -0
  18. package/dist/providers/evm/index.mjs.map +1 -0
  19. package/dist/providers/near/index.d.mts +99 -0
  20. package/dist/providers/near/index.d.ts +99 -0
  21. package/dist/providers/near/index.js +483 -0
  22. package/dist/providers/near/index.js.map +1 -0
  23. package/dist/providers/near/index.mjs +478 -0
  24. package/dist/providers/near/index.mjs.map +1 -0
  25. package/dist/providers/solana/index.d.mts +115 -0
  26. package/dist/providers/solana/index.d.ts +115 -0
  27. package/dist/providers/solana/index.js +771 -0
  28. package/dist/providers/solana/index.js.map +1 -0
  29. package/dist/providers/solana/index.mjs +765 -0
  30. package/dist/providers/solana/index.mjs.map +1 -0
  31. package/dist/providers/stellar/index.d.mts +67 -0
  32. package/dist/providers/stellar/index.d.ts +67 -0
  33. package/dist/providers/stellar/index.js +306 -0
  34. package/dist/providers/stellar/index.js.map +1 -0
  35. package/dist/providers/stellar/index.mjs +301 -0
  36. package/dist/providers/stellar/index.mjs.map +1 -0
  37. package/dist/react/index.d.mts +73 -0
  38. package/dist/react/index.d.ts +73 -0
  39. package/dist/react/index.js +1218 -0
  40. package/dist/react/index.js.map +1 -0
  41. package/dist/react/index.mjs +1211 -0
  42. package/dist/react/index.mjs.map +1 -0
  43. package/dist/utils/index.d.mts +103 -0
  44. package/dist/utils/index.d.ts +103 -0
  45. package/dist/utils/index.js +575 -0
  46. package/dist/utils/index.js.map +1 -0
  47. package/dist/utils/index.mjs +562 -0
  48. package/dist/utils/index.mjs.map +1 -0
  49. package/package.json +149 -0
  50. package/src/chains/index.ts +539 -0
  51. package/src/client/X402Client.ts +663 -0
  52. package/src/client/index.ts +1 -0
  53. package/src/index.ts +166 -0
  54. package/src/providers/evm/index.ts +394 -0
  55. package/src/providers/near/index.ts +664 -0
  56. package/src/providers/solana/index.ts +489 -0
  57. package/src/providers/stellar/index.ts +376 -0
  58. package/src/react/index.tsx +417 -0
  59. package/src/types/index.ts +561 -0
  60. package/src/utils/index.ts +20 -0
  61. package/src/utils/x402.ts +295 -0
@@ -0,0 +1,1218 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var ethers = require('ethers');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/react/index.tsx
8
+
9
+ // src/types/index.ts
10
+ var CAIP2_IDENTIFIERS = {
11
+ // EVM chains
12
+ base: "eip155:8453",
13
+ ethereum: "eip155:1",
14
+ polygon: "eip155:137",
15
+ arbitrum: "eip155:42161",
16
+ optimism: "eip155:10",
17
+ avalanche: "eip155:43114",
18
+ celo: "eip155:42220",
19
+ hyperevm: "eip155:999",
20
+ unichain: "eip155:130",
21
+ monad: "eip155:143",
22
+ bsc: "eip155:56",
23
+ // SVM chains
24
+ solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
25
+ fogo: "svm:fogo",
26
+ // Stellar
27
+ stellar: "stellar:pubnet",
28
+ // NEAR
29
+ near: "near:mainnet"
30
+ };
31
+ Object.fromEntries(
32
+ Object.entries(CAIP2_IDENTIFIERS).map(([k, v]) => [v, k])
33
+ );
34
+ var DEFAULT_CONFIG = {
35
+ facilitatorUrl: "https://facilitator.ultravioletadao.xyz",
36
+ defaultChain: "base",
37
+ autoConnect: false,
38
+ debug: false,
39
+ x402Version: "auto"
40
+ };
41
+ var X402Error = class _X402Error extends Error {
42
+ code;
43
+ details;
44
+ constructor(message, code, details) {
45
+ super(message);
46
+ this.name = "X402Error";
47
+ this.code = code;
48
+ this.details = details;
49
+ if (Error.captureStackTrace) {
50
+ Error.captureStackTrace(this, _X402Error);
51
+ }
52
+ }
53
+ };
54
+
55
+ // src/chains/index.ts
56
+ var DEFAULT_FACILITATOR_URL = "https://facilitator.ultravioletadao.xyz";
57
+ var SUPPORTED_CHAINS = {
58
+ // ============================================================================
59
+ // EVM CHAINS (11 networks)
60
+ // ============================================================================
61
+ base: {
62
+ chainId: 8453,
63
+ chainIdHex: "0x2105",
64
+ name: "base",
65
+ displayName: "Base",
66
+ networkType: "evm",
67
+ rpcUrl: "https://mainnet.base.org",
68
+ explorerUrl: "https://basescan.org",
69
+ nativeCurrency: {
70
+ name: "Ethereum",
71
+ symbol: "ETH",
72
+ decimals: 18
73
+ },
74
+ usdc: {
75
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
76
+ decimals: 6,
77
+ name: "USD Coin",
78
+ version: "2"
79
+ },
80
+ x402: {
81
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
82
+ enabled: true
83
+ }
84
+ },
85
+ avalanche: {
86
+ chainId: 43114,
87
+ chainIdHex: "0xa86a",
88
+ name: "avalanche",
89
+ displayName: "Avalanche C-Chain",
90
+ networkType: "evm",
91
+ rpcUrl: "https://avalanche-c-chain-rpc.publicnode.com",
92
+ explorerUrl: "https://snowtrace.io",
93
+ nativeCurrency: {
94
+ name: "Avalanche",
95
+ symbol: "AVAX",
96
+ decimals: 18
97
+ },
98
+ usdc: {
99
+ address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
100
+ decimals: 6,
101
+ name: "USD Coin",
102
+ version: "2"
103
+ },
104
+ x402: {
105
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
106
+ enabled: true
107
+ }
108
+ },
109
+ ethereum: {
110
+ chainId: 1,
111
+ chainIdHex: "0x1",
112
+ name: "ethereum",
113
+ displayName: "Ethereum",
114
+ networkType: "evm",
115
+ rpcUrl: "https://eth.llamarpc.com",
116
+ explorerUrl: "https://etherscan.io",
117
+ nativeCurrency: {
118
+ name: "Ethereum",
119
+ symbol: "ETH",
120
+ decimals: 18
121
+ },
122
+ usdc: {
123
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
124
+ decimals: 6,
125
+ name: "USD Coin",
126
+ version: "2"
127
+ },
128
+ x402: {
129
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
130
+ enabled: true
131
+ }
132
+ },
133
+ polygon: {
134
+ chainId: 137,
135
+ chainIdHex: "0x89",
136
+ name: "polygon",
137
+ displayName: "Polygon",
138
+ networkType: "evm",
139
+ rpcUrl: "https://polygon-rpc.com",
140
+ explorerUrl: "https://polygonscan.com",
141
+ nativeCurrency: {
142
+ name: "Polygon",
143
+ symbol: "POL",
144
+ decimals: 18
145
+ },
146
+ usdc: {
147
+ address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
148
+ decimals: 6,
149
+ name: "USD Coin",
150
+ version: "2"
151
+ },
152
+ x402: {
153
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
154
+ enabled: true
155
+ }
156
+ },
157
+ arbitrum: {
158
+ chainId: 42161,
159
+ chainIdHex: "0xa4b1",
160
+ name: "arbitrum",
161
+ displayName: "Arbitrum One",
162
+ networkType: "evm",
163
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
164
+ explorerUrl: "https://arbiscan.io",
165
+ nativeCurrency: {
166
+ name: "Ethereum",
167
+ symbol: "ETH",
168
+ decimals: 18
169
+ },
170
+ usdc: {
171
+ address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
172
+ decimals: 6,
173
+ name: "USD Coin",
174
+ version: "2"
175
+ },
176
+ x402: {
177
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
178
+ enabled: true
179
+ }
180
+ },
181
+ optimism: {
182
+ chainId: 10,
183
+ chainIdHex: "0xa",
184
+ name: "optimism",
185
+ displayName: "Optimism",
186
+ networkType: "evm",
187
+ rpcUrl: "https://mainnet.optimism.io",
188
+ explorerUrl: "https://optimistic.etherscan.io",
189
+ nativeCurrency: {
190
+ name: "Ethereum",
191
+ symbol: "ETH",
192
+ decimals: 18
193
+ },
194
+ usdc: {
195
+ address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
196
+ decimals: 6,
197
+ name: "USD Coin",
198
+ version: "2"
199
+ },
200
+ x402: {
201
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
202
+ enabled: true
203
+ }
204
+ },
205
+ celo: {
206
+ chainId: 42220,
207
+ chainIdHex: "0xa4ec",
208
+ name: "celo",
209
+ displayName: "Celo",
210
+ networkType: "evm",
211
+ rpcUrl: "https://forno.celo.org",
212
+ explorerUrl: "https://celoscan.io",
213
+ nativeCurrency: {
214
+ name: "Celo",
215
+ symbol: "CELO",
216
+ decimals: 18
217
+ },
218
+ usdc: {
219
+ address: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
220
+ decimals: 6,
221
+ name: "USDC",
222
+ // Celo uses "USDC" not "USD Coin" for EIP-712
223
+ version: "2"
224
+ },
225
+ x402: {
226
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
227
+ enabled: true
228
+ }
229
+ },
230
+ hyperevm: {
231
+ chainId: 999,
232
+ chainIdHex: "0x3e7",
233
+ name: "hyperevm",
234
+ displayName: "HyperEVM",
235
+ networkType: "evm",
236
+ rpcUrl: "https://rpc.hyperliquid.xyz/evm",
237
+ explorerUrl: "https://hyperevmscan.io",
238
+ nativeCurrency: {
239
+ name: "Ethereum",
240
+ symbol: "ETH",
241
+ decimals: 18
242
+ },
243
+ usdc: {
244
+ address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
245
+ decimals: 6,
246
+ name: "USDC",
247
+ // HyperEVM uses "USDC" not "USD Coin"
248
+ version: "2"
249
+ },
250
+ x402: {
251
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
252
+ enabled: true
253
+ }
254
+ },
255
+ unichain: {
256
+ chainId: 130,
257
+ chainIdHex: "0x82",
258
+ name: "unichain",
259
+ displayName: "Unichain",
260
+ networkType: "evm",
261
+ rpcUrl: "https://unichain-rpc.publicnode.com",
262
+ explorerUrl: "https://uniscan.xyz",
263
+ nativeCurrency: {
264
+ name: "Ethereum",
265
+ symbol: "ETH",
266
+ decimals: 18
267
+ },
268
+ usdc: {
269
+ address: "0x078d782b760474a361dda0af3839290b0ef57ad6",
270
+ decimals: 6,
271
+ name: "USDC",
272
+ // Unichain uses "USDC" not "USD Coin"
273
+ version: "2"
274
+ },
275
+ x402: {
276
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
277
+ enabled: true
278
+ }
279
+ },
280
+ monad: {
281
+ chainId: 143,
282
+ chainIdHex: "0x8f",
283
+ name: "monad",
284
+ displayName: "Monad",
285
+ networkType: "evm",
286
+ rpcUrl: "https://rpc.monad.xyz",
287
+ explorerUrl: "https://monad.socialscan.io",
288
+ nativeCurrency: {
289
+ name: "Monad",
290
+ symbol: "MON",
291
+ decimals: 18
292
+ },
293
+ usdc: {
294
+ address: "0x754704bc059f8c67012fed69bc8a327a5aafb603",
295
+ decimals: 6,
296
+ name: "USDC",
297
+ // Monad uses "USDC" not "USD Coin"
298
+ version: "2"
299
+ },
300
+ x402: {
301
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
302
+ enabled: true
303
+ }
304
+ },
305
+ // BSC disabled: USDC doesn't support ERC-3009 transferWithAuthorization
306
+ bsc: {
307
+ chainId: 56,
308
+ chainIdHex: "0x38",
309
+ name: "bsc",
310
+ displayName: "BNB Smart Chain",
311
+ networkType: "evm",
312
+ rpcUrl: "https://binance.llamarpc.com",
313
+ explorerUrl: "https://bscscan.com",
314
+ nativeCurrency: {
315
+ name: "Binance Coin",
316
+ symbol: "BNB",
317
+ decimals: 18
318
+ },
319
+ usdc: {
320
+ address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
321
+ decimals: 18,
322
+ // BSC USDC uses 18 decimals
323
+ name: "USD Coin",
324
+ version: "2"
325
+ },
326
+ x402: {
327
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
328
+ enabled: false
329
+ // Disabled: BSC USDC doesn't support ERC-3009
330
+ }
331
+ },
332
+ // ============================================================================
333
+ // SVM CHAINS (2 networks) - Solana Virtual Machine
334
+ // ============================================================================
335
+ solana: {
336
+ chainId: 0,
337
+ // Non-EVM
338
+ chainIdHex: "0x0",
339
+ name: "solana",
340
+ displayName: "Solana",
341
+ networkType: "svm",
342
+ rpcUrl: "https://api.mainnet-beta.solana.com",
343
+ explorerUrl: "https://solscan.io",
344
+ nativeCurrency: {
345
+ name: "Solana",
346
+ symbol: "SOL",
347
+ decimals: 9
348
+ },
349
+ usdc: {
350
+ address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
351
+ // USDC SPL token mint
352
+ decimals: 6,
353
+ name: "USD Coin",
354
+ version: "1"
355
+ },
356
+ x402: {
357
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
358
+ enabled: true
359
+ }
360
+ },
361
+ fogo: {
362
+ chainId: 0,
363
+ // Non-EVM (SVM)
364
+ chainIdHex: "0x0",
365
+ name: "fogo",
366
+ displayName: "Fogo",
367
+ networkType: "svm",
368
+ rpcUrl: "https://rpc.fogo.nightly.app/",
369
+ explorerUrl: "https://explorer.fogo.nightly.app",
370
+ nativeCurrency: {
371
+ name: "Fogo",
372
+ symbol: "FOGO",
373
+ decimals: 9
374
+ },
375
+ usdc: {
376
+ address: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
377
+ // Fogo USDC mint
378
+ decimals: 6,
379
+ name: "USDC",
380
+ version: "1"
381
+ },
382
+ x402: {
383
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
384
+ enabled: true
385
+ }
386
+ },
387
+ // ============================================================================
388
+ // STELLAR (1 network)
389
+ // ============================================================================
390
+ stellar: {
391
+ chainId: 0,
392
+ // Non-EVM
393
+ chainIdHex: "0x0",
394
+ name: "stellar",
395
+ displayName: "Stellar",
396
+ networkType: "stellar",
397
+ rpcUrl: "https://horizon.stellar.org",
398
+ explorerUrl: "https://stellar.expert/explorer/public",
399
+ nativeCurrency: {
400
+ name: "Lumens",
401
+ symbol: "XLM",
402
+ decimals: 7
403
+ // Stellar uses 7 decimals (stroops)
404
+ },
405
+ usdc: {
406
+ address: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
407
+ // Soroban Asset Contract
408
+ decimals: 7,
409
+ // Stellar USDC uses 7 decimals
410
+ name: "USDC",
411
+ version: "1"
412
+ },
413
+ x402: {
414
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
415
+ enabled: true
416
+ }
417
+ },
418
+ // ============================================================================
419
+ // NEAR (1 network) - Uses NEP-366 meta-transactions
420
+ // ============================================================================
421
+ near: {
422
+ chainId: 0,
423
+ // Non-EVM
424
+ chainIdHex: "0x0",
425
+ name: "near",
426
+ displayName: "NEAR Protocol",
427
+ networkType: "near",
428
+ rpcUrl: "https://rpc.mainnet.near.org",
429
+ explorerUrl: "https://nearblocks.io",
430
+ nativeCurrency: {
431
+ name: "NEAR",
432
+ symbol: "NEAR",
433
+ decimals: 24
434
+ // NEAR uses 24 decimals (yoctoNEAR)
435
+ },
436
+ usdc: {
437
+ address: "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
438
+ // Native Circle USDC
439
+ decimals: 6,
440
+ name: "USDC",
441
+ version: "1"
442
+ },
443
+ x402: {
444
+ facilitatorUrl: DEFAULT_FACILITATOR_URL,
445
+ enabled: true
446
+ // NEP-366 meta-transactions supported
447
+ }
448
+ }
449
+ };
450
+ function getChainById(chainId) {
451
+ return Object.values(SUPPORTED_CHAINS).find((chain) => chain.chainId === chainId);
452
+ }
453
+ function getChainByName(name) {
454
+ return SUPPORTED_CHAINS[name.toLowerCase()];
455
+ }
456
+ function getEnabledChains() {
457
+ return Object.values(SUPPORTED_CHAINS).filter((chain) => chain.x402.enabled);
458
+ }
459
+
460
+ // src/client/X402Client.ts
461
+ var X402Client = class {
462
+ // Configuration
463
+ config;
464
+ // Wallet state
465
+ provider = null;
466
+ signer = null;
467
+ connectedAddress = null;
468
+ currentChainId = null;
469
+ currentNetwork = null;
470
+ currentChainName = null;
471
+ // Event emitter
472
+ eventHandlers = /* @__PURE__ */ new Map();
473
+ constructor(config = {}) {
474
+ this.config = {
475
+ ...DEFAULT_CONFIG,
476
+ ...config
477
+ };
478
+ if (config.rpcOverrides) {
479
+ for (const [chainName, rpcUrl] of Object.entries(config.rpcOverrides)) {
480
+ if (SUPPORTED_CHAINS[chainName]) {
481
+ SUPPORTED_CHAINS[chainName].rpcUrl = rpcUrl;
482
+ }
483
+ }
484
+ }
485
+ if (config.customChains) {
486
+ for (const [chainName, chainConfig] of Object.entries(config.customChains)) {
487
+ if (SUPPORTED_CHAINS[chainName]) {
488
+ Object.assign(SUPPORTED_CHAINS[chainName], chainConfig);
489
+ }
490
+ }
491
+ }
492
+ this.log("X402Client initialized", { config: this.config });
493
+ }
494
+ // ============================================================================
495
+ // PUBLIC API - Wallet Connection
496
+ // ============================================================================
497
+ /**
498
+ * Connect to a wallet on the specified chain
499
+ */
500
+ async connect(chainName) {
501
+ const targetChain = chainName || this.config.defaultChain;
502
+ const chain = getChainByName(targetChain);
503
+ if (!chain) {
504
+ throw new X402Error(`Unsupported chain: ${targetChain}`, "CHAIN_NOT_SUPPORTED");
505
+ }
506
+ if (!chain.x402.enabled) {
507
+ throw new X402Error(`Chain ${targetChain} is not enabled for x402 payments`, "CHAIN_NOT_SUPPORTED");
508
+ }
509
+ this.log(`Connecting wallet on ${chain.displayName}...`);
510
+ switch (chain.networkType) {
511
+ case "evm":
512
+ return this.connectEVMWallet(chain);
513
+ case "solana":
514
+ throw new X402Error(
515
+ 'Solana support requires importing from "uvd-x402-sdk/solana"',
516
+ "CHAIN_NOT_SUPPORTED"
517
+ );
518
+ case "stellar":
519
+ throw new X402Error(
520
+ 'Stellar support requires importing from "uvd-x402-sdk/stellar"',
521
+ "CHAIN_NOT_SUPPORTED"
522
+ );
523
+ case "near":
524
+ throw new X402Error("NEAR is not yet supported by the facilitator", "CHAIN_NOT_SUPPORTED");
525
+ default:
526
+ throw new X402Error(`Unknown network type for chain ${targetChain}`, "CHAIN_NOT_SUPPORTED");
527
+ }
528
+ }
529
+ /**
530
+ * Disconnect the current wallet
531
+ */
532
+ async disconnect() {
533
+ this.provider = null;
534
+ this.signer = null;
535
+ this.connectedAddress = null;
536
+ this.currentChainId = null;
537
+ this.currentNetwork = null;
538
+ this.currentChainName = null;
539
+ this.emit("disconnect", void 0);
540
+ this.log("Wallet disconnected");
541
+ }
542
+ /**
543
+ * Switch to a different chain (EVM only)
544
+ */
545
+ async switchChain(chainName) {
546
+ const chain = getChainByName(chainName);
547
+ if (!chain) {
548
+ throw new X402Error(`Unsupported chain: ${chainName}`, "CHAIN_NOT_SUPPORTED");
549
+ }
550
+ if (chain.networkType !== "evm") {
551
+ throw new X402Error(
552
+ "switchChain is only supported for EVM networks. Reconnect with connect() for other networks.",
553
+ "CHAIN_NOT_SUPPORTED"
554
+ );
555
+ }
556
+ if (!this.provider) {
557
+ throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
558
+ }
559
+ await this.switchEVMChain(chain);
560
+ }
561
+ // ============================================================================
562
+ // PUBLIC API - Payment Creation
563
+ // ============================================================================
564
+ /**
565
+ * Create a payment authorization
566
+ *
567
+ * @param paymentInfo - Payment information from 402 response
568
+ * @returns Payment result with encoded X-PAYMENT header
569
+ */
570
+ async createPayment(paymentInfo) {
571
+ if (!this.connectedAddress) {
572
+ throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
573
+ }
574
+ if (!this.currentChainName) {
575
+ throw new X402Error("Chain not set", "CHAIN_NOT_SUPPORTED");
576
+ }
577
+ const chain = getChainByName(this.currentChainName);
578
+ if (!chain) {
579
+ throw new X402Error(`Chain ${this.currentChainName} not found`, "CHAIN_NOT_SUPPORTED");
580
+ }
581
+ this.emit("paymentStarted", { amount: paymentInfo.amount, network: chain.name });
582
+ this.log("Creating payment...", { paymentInfo, chain: chain.name });
583
+ try {
584
+ switch (chain.networkType) {
585
+ case "evm":
586
+ return await this.createEVMPayment(paymentInfo, chain);
587
+ default:
588
+ throw new X402Error(
589
+ `Payment creation for ${chain.networkType} requires the appropriate provider`,
590
+ "CHAIN_NOT_SUPPORTED"
591
+ );
592
+ }
593
+ } catch (error) {
594
+ const x402Error = error instanceof X402Error ? error : new X402Error(
595
+ error instanceof Error ? error.message : "Unknown error",
596
+ "PAYMENT_FAILED",
597
+ error
598
+ );
599
+ this.emit("paymentFailed", { error: x402Error.message, code: x402Error.code });
600
+ throw x402Error;
601
+ }
602
+ }
603
+ /**
604
+ * Check USDC balance on current chain
605
+ */
606
+ async getBalance() {
607
+ if (!this.connectedAddress || !this.currentChainName) {
608
+ throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
609
+ }
610
+ const chain = getChainByName(this.currentChainName);
611
+ if (!chain) {
612
+ throw new X402Error(`Chain ${this.currentChainName} not found`, "CHAIN_NOT_SUPPORTED");
613
+ }
614
+ switch (chain.networkType) {
615
+ case "evm":
616
+ return this.getEVMBalance(chain);
617
+ default:
618
+ throw new X402Error(
619
+ `Balance check for ${chain.networkType} requires the appropriate provider`,
620
+ "CHAIN_NOT_SUPPORTED"
621
+ );
622
+ }
623
+ }
624
+ // ============================================================================
625
+ // PUBLIC API - Getters
626
+ // ============================================================================
627
+ /**
628
+ * Get current wallet state
629
+ */
630
+ getState() {
631
+ return {
632
+ connected: this.connectedAddress !== null,
633
+ address: this.connectedAddress,
634
+ chainId: this.currentChainId,
635
+ network: this.currentChainName,
636
+ networkType: this.currentNetwork,
637
+ balance: null
638
+ // Call getBalance() separately
639
+ };
640
+ }
641
+ /**
642
+ * Get connected wallet address
643
+ */
644
+ getAddress() {
645
+ return this.connectedAddress;
646
+ }
647
+ /**
648
+ * Get current chain ID
649
+ */
650
+ getChainId() {
651
+ return this.currentChainId;
652
+ }
653
+ /**
654
+ * Get current chain name
655
+ */
656
+ getChainName() {
657
+ return this.currentChainName;
658
+ }
659
+ /**
660
+ * Get current chain display name
661
+ */
662
+ getChainDisplayName() {
663
+ if (!this.currentChainName) return null;
664
+ const chain = getChainByName(this.currentChainName);
665
+ return chain?.displayName ?? null;
666
+ }
667
+ /**
668
+ * Check if wallet is connected
669
+ */
670
+ isConnected() {
671
+ return this.connectedAddress !== null;
672
+ }
673
+ /**
674
+ * Get list of enabled chains
675
+ */
676
+ getEnabledChains() {
677
+ return getEnabledChains();
678
+ }
679
+ /**
680
+ * Get chain config by name
681
+ */
682
+ getChain(name) {
683
+ return getChainByName(name);
684
+ }
685
+ // ============================================================================
686
+ // PUBLIC API - Events
687
+ // ============================================================================
688
+ /**
689
+ * Subscribe to an event
690
+ */
691
+ on(event, handler) {
692
+ if (!this.eventHandlers.has(event)) {
693
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
694
+ }
695
+ this.eventHandlers.get(event).add(handler);
696
+ return () => this.off(event, handler);
697
+ }
698
+ /**
699
+ * Unsubscribe from an event
700
+ */
701
+ off(event, handler) {
702
+ this.eventHandlers.get(event)?.delete(handler);
703
+ }
704
+ // ============================================================================
705
+ // PRIVATE - EVM Wallet Connection
706
+ // ============================================================================
707
+ async connectEVMWallet(chain) {
708
+ if (typeof window === "undefined" || !window.ethereum) {
709
+ throw new X402Error(
710
+ "No Ethereum wallet found. Please install MetaMask or another EVM wallet.",
711
+ "WALLET_NOT_FOUND"
712
+ );
713
+ }
714
+ try {
715
+ this.provider = new ethers.ethers.BrowserProvider(window.ethereum);
716
+ await this.provider.send("eth_requestAccounts", []);
717
+ await this.switchEVMChain(chain);
718
+ this.signer = await this.provider.getSigner();
719
+ this.connectedAddress = await this.signer.getAddress();
720
+ this.currentChainId = chain.chainId;
721
+ this.currentNetwork = "evm";
722
+ this.currentChainName = chain.name;
723
+ this.setupEVMEventListeners();
724
+ const state = this.getState();
725
+ this.emit("connect", state);
726
+ this.log("EVM wallet connected", { address: this.connectedAddress, chain: chain.name });
727
+ return this.connectedAddress;
728
+ } catch (error) {
729
+ if (error instanceof Error) {
730
+ if (error.message.includes("User rejected") || error.code === 4001) {
731
+ throw new X402Error("Connection rejected by user", "WALLET_CONNECTION_REJECTED");
732
+ }
733
+ }
734
+ throw new X402Error(
735
+ `Failed to connect wallet: ${error instanceof Error ? error.message : "Unknown error"}`,
736
+ "UNKNOWN_ERROR",
737
+ error
738
+ );
739
+ }
740
+ }
741
+ async switchEVMChain(chain) {
742
+ if (!this.provider) {
743
+ throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
744
+ }
745
+ try {
746
+ await this.provider.send("wallet_switchEthereumChain", [{ chainId: chain.chainIdHex }]);
747
+ } catch (switchError) {
748
+ if (switchError.code === 4902) {
749
+ try {
750
+ await this.provider.send("wallet_addEthereumChain", [
751
+ {
752
+ chainId: chain.chainIdHex,
753
+ chainName: chain.displayName,
754
+ nativeCurrency: chain.nativeCurrency,
755
+ rpcUrls: [chain.rpcUrl],
756
+ blockExplorerUrls: [chain.explorerUrl]
757
+ }
758
+ ]);
759
+ } catch (addError) {
760
+ throw new X402Error(
761
+ `Failed to add ${chain.displayName} network`,
762
+ "CHAIN_SWITCH_REJECTED",
763
+ addError
764
+ );
765
+ }
766
+ } else if (switchError.code === 4001) {
767
+ throw new X402Error("Network switch rejected by user", "CHAIN_SWITCH_REJECTED");
768
+ } else {
769
+ throw new X402Error(
770
+ `Failed to switch to ${chain.displayName}`,
771
+ "CHAIN_SWITCH_REJECTED",
772
+ switchError
773
+ );
774
+ }
775
+ }
776
+ this.currentChainId = chain.chainId;
777
+ this.currentChainName = chain.name;
778
+ this.emit("chainChanged", { chainId: chain.chainId, chainName: chain.name });
779
+ }
780
+ setupEVMEventListeners() {
781
+ if (typeof window === "undefined" || !window.ethereum) return;
782
+ window.ethereum.on?.("accountsChanged", ((...args) => {
783
+ const accounts = args[0];
784
+ if (accounts.length === 0) {
785
+ this.disconnect();
786
+ } else if (accounts[0] !== this.connectedAddress) {
787
+ this.connectedAddress = accounts[0];
788
+ this.emit("accountChanged", { address: accounts[0] });
789
+ }
790
+ }));
791
+ window.ethereum.on?.("chainChanged", ((...args) => {
792
+ const chainIdHex = args[0];
793
+ const chainId = parseInt(chainIdHex, 16);
794
+ const chain = getChainById(chainId);
795
+ if (chain) {
796
+ this.currentChainId = chainId;
797
+ this.currentChainName = chain.name;
798
+ this.emit("chainChanged", { chainId, chainName: chain.name });
799
+ }
800
+ }));
801
+ }
802
+ // ============================================================================
803
+ // PRIVATE - EVM Payment Creation
804
+ // ============================================================================
805
+ async createEVMPayment(paymentInfo, chain) {
806
+ if (!this.signer) {
807
+ throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
808
+ }
809
+ const recipient = this.getRecipientForNetwork(paymentInfo, "evm");
810
+ const nonceBytes = new Uint8Array(32);
811
+ if (typeof window !== "undefined" && window.crypto) {
812
+ window.crypto.getRandomValues(nonceBytes);
813
+ } else {
814
+ for (let i = 0; i < 32; i++) {
815
+ nonceBytes[i] = Math.floor(Math.random() * 256);
816
+ }
817
+ }
818
+ const nonce = ethers.ethers.hexlify(nonceBytes);
819
+ const validAfter = 0;
820
+ const validityWindowSeconds = chain.name === "base" ? 300 : 60;
821
+ const validBefore = Math.floor(Date.now() / 1e3) + validityWindowSeconds;
822
+ const domain = {
823
+ name: chain.usdc.name,
824
+ version: chain.usdc.version,
825
+ chainId: chain.chainId,
826
+ verifyingContract: chain.usdc.address
827
+ };
828
+ const types = {
829
+ TransferWithAuthorization: [
830
+ { name: "from", type: "address" },
831
+ { name: "to", type: "address" },
832
+ { name: "value", type: "uint256" },
833
+ { name: "validAfter", type: "uint256" },
834
+ { name: "validBefore", type: "uint256" },
835
+ { name: "nonce", type: "bytes32" }
836
+ ]
837
+ };
838
+ const value = ethers.ethers.parseUnits(paymentInfo.amount, chain.usdc.decimals);
839
+ const from = await this.signer.getAddress();
840
+ const to = ethers.ethers.getAddress(recipient);
841
+ const message = {
842
+ from,
843
+ to,
844
+ value,
845
+ validAfter,
846
+ validBefore,
847
+ nonce
848
+ };
849
+ this.log("Signing EIP-712 message...", { domain, message });
850
+ let signature;
851
+ try {
852
+ signature = await this.signer.signTypedData(domain, types, message);
853
+ } catch (error) {
854
+ if (error instanceof Error && (error.message.includes("User rejected") || error.code === 4001)) {
855
+ throw new X402Error("Signature rejected by user", "SIGNATURE_REJECTED");
856
+ }
857
+ throw new X402Error(
858
+ `Failed to sign payment: ${error instanceof Error ? error.message : "Unknown error"}`,
859
+ "PAYMENT_FAILED",
860
+ error
861
+ );
862
+ }
863
+ const sig = ethers.ethers.Signature.from(signature);
864
+ const payload = {
865
+ from,
866
+ to,
867
+ value: value.toString(),
868
+ validAfter,
869
+ validBefore,
870
+ nonce,
871
+ v: sig.v,
872
+ r: sig.r,
873
+ s: sig.s,
874
+ chainId: chain.chainId,
875
+ token: chain.usdc.address
876
+ };
877
+ const paymentHeader = this.encodeEVMPaymentHeader(payload, chain);
878
+ this.emit("paymentSigned", { paymentHeader });
879
+ const result = {
880
+ success: true,
881
+ paymentHeader,
882
+ network: chain.name,
883
+ payer: from
884
+ };
885
+ this.emit("paymentCompleted", result);
886
+ this.log("Payment created successfully", { network: chain.name, from });
887
+ return result;
888
+ }
889
+ encodeEVMPaymentHeader(payload, chain) {
890
+ const fullSignature = payload.r + payload.s.slice(2) + payload.v.toString(16).padStart(2, "0");
891
+ const x402Payload = {
892
+ x402Version: 1,
893
+ scheme: "exact",
894
+ network: chain.name,
895
+ payload: {
896
+ signature: fullSignature,
897
+ authorization: {
898
+ from: payload.from,
899
+ to: payload.to,
900
+ value: payload.value,
901
+ validAfter: payload.validAfter.toString(),
902
+ validBefore: payload.validBefore.toString(),
903
+ nonce: payload.nonce
904
+ }
905
+ }
906
+ };
907
+ const jsonString = JSON.stringify(x402Payload);
908
+ return btoa(jsonString);
909
+ }
910
+ // ============================================================================
911
+ // PRIVATE - EVM Balance Check
912
+ // ============================================================================
913
+ async getEVMBalance(chain) {
914
+ const publicProvider = new ethers.ethers.JsonRpcProvider(chain.rpcUrl);
915
+ const usdcAbi = ["function balanceOf(address owner) view returns (uint256)"];
916
+ const usdcContract = new ethers.ethers.Contract(chain.usdc.address, usdcAbi, publicProvider);
917
+ try {
918
+ const balance = await usdcContract.balanceOf(this.connectedAddress);
919
+ const formatted = ethers.ethers.formatUnits(balance, chain.usdc.decimals);
920
+ return parseFloat(formatted).toFixed(2);
921
+ } catch {
922
+ return "0.00";
923
+ }
924
+ }
925
+ // ============================================================================
926
+ // PRIVATE - Utilities
927
+ // ============================================================================
928
+ getRecipientForNetwork(paymentInfo, network) {
929
+ const lookupNetwork = network === "svm" ? "solana" : network;
930
+ const recipients = paymentInfo.recipients;
931
+ if (recipients?.[lookupNetwork]) {
932
+ return recipients[lookupNetwork];
933
+ }
934
+ return paymentInfo.recipient;
935
+ }
936
+ emit(event, data) {
937
+ this.eventHandlers.get(event)?.forEach((handler) => {
938
+ try {
939
+ handler(data);
940
+ } catch (error) {
941
+ console.error(`Error in ${event} handler:`, error);
942
+ }
943
+ });
944
+ }
945
+ log(message, data) {
946
+ if (this.config.debug) {
947
+ console.log(`[X402Client] ${message}`, data ?? "");
948
+ }
949
+ }
950
+ };
951
+ var X402Context = react.createContext(null);
952
+ function X402Provider({ children, config }) {
953
+ const [client] = react.useState(() => new X402Client(config));
954
+ const [state, setState] = react.useState(() => client.getState());
955
+ react.useEffect(() => {
956
+ const unsubConnect = client.on("connect", (newState) => {
957
+ setState(newState);
958
+ });
959
+ const unsubDisconnect = client.on("disconnect", () => {
960
+ setState(client.getState());
961
+ });
962
+ const unsubChainChanged = client.on("chainChanged", () => {
963
+ setState(client.getState());
964
+ });
965
+ const unsubAccountChanged = client.on("accountChanged", () => {
966
+ setState(client.getState());
967
+ });
968
+ return () => {
969
+ unsubConnect();
970
+ unsubDisconnect();
971
+ unsubChainChanged();
972
+ unsubAccountChanged();
973
+ };
974
+ }, [client]);
975
+ const connect = react.useCallback(
976
+ async (chainName) => {
977
+ const address = await client.connect(chainName);
978
+ setState(client.getState());
979
+ return address;
980
+ },
981
+ [client]
982
+ );
983
+ const disconnect = react.useCallback(async () => {
984
+ await client.disconnect();
985
+ setState(client.getState());
986
+ }, [client]);
987
+ const switchChain = react.useCallback(
988
+ async (chainName) => {
989
+ await client.switchChain(chainName);
990
+ setState(client.getState());
991
+ },
992
+ [client]
993
+ );
994
+ const getBalance = react.useCallback(() => client.getBalance(), [client]);
995
+ const value = react.useMemo(
996
+ () => ({
997
+ client,
998
+ state,
999
+ connect,
1000
+ disconnect,
1001
+ switchChain,
1002
+ getBalance
1003
+ }),
1004
+ [client, state, connect, disconnect, switchChain, getBalance]
1005
+ );
1006
+ return /* @__PURE__ */ jsxRuntime.jsx(X402Context.Provider, { value, children });
1007
+ }
1008
+ function useX402() {
1009
+ const context = react.useContext(X402Context);
1010
+ if (!context) {
1011
+ throw new Error("useX402 must be used within an X402Provider");
1012
+ }
1013
+ const { client, state, connect, disconnect, switchChain, getBalance } = context;
1014
+ return {
1015
+ // State
1016
+ isConnected: state.connected,
1017
+ address: state.address,
1018
+ chainId: state.chainId,
1019
+ network: state.network,
1020
+ networkType: state.networkType,
1021
+ // Methods
1022
+ connect,
1023
+ disconnect,
1024
+ switchChain,
1025
+ getBalance,
1026
+ // Client access for advanced usage
1027
+ client
1028
+ };
1029
+ }
1030
+ function useBalance() {
1031
+ const { client, isConnected, network } = useX402();
1032
+ const [balance, setBalance] = react.useState(null);
1033
+ const [isLoading, setIsLoading] = react.useState(false);
1034
+ const [error, setError] = react.useState(null);
1035
+ const fetchBalance = react.useCallback(async () => {
1036
+ if (!isConnected) {
1037
+ setBalance(null);
1038
+ return;
1039
+ }
1040
+ setIsLoading(true);
1041
+ setError(null);
1042
+ try {
1043
+ const bal = await client.getBalance();
1044
+ setBalance(bal);
1045
+ } catch (err) {
1046
+ setError(err instanceof Error ? err.message : "Failed to fetch balance");
1047
+ setBalance(null);
1048
+ } finally {
1049
+ setIsLoading(false);
1050
+ }
1051
+ }, [client, isConnected]);
1052
+ react.useEffect(() => {
1053
+ fetchBalance();
1054
+ }, [fetchBalance, network]);
1055
+ return {
1056
+ balance,
1057
+ isLoading,
1058
+ error,
1059
+ refetch: fetchBalance
1060
+ };
1061
+ }
1062
+ function usePayment() {
1063
+ const { client, isConnected } = useX402();
1064
+ const [isPaying, setIsPaying] = react.useState(false);
1065
+ const [error, setError] = react.useState(null);
1066
+ const [lastResult, setLastResult] = react.useState(null);
1067
+ const pay = react.useCallback(
1068
+ async (paymentInfo) => {
1069
+ if (!isConnected) {
1070
+ throw new Error("Wallet not connected");
1071
+ }
1072
+ setIsPaying(true);
1073
+ setError(null);
1074
+ try {
1075
+ const result = await client.createPayment(paymentInfo);
1076
+ setLastResult(result);
1077
+ return result;
1078
+ } catch (err) {
1079
+ const message = err instanceof Error ? err.message : "Payment failed";
1080
+ setError(message);
1081
+ throw err;
1082
+ } finally {
1083
+ setIsPaying(false);
1084
+ }
1085
+ },
1086
+ [client, isConnected]
1087
+ );
1088
+ const reset = react.useCallback(() => {
1089
+ setError(null);
1090
+ setLastResult(null);
1091
+ }, []);
1092
+ return {
1093
+ pay,
1094
+ isPaying,
1095
+ error,
1096
+ lastResult,
1097
+ reset
1098
+ };
1099
+ }
1100
+ function useChains() {
1101
+ const { network: currentNetwork } = useX402();
1102
+ const chains = react.useMemo(() => getEnabledChains(), []);
1103
+ const currentChain = react.useMemo(
1104
+ () => currentNetwork ? getChainByName(currentNetwork) : null,
1105
+ [currentNetwork]
1106
+ );
1107
+ const evmChains = react.useMemo(
1108
+ () => chains.filter((c) => c.networkType === "evm"),
1109
+ [chains]
1110
+ );
1111
+ const nonEvmChains = react.useMemo(
1112
+ () => chains.filter((c) => c.networkType !== "evm"),
1113
+ [chains]
1114
+ );
1115
+ return {
1116
+ chains,
1117
+ currentChain,
1118
+ evmChains,
1119
+ nonEvmChains,
1120
+ getChain: getChainByName
1121
+ };
1122
+ }
1123
+ function useNetworkBalances() {
1124
+ const { address, isConnected, networkType } = useX402();
1125
+ const [balances, setBalances] = react.useState(/* @__PURE__ */ new Map());
1126
+ const [isLoading, setIsLoading] = react.useState(false);
1127
+ const fetchAllBalances = react.useCallback(async () => {
1128
+ if (!isConnected || !address) {
1129
+ setBalances(/* @__PURE__ */ new Map());
1130
+ return;
1131
+ }
1132
+ setIsLoading(true);
1133
+ const chains = getEnabledChains();
1134
+ const compatibleChains = chains.filter((chain) => {
1135
+ if (address.startsWith("0x")) {
1136
+ return chain.networkType === "evm";
1137
+ }
1138
+ return chain.networkType === networkType;
1139
+ });
1140
+ const newBalances = /* @__PURE__ */ new Map();
1141
+ compatibleChains.forEach((chain) => {
1142
+ newBalances.set(chain.name, {
1143
+ chainName: chain.name,
1144
+ displayName: chain.displayName,
1145
+ balance: null,
1146
+ isLoading: true,
1147
+ error: null
1148
+ });
1149
+ });
1150
+ setBalances(new Map(newBalances));
1151
+ await Promise.allSettled(
1152
+ compatibleChains.map(async (chain) => {
1153
+ try {
1154
+ const provider = new (await import('ethers')).JsonRpcProvider(chain.rpcUrl);
1155
+ const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
1156
+ const contract = new (await import('ethers')).Contract(
1157
+ chain.usdc.address,
1158
+ usdcAbi,
1159
+ provider
1160
+ );
1161
+ const balance = await contract.balanceOf(address);
1162
+ const formatted = parseFloat(
1163
+ (await import('ethers')).formatUnits(balance, chain.usdc.decimals)
1164
+ ).toFixed(2);
1165
+ newBalances.set(chain.name, {
1166
+ chainName: chain.name,
1167
+ displayName: chain.displayName,
1168
+ balance: formatted,
1169
+ isLoading: false,
1170
+ error: null
1171
+ });
1172
+ } catch (err) {
1173
+ newBalances.set(chain.name, {
1174
+ chainName: chain.name,
1175
+ displayName: chain.displayName,
1176
+ balance: null,
1177
+ isLoading: false,
1178
+ error: err instanceof Error ? err.message : "Failed"
1179
+ });
1180
+ }
1181
+ setBalances(new Map(newBalances));
1182
+ })
1183
+ );
1184
+ setIsLoading(false);
1185
+ }, [address, isConnected, networkType]);
1186
+ react.useEffect(() => {
1187
+ fetchAllBalances();
1188
+ }, [fetchAllBalances]);
1189
+ const highestBalanceNetwork = react.useMemo(() => {
1190
+ let maxBalance = 0;
1191
+ let maxChain = null;
1192
+ balances.forEach((nb, chainName) => {
1193
+ if (nb.balance !== null && !nb.error) {
1194
+ const bal = parseFloat(nb.balance);
1195
+ if (bal > maxBalance) {
1196
+ maxBalance = bal;
1197
+ maxChain = chainName;
1198
+ }
1199
+ }
1200
+ });
1201
+ return maxChain;
1202
+ }, [balances]);
1203
+ return {
1204
+ balances,
1205
+ isLoading,
1206
+ refetch: fetchAllBalances,
1207
+ highestBalanceNetwork
1208
+ };
1209
+ }
1210
+
1211
+ exports.X402Provider = X402Provider;
1212
+ exports.useBalance = useBalance;
1213
+ exports.useChains = useChains;
1214
+ exports.useNetworkBalances = useNetworkBalances;
1215
+ exports.usePayment = usePayment;
1216
+ exports.useX402 = useX402;
1217
+ //# sourceMappingURL=index.js.map
1218
+ //# sourceMappingURL=index.js.map