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