stellar-wallet-kit 2.0.2 → 2.0.5

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 CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var React3 = require('react');
4
4
  var freighterApi = require('@stellar/freighter-api');
5
+ var appkitUniversalConnector = require('@reown/appkit-universal-connector');
5
6
 
6
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
8
 
@@ -13,6 +14,8 @@ var React3__default = /*#__PURE__*/_interopDefault(React3);
13
14
  var WalletType = /* @__PURE__ */ ((WalletType2) => {
14
15
  WalletType2["FREIGHTER"] = "freighter";
15
16
  WalletType2["ALBEDO"] = "albedo";
17
+ WalletType2["WALLETCONNECT"] = "walletconnect";
18
+ WalletType2["LOBSTR"] = "lobstr";
16
19
  return WalletType2;
17
20
  })(WalletType || {});
18
21
  var NetworkType = /* @__PURE__ */ ((NetworkType2) => {
@@ -118,66 +121,6 @@ var FreighterAdapter = class {
118
121
  }
119
122
  };
120
123
 
121
- // src/utils/balanceUtils.ts
122
- var HORIZON_URLS = {
123
- PUBLIC: "https://horizon.stellar.org",
124
- TESTNET: "https://horizon-testnet.stellar.org",
125
- FUTURENET: "https://horizon-futurenet.stellar.org",
126
- STANDALONE: "http://localhost:8000"
127
- };
128
- async function fetchAccountBalances(publicKey, network) {
129
- const horizonUrl = HORIZON_URLS[network];
130
- try {
131
- const response = await fetch(`${horizonUrl}/accounts/${publicKey}`);
132
- if (!response.ok) {
133
- if (response.status === 404) {
134
- throw new Error("Account not found. Make sure the account is funded.");
135
- }
136
- throw new Error(`Failed to fetch account: ${response.statusText}`);
137
- }
138
- const data = await response.json();
139
- return data.balances;
140
- } catch (error) {
141
- console.error("Error fetching balances:", error);
142
- throw error;
143
- }
144
- }
145
- function getNativeBalance(balances) {
146
- const nativeBalance = balances.find((b) => b.asset_type === "native");
147
- return nativeBalance?.balance || "0";
148
- }
149
- function formatBalance(balance, decimals = 7) {
150
- const num = parseFloat(balance);
151
- if (isNaN(num)) return "0";
152
- return num.toFixed(decimals).replace(/\.?0+$/, "");
153
- }
154
- function getAssetBalance(balances, assetCode, assetIssuer) {
155
- if (!assetCode) {
156
- return getNativeBalance(balances);
157
- }
158
- const asset = balances.find(
159
- (b) => b.asset_code === assetCode && (!assetIssuer || b.asset_issuer === assetIssuer)
160
- );
161
- return asset?.balance || "0";
162
- }
163
- function hasSufficientBalance(balances, requiredAmount, assetCode, assetIssuer) {
164
- const balance = getAssetBalance(balances, assetCode, assetIssuer);
165
- return parseFloat(balance) >= parseFloat(requiredAmount);
166
- }
167
- function getTotalValueXLM(balances) {
168
- return getNativeBalance(balances);
169
- }
170
- function groupBalancesByType(balances) {
171
- return balances.reduce((acc, balance) => {
172
- const type = balance.asset_type;
173
- if (!acc[type]) {
174
- acc[type] = [];
175
- }
176
- acc[type].push(balance);
177
- return acc;
178
- }, {});
179
- }
180
-
181
124
  // src/utils/albedoCallback.ts
182
125
  function openAlbedoPopup(url) {
183
126
  const popup = window.open(
@@ -297,39 +240,75 @@ var AlbedoAdapter = class {
297
240
  }
298
241
  };
299
242
 
243
+ // src/utils/balanceUtils.ts
244
+ var HORIZON_URLS = {
245
+ PUBLIC: "https://horizon.stellar.org",
246
+ TESTNET: "https://horizon-testnet.stellar.org",
247
+ FUTURENET: "https://horizon-futurenet.stellar.org",
248
+ STANDALONE: "http://localhost:8000"
249
+ };
250
+ async function fetchAccountBalances(publicKey, network) {
251
+ const horizonUrl = HORIZON_URLS[network];
252
+ try {
253
+ const response = await fetch(`${horizonUrl}/accounts/${publicKey}`);
254
+ if (!response.ok) {
255
+ if (response.status === 404) {
256
+ throw new Error("Account not found. Make sure the account is funded.");
257
+ }
258
+ throw new Error(`Failed to fetch account: ${response.statusText}`);
259
+ }
260
+ const data = await response.json();
261
+ return data.balances;
262
+ } catch (error) {
263
+ console.error("Error fetching balances:", error);
264
+ throw error;
265
+ }
266
+ }
267
+ function getNativeBalance(balances) {
268
+ const nativeBalance = balances.find((b) => b.asset_type === "native");
269
+ return nativeBalance?.balance || "0";
270
+ }
271
+ function formatBalance(balance, decimals = 7) {
272
+ const num = parseFloat(balance);
273
+ if (isNaN(num)) return "0";
274
+ return num.toFixed(decimals).replace(/\.?0+$/, "");
275
+ }
276
+ function getAssetBalance(balances, assetCode, assetIssuer) {
277
+ if (!assetCode) {
278
+ return getNativeBalance(balances);
279
+ }
280
+ const asset = balances.find(
281
+ (b) => b.asset_code === assetCode && (!assetIssuer || b.asset_issuer === assetIssuer)
282
+ );
283
+ return asset?.balance || "0";
284
+ }
285
+ function hasSufficientBalance(balances, requiredAmount, assetCode, assetIssuer) {
286
+ const balance = getAssetBalance(balances, assetCode, assetIssuer);
287
+ return parseFloat(balance) >= parseFloat(requiredAmount);
288
+ }
289
+ function getTotalValueXLM(balances) {
290
+ return getNativeBalance(balances);
291
+ }
292
+ function groupBalancesByType(balances) {
293
+ return balances.reduce((acc, balance) => {
294
+ const type = balance.asset_type;
295
+ if (!acc[type]) {
296
+ acc[type] = [];
297
+ }
298
+ acc[type].push(balance);
299
+ return acc;
300
+ }, {});
301
+ }
302
+
300
303
  // src/context/WalletContext.tsx
304
+ var STORAGE_KEY = "stellar_wallet_kit";
305
+ var isBrowser = typeof window !== "undefined";
301
306
  var DEFAULT_SUPPORTS = {
302
307
  silentReconnect: false,
303
308
  networkDetection: false,
304
309
  authEntrySigning: false
305
310
  };
306
311
  var WalletContext = React3.createContext(void 0);
307
- var STORAGE_KEY = "stellar_wallet_kit";
308
- var getStorageData = () => {
309
- if (typeof window === "undefined") {
310
- return { selectedWallet: null, autoConnect: false };
311
- }
312
- try {
313
- const data = localStorage.getItem(STORAGE_KEY);
314
- return data ? JSON.parse(data) : { selectedWallet: null, autoConnect: false };
315
- } catch {
316
- return { selectedWallet: null, autoConnect: false };
317
- }
318
- };
319
- var setStorageData = (data) => {
320
- if (typeof window === "undefined") return;
321
- try {
322
- const existing = getStorageData();
323
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ ...existing, ...data }));
324
- } catch (error) {
325
- console.error("Failed to save to localStorage:", error);
326
- }
327
- };
328
- var isBrowser = typeof window !== "undefined";
329
- var walletAdapters = {
330
- ["freighter" /* FREIGHTER */]: new FreighterAdapter(),
331
- ["albedo" /* ALBEDO */]: new AlbedoAdapter()
332
- };
333
312
  var walletMetadata = {
334
313
  ["freighter" /* FREIGHTER */]: {
335
314
  id: "freighter" /* FREIGHTER */,
@@ -355,11 +334,55 @@ var walletMetadata = {
355
334
  networkDetection: false,
356
335
  authEntrySigning: true
357
336
  }
337
+ },
338
+ ["walletconnect" /* WALLETCONNECT */]: {
339
+ id: "walletconnect" /* WALLETCONNECT */,
340
+ name: "WalletConnect",
341
+ icon: "https://walletconnect.com/walletconnect-logo.png",
342
+ description: "Connect mobile wallets via WalletConnect",
343
+ kind: "web",
344
+ capabilities: {
345
+ silentReconnect: true,
346
+ networkDetection: false,
347
+ authEntrySigning: false
348
+ }
349
+ },
350
+ ["lobstr" /* LOBSTR */]: {
351
+ id: "lobstr" /* LOBSTR */,
352
+ name: "LOBSTR",
353
+ icon: "https://lobstr.co/favicon.ico",
354
+ description: "LOBSTR mobile wallet",
355
+ kind: "web",
356
+ capabilities: {
357
+ silentReconnect: true,
358
+ networkDetection: false,
359
+ authEntrySigning: false
360
+ }
358
361
  }
359
362
  };
363
+ function getStorageData() {
364
+ if (!isBrowser) {
365
+ return { selectedWallet: null, autoConnect: false };
366
+ }
367
+ try {
368
+ const raw = localStorage.getItem(STORAGE_KEY);
369
+ return raw ? JSON.parse(raw) : { selectedWallet: null, autoConnect: false };
370
+ } catch {
371
+ return { selectedWallet: null, autoConnect: false };
372
+ }
373
+ }
374
+ function setStorageData(data) {
375
+ if (!isBrowser) return;
376
+ const existing = getStorageData();
377
+ localStorage.setItem(
378
+ STORAGE_KEY,
379
+ JSON.stringify({ ...existing, ...data })
380
+ );
381
+ }
360
382
  function WalletProvider({ config = {}, children }) {
361
383
  const [account, setAccount] = React3.useState(null);
362
384
  const [isConnecting, setIsConnecting] = React3.useState(false);
385
+ const [connectingWallet, setConnectingWallet] = React3.useState(null);
363
386
  const [error, setError] = React3.useState(null);
364
387
  const [network, setNetwork] = React3.useState(
365
388
  config.network || "TESTNET" /* TESTNET */
@@ -368,164 +391,138 @@ function WalletProvider({ config = {}, children }) {
368
391
  const [availableWallets, setAvailableWallets] = React3.useState([]);
369
392
  const [isLoadingBalances, setIsLoadingBalances] = React3.useState(false);
370
393
  const isConnected2 = !!account;
394
+ const walletAdapters = React3.useMemo(
395
+ () => ({
396
+ ["freighter" /* FREIGHTER */]: new FreighterAdapter(),
397
+ ["albedo" /* ALBEDO */]: new AlbedoAdapter(),
398
+ ...config.adapters ?? {}
399
+ }),
400
+ [config.adapters]
401
+ );
371
402
  React3.useEffect(() => {
372
403
  if (!isBrowser) return;
373
- const checkWallets = async () => {
404
+ (async () => {
374
405
  const wallets = [];
375
- for (const [type, adapter] of Object.entries(walletAdapters)) {
376
- const walletType = type;
406
+ const entries = Object.entries(walletAdapters);
407
+ for (const [walletType, adapter] of entries) {
408
+ if (!adapter) continue;
377
409
  const meta = walletMetadata[walletType];
378
410
  let installed = true;
379
411
  if (meta.kind === "extension") {
380
412
  installed = await adapter.isAvailable();
381
413
  }
382
- wallets.push({
383
- ...meta,
384
- installed
385
- });
414
+ wallets.push({ ...meta, installed });
386
415
  }
387
416
  setAvailableWallets(wallets);
388
- };
389
- checkWallets();
390
- }, []);
417
+ })();
418
+ }, [walletAdapters]);
391
419
  React3.useEffect(() => {
392
- if (!isBrowser) return;
393
- const autoConnectWallet = async () => {
394
- const storage = getStorageData();
395
- if (config.autoConnect && storage.selectedWallet) {
396
- try {
397
- await connect(storage.selectedWallet);
398
- } catch (err) {
399
- console.error("Auto-connect failed:", err);
400
- }
401
- }
402
- };
403
- autoConnectWallet();
420
+ if (!isBrowser || !config.autoConnect) return;
421
+ const { selectedWallet: selectedWallet2 } = getStorageData();
422
+ if (selectedWallet2) {
423
+ connect(selectedWallet2).catch(() => {
424
+ });
425
+ }
404
426
  }, [config.autoConnect]);
405
427
  const refreshBalances = React3.useCallback(async () => {
406
- if (!account?.publicKey) {
407
- return;
408
- }
428
+ if (!account?.publicKey) return;
409
429
  setIsLoadingBalances(true);
410
430
  try {
411
- const balances = await fetchAccountBalances(account.publicKey, network);
431
+ const balances = await fetchAccountBalances(
432
+ account.publicKey,
433
+ network
434
+ );
412
435
  setAccount((prev) => prev ? { ...prev, balances } : null);
413
- } catch (err) {
414
- console.error("Failed to fetch balances:", err);
415
436
  } finally {
416
437
  setIsLoadingBalances(false);
417
438
  }
418
439
  }, [account?.publicKey, network]);
419
440
  const connect = React3.useCallback(
420
441
  async (walletType) => {
442
+ const type = walletType || config.defaultWallet || "freighter" /* FREIGHTER */;
421
443
  setIsConnecting(true);
444
+ setConnectingWallet(type);
422
445
  setError(null);
423
446
  try {
424
- const typeToConnect = walletType || config.defaultWallet || "freighter" /* FREIGHTER */;
425
- const adapter = walletAdapters[typeToConnect];
447
+ const adapter = walletAdapters[type];
426
448
  if (!adapter) {
427
- throw new Error(`Wallet adapter not found for ${typeToConnect}`);
449
+ throw new Error(`Wallet adapter not configured: ${type}`);
428
450
  }
429
- const meta = walletMetadata[typeToConnect];
451
+ const meta = walletMetadata[type];
430
452
  if (meta.kind === "extension") {
431
- const available = await adapter.isAvailable();
432
- if (!available) {
453
+ const ok = await adapter.isAvailable();
454
+ if (!ok) {
433
455
  throw new Error(`${meta.name} is not installed`);
434
456
  }
435
457
  }
436
- const response = await adapter.connect();
437
- const newAccount = {
438
- address: response.address,
439
- publicKey: response.publicKey,
440
- displayName: `${response.address.slice(
441
- 0,
442
- 4
443
- )}...${response.address.slice(-4)}`
444
- };
445
- setAccount(newAccount);
446
- setSelectedWallet(typeToConnect);
447
- setStorageData({ selectedWallet: typeToConnect, autoConnect: true });
448
- try {
449
- const balances = await fetchAccountBalances(
450
- response.publicKey,
451
- network
452
- );
453
- setAccount((prev) => prev ? { ...prev, balances } : null);
454
- } catch (balanceError) {
455
- console.error("Failed to fetch initial balances:", balanceError);
456
- }
457
- } catch (err) {
458
- const error2 = err instanceof Error ? err : new Error("Failed to connect wallet");
459
- setError(error2);
460
- throw error2;
458
+ const res = await adapter.connect();
459
+ setAccount({
460
+ address: res.address,
461
+ publicKey: res.publicKey,
462
+ displayName: `${res.address.slice(0, 4)}\u2026${res.address.slice(-4)}`
463
+ });
464
+ setSelectedWallet(type);
465
+ setStorageData({ selectedWallet: type, autoConnect: true });
466
+ await refreshBalances();
467
+ } catch (e) {
468
+ setError(e);
469
+ throw e;
461
470
  } finally {
462
471
  setIsConnecting(false);
472
+ setConnectingWallet(null);
463
473
  }
464
474
  },
465
- [config.defaultWallet]
475
+ [config.defaultWallet, refreshBalances, walletAdapters]
466
476
  );
467
477
  const disconnect = React3.useCallback(async () => {
468
- try {
469
- if (selectedWallet) {
470
- const adapter = walletAdapters[selectedWallet];
471
- await adapter.disconnect();
472
- }
473
- setAccount(null);
474
- setSelectedWallet(null);
475
- setError(null);
476
- setStorageData({ selectedWallet: null, autoConnect: false });
477
- } catch (err) {
478
- const error2 = err instanceof Error ? err : new Error("Failed to disconnect wallet");
479
- setError(error2);
480
- throw error2;
478
+ if (selectedWallet) {
479
+ await walletAdapters[selectedWallet]?.disconnect();
481
480
  }
482
- }, [selectedWallet]);
481
+ setAccount(null);
482
+ setSelectedWallet(null);
483
+ setError(null);
484
+ setStorageData({ selectedWallet: null, autoConnect: false });
485
+ }, [selectedWallet, walletAdapters]);
483
486
  const signTransaction2 = React3.useCallback(
484
- async (xdr, options) => {
487
+ (xdr, options) => {
485
488
  if (!selectedWallet) {
486
489
  throw new Error("No wallet connected");
487
490
  }
488
491
  const adapter = walletAdapters[selectedWallet];
492
+ if (!adapter) {
493
+ throw new Error("Wallet adapter missing");
494
+ }
489
495
  return adapter.signTransaction(xdr, options);
490
496
  },
491
- [selectedWallet]
497
+ [selectedWallet, walletAdapters]
492
498
  );
493
499
  const signAuthEntry2 = React3.useCallback(
494
- async (entryXdr, options) => {
500
+ (entryXdr, options) => {
495
501
  if (!selectedWallet) {
496
502
  throw new Error("No wallet connected");
497
503
  }
498
504
  const adapter = walletAdapters[selectedWallet];
505
+ if (!adapter) {
506
+ throw new Error("Wallet adapter missing");
507
+ }
499
508
  return adapter.signAuthEntry(entryXdr, options);
500
509
  },
501
- [selectedWallet]
510
+ [selectedWallet, walletAdapters]
502
511
  );
503
512
  const switchNetwork = React3.useCallback(
504
- async (newNetwork) => {
505
- setNetwork(newNetwork);
506
- if (account?.publicKey) {
507
- await refreshBalances();
508
- }
513
+ async (n) => {
514
+ setNetwork(n);
515
+ await refreshBalances();
509
516
  },
510
- [account?.publicKey, refreshBalances]
517
+ [refreshBalances]
511
518
  );
512
- React3.useEffect(() => {
513
- if (!account?.publicKey || !isBrowser) return;
514
- refreshBalances();
515
- const interval = setInterval(() => {
516
- refreshBalances();
517
- }, 3e4);
518
- return () => clearInterval(interval);
519
- }, [account?.publicKey, refreshBalances]);
520
519
  const supports = React3.useMemo(() => {
521
- if (!selectedWallet) {
522
- return DEFAULT_SUPPORTS;
523
- }
524
- const meta = walletMetadata[selectedWallet];
520
+ if (!selectedWallet) return DEFAULT_SUPPORTS;
521
+ const caps = walletMetadata[selectedWallet].capabilities;
525
522
  return {
526
- silentReconnect: !!meta.capabilities?.silentReconnect,
527
- networkDetection: !!meta.capabilities?.networkDetection,
528
- authEntrySigning: !!meta.capabilities?.authEntrySigning
523
+ silentReconnect: caps?.silentReconnect ?? false,
524
+ networkDetection: caps?.networkDetection ?? false,
525
+ authEntrySigning: caps?.authEntrySigning ?? false
529
526
  };
530
527
  }, [selectedWallet]);
531
528
  const value = React3.useMemo(
@@ -533,6 +530,7 @@ function WalletProvider({ config = {}, children }) {
533
530
  account,
534
531
  isConnected: isConnected2,
535
532
  isConnecting,
533
+ connectingWallet,
536
534
  error,
537
535
  network,
538
536
  selectedWallet,
@@ -550,6 +548,7 @@ function WalletProvider({ config = {}, children }) {
550
548
  account,
551
549
  isConnected2,
552
550
  isConnecting,
551
+ connectingWallet,
553
552
  error,
554
553
  network,
555
554
  selectedWallet,
@@ -567,11 +566,11 @@ function WalletProvider({ config = {}, children }) {
567
566
  return /* @__PURE__ */ React3__default.default.createElement(WalletContext.Provider, { value }, children);
568
567
  }
569
568
  function useWallet() {
570
- const context = React3.useContext(WalletContext);
571
- if (context === void 0) {
569
+ const ctx = React3.useContext(WalletContext);
570
+ if (!ctx) {
572
571
  throw new Error("useWallet must be used within a WalletProvider");
573
572
  }
574
- return context;
573
+ return ctx;
575
574
  }
576
575
  function WalletModal({
577
576
  isOpen,
@@ -583,216 +582,149 @@ function WalletModal({
583
582
  appIcon
584
583
  }) {
585
584
  React3.useEffect(() => {
586
- const handleEsc = (e) => {
585
+ const onEsc = (e) => {
587
586
  if (e.key === "Escape") onClose();
588
587
  };
589
588
  if (isOpen) {
590
- document.addEventListener("keydown", handleEsc);
589
+ document.addEventListener("keydown", onEsc);
591
590
  document.body.style.overflow = "hidden";
592
591
  }
593
592
  return () => {
594
- document.removeEventListener("keydown", handleEsc);
595
- document.body.style.overflow = "unset";
593
+ document.removeEventListener("keydown", onEsc);
594
+ document.body.style.overflow = "auto";
596
595
  };
597
596
  }, [isOpen, onClose]);
598
597
  if (!isOpen) return null;
599
- const themeMode = theme?.mode || "light";
600
- const isDark = themeMode === "dark" || themeMode === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
601
- const styles = {
602
- overlay: {
603
- position: "fixed",
604
- top: 0,
605
- left: 0,
606
- right: 0,
607
- bottom: 0,
608
- backgroundColor: theme?.overlayBackground || (isDark ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.5)"),
609
- display: "flex",
610
- alignItems: "center",
611
- justifyContent: "center",
612
- zIndex: 9999,
613
- animation: "swk-fade-in 0.2s ease-out"
614
- },
615
- modal: {
616
- backgroundColor: theme?.modalBackground || (isDark ? "#1a1a1a" : "#ffffff"),
617
- borderRadius: theme?.borderRadius || "16px",
618
- padding: "24px",
619
- maxWidth: "420px",
620
- width: "90%",
621
- maxHeight: "80vh",
622
- overflowY: "auto",
623
- boxShadow: isDark ? "0 8px 32px rgba(0, 0, 0, 0.4)" : "0 8px 32px rgba(0, 0, 0, 0.1)",
624
- animation: "swk-slide-up 0.3s ease-out",
625
- fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
626
- },
627
- header: {
628
- display: "flex",
629
- alignItems: "center",
630
- justifyContent: "space-between",
631
- marginBottom: "24px"
632
- },
633
- title: {
634
- fontSize: "20px",
635
- fontWeight: 600,
636
- color: theme?.textColor || (isDark ? "#ffffff" : "#000000"),
637
- margin: 0,
638
- display: "flex",
639
- alignItems: "center",
640
- gap: "12px"
641
- },
642
- closeButton: {
643
- background: "none",
644
- border: "none",
645
- fontSize: "24px",
646
- cursor: "pointer",
647
- padding: "4px",
648
- color: theme?.textColor || (isDark ? "#888888" : "#666666"),
649
- transition: "color 0.2s",
650
- lineHeight: 1
651
- },
652
- walletList: {
653
- display: "flex",
654
- flexDirection: "column",
655
- gap: "12px"
656
- },
657
- walletButton: {
658
- display: "flex",
659
- alignItems: "center",
660
- gap: "16px",
661
- padding: "16px",
662
- border: `1px solid ${isDark ? "#333333" : "#e5e5e5"}`,
663
- borderRadius: theme?.borderRadius || "12px",
664
- background: "none",
665
- cursor: "pointer",
666
- transition: "all 0.2s",
667
- width: "100%",
668
- textAlign: "left",
669
- fontFamily: "inherit"
670
- },
671
- walletIcon: {
672
- width: "40px",
673
- height: "40px",
674
- borderRadius: "8px",
675
- flexShrink: 0
676
- },
677
- walletInfo: {
678
- flex: 1
679
- },
680
- walletName: {
681
- fontSize: "16px",
682
- fontWeight: 500,
683
- color: theme?.textColor || (isDark ? "#ffffff" : "#000000"),
684
- margin: 0,
685
- marginBottom: "4px"
686
- },
687
- walletDescription: {
688
- fontSize: "13px",
689
- color: theme?.textColor || (isDark ? "#888888" : "#666666"),
690
- margin: 0
691
- },
692
- badge: {
693
- padding: "4px 8px",
694
- borderRadius: "6px",
695
- fontSize: "12px",
696
- fontWeight: 500,
697
- flexShrink: 0
698
- },
699
- installedBadge: {
700
- backgroundColor: theme?.primaryColor || (isDark ? "#4CAF50" : "#4CAF50"),
701
- color: "#ffffff"
702
- },
703
- notInstalledBadge: {
704
- backgroundColor: isDark ? "#333333" : "#f0f0f0",
705
- color: isDark ? "#888888" : "#666666"
706
- },
707
- footer: {
708
- marginTop: "24px",
709
- paddingTop: "16px",
710
- borderTop: `1px solid ${isDark ? "#333333" : "#e5e5e5"}`,
711
- fontSize: "13px",
712
- color: isDark ? "#888888" : "#666666",
713
- textAlign: "center"
714
- },
715
- link: {
716
- color: theme?.primaryColor || "#8b5cf6",
717
- textDecoration: "none"
718
- }
719
- };
720
- const handleWalletClick = (wallet) => {
721
- if (wallet.installed) {
722
- onSelectWallet(wallet.id);
723
- } else if (wallet.downloadUrl) {
724
- window.open(wallet.downloadUrl, "_blank");
725
- }
726
- };
598
+ const isDark = theme?.mode === "dark" || theme?.mode === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
599
+ const bg = theme?.modalBackground || (isDark ? "#0f0f11" : "#ffffff");
600
+ const text = theme?.textColor || (isDark ? "#ffffff" : "#0a0a0a");
601
+ const muted = isDark ? "#8b8b8b" : "#6b6b6b";
602
+ const border = isDark ? "#1f1f23" : "#eaeaea";
603
+ const hover = theme?.buttonHoverColor || (isDark ? "#1a1a1f" : "#f6f6f6");
727
604
  return /* @__PURE__ */ React3__default.default.createElement(React3__default.default.Fragment, null, /* @__PURE__ */ React3__default.default.createElement("style", null, `
728
- @keyframes swk-fade-in {
729
- from { opacity: 0; }
730
- to { opacity: 1; }
605
+ .swk-overlay {
606
+ position: fixed;
607
+ inset: 0;
608
+ background: rgba(0,0,0,0.55);
609
+ display: flex;
610
+ align-items: center;
611
+ justify-content: center;
612
+ z-index: 9999;
731
613
  }
732
- @keyframes swk-slide-up {
733
- from {
734
- opacity: 0;
735
- transform: translateY(20px);
736
- }
737
- to {
738
- opacity: 1;
739
- transform: translateY(0);
740
- }
614
+
615
+ .swk-modal {
616
+ width: 420px;
617
+ max-width: 92vw;
618
+ background: ${bg};
619
+ border-radius: 20px;
620
+ padding: 28px;
621
+ box-shadow: 0 20px 60px rgba(0,0,0,0.4);
622
+ font-family: ${theme?.fontFamily || "Inter, system-ui, sans-serif"};
741
623
  }
742
- .swk-wallet-button:hover {
743
- background-color: ${theme?.buttonHoverColor || (isDark ? "#252525" : "#f5f5f5")} !important;
744
- border-color: ${theme?.primaryColor || "#8b5cf6"} !important;
624
+
625
+ .swk-wallet {
626
+ display: flex;
627
+ align-items: center;
628
+ gap: 14px;
629
+ padding: 14px 16px;
630
+ border-radius: 14px;
631
+ cursor: pointer;
632
+ transition: background 0.15s ease;
745
633
  }
746
- .swk-close-button:hover {
747
- color: ${theme?.primaryColor || "#8b5cf6"} !important;
634
+
635
+ .swk-wallet:hover {
636
+ background: ${hover};
748
637
  }
749
- `), /* @__PURE__ */ React3__default.default.createElement("div", { style: styles.overlay, onClick: onClose }, /* @__PURE__ */ React3__default.default.createElement("div", { style: styles.modal, onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React3__default.default.createElement("div", { style: styles.header }, /* @__PURE__ */ React3__default.default.createElement("h2", { style: styles.title }, appIcon && /* @__PURE__ */ React3__default.default.createElement("img", { src: appIcon, alt: "", style: { width: "32px", height: "32px", borderRadius: "8px" } }), "Connect Wallet"), /* @__PURE__ */ React3__default.default.createElement(
750
- "button",
638
+
639
+ .swk-pill {
640
+ font-size: 12px;
641
+ padding: 4px 10px;
642
+ border-radius: 999px;
643
+ background: ${theme?.primaryColor || "#6c5ce7"};
644
+ color: white;
645
+ font-weight: 500;
646
+ }
647
+ `), /* @__PURE__ */ React3__default.default.createElement("div", { className: "swk-overlay", onClick: onClose }, /* @__PURE__ */ React3__default.default.createElement("div", { className: "swk-modal", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginBottom: 24 } }, /* @__PURE__ */ React3__default.default.createElement(
648
+ "div",
751
649
  {
752
- className: "swk-close-button",
753
- style: styles.closeButton,
754
- onClick: onClose,
755
- "aria-label": "Close modal"
650
+ style: {
651
+ display: "flex",
652
+ alignItems: "center",
653
+ gap: 12,
654
+ marginBottom: 8
655
+ }
756
656
  },
757
- "\xD7"
758
- )), /* @__PURE__ */ React3__default.default.createElement("div", { style: styles.walletList }, wallets.map((wallet) => /* @__PURE__ */ React3__default.default.createElement(
759
- "button",
657
+ appIcon && /* @__PURE__ */ React3__default.default.createElement(
658
+ "img",
659
+ {
660
+ src: appIcon,
661
+ alt: "",
662
+ style: { width: 32, height: 32, borderRadius: 8 }
663
+ }
664
+ ),
665
+ /* @__PURE__ */ React3__default.default.createElement("span", { style: { color: muted, fontSize: 14 } }, "Log in or sign up")
666
+ ), /* @__PURE__ */ React3__default.default.createElement(
667
+ "h2",
668
+ {
669
+ style: {
670
+ margin: 0,
671
+ fontSize: 22,
672
+ fontWeight: 600,
673
+ color: text
674
+ }
675
+ },
676
+ appName
677
+ )), /* @__PURE__ */ React3__default.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8 } }, wallets.map((wallet, i) => /* @__PURE__ */ React3__default.default.createElement(
678
+ "div",
760
679
  {
761
680
  key: wallet.id,
762
- className: "swk-wallet-button",
763
- style: styles.walletButton,
764
- onClick: () => handleWalletClick(wallet),
765
- disabled: !wallet.installed && !wallet.downloadUrl
681
+ className: "swk-wallet",
682
+ onClick: () => onSelectWallet(wallet.id)
766
683
  },
767
684
  /* @__PURE__ */ React3__default.default.createElement(
768
685
  "img",
769
686
  {
770
687
  src: wallet.icon,
771
- alt: `${wallet.name} icon`,
772
- style: styles.walletIcon
688
+ alt: wallet.name,
689
+ style: { width: 36, height: 36, borderRadius: 10 }
773
690
  }
774
691
  ),
775
- /* @__PURE__ */ React3__default.default.createElement("div", { style: styles.walletInfo }, /* @__PURE__ */ React3__default.default.createElement("p", { style: styles.walletName }, wallet.name), wallet.description && /* @__PURE__ */ React3__default.default.createElement("p", { style: styles.walletDescription }, wallet.description)),
776
- /* @__PURE__ */ React3__default.default.createElement(
692
+ /* @__PURE__ */ React3__default.default.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React3__default.default.createElement(
777
693
  "div",
778
694
  {
779
695
  style: {
780
- ...styles.badge,
781
- ...wallet.installed ? styles.installedBadge : styles.notInstalledBadge
696
+ fontSize: 15,
697
+ fontWeight: 500,
698
+ color: text
782
699
  }
783
700
  },
784
- wallet.installed ? "Installed" : "Get"
785
- )
786
- ))), /* @__PURE__ */ React3__default.default.createElement("div", { style: styles.footer }, /* @__PURE__ */ React3__default.default.createElement("p", { style: { margin: 0 } }, "Don't have a wallet?", " ", /* @__PURE__ */ React3__default.default.createElement(
787
- "a",
701
+ wallet.name
702
+ ), wallet.description && /* @__PURE__ */ React3__default.default.createElement(
703
+ "div",
704
+ {
705
+ style: {
706
+ fontSize: 13,
707
+ color: muted,
708
+ marginTop: 2
709
+ }
710
+ },
711
+ wallet.description
712
+ )),
713
+ i === 0 && /* @__PURE__ */ React3__default.default.createElement("div", { className: "swk-pill" }, "Recent")
714
+ ))), /* @__PURE__ */ React3__default.default.createElement(
715
+ "div",
788
716
  {
789
- href: "https://www.stellar.org/ecosystem/projects#wallets",
790
- target: "_blank",
791
- rel: "noopener noreferrer",
792
- style: styles.link
717
+ style: {
718
+ marginTop: 24,
719
+ paddingTop: 16,
720
+ borderTop: `1px solid ${border}`,
721
+ textAlign: "center",
722
+ fontSize: 13,
723
+ color: muted
724
+ }
793
725
  },
794
- "Learn more"
795
- ))))));
726
+ "Powered by Stellar Wallet Kit"
727
+ ))));
796
728
  }
797
729
 
798
730
  // src/components/ConnectButton.tsx
@@ -1006,13 +938,132 @@ function ConnectButton({
1006
938
  "\u{1F6AA} Disconnect"
1007
939
  )))));
1008
940
  }
941
+ var stellarTestnet = {
942
+ id: "testnet",
943
+ chainNamespace: "stellar",
944
+ caipNetworkId: "stellar:testnet",
945
+ name: "Stellar Testnet",
946
+ nativeCurrency: {
947
+ name: "XLM",
948
+ symbol: "XLM",
949
+ decimals: 7
950
+ },
951
+ rpcUrls: {
952
+ default: {
953
+ http: ["https://horizon-testnet.stellar.org"]
954
+ }
955
+ }
956
+ };
957
+ async function getUniversalConnector(projectId) {
958
+ return appkitUniversalConnector.UniversalConnector.init({
959
+ projectId,
960
+ metadata: {
961
+ name: "Stellar Wallet Kit",
962
+ description: "Connect Stellar wallets",
963
+ url: "https://stellar.org",
964
+ icons: []
965
+ },
966
+ networks: [
967
+ {
968
+ namespace: "stellar",
969
+ chains: [stellarTestnet],
970
+ methods: ["stellar_signTransaction"],
971
+ events: []
972
+ }
973
+ ]
974
+ });
975
+ }
976
+
977
+ // src/adapters/WalletConnectAdapter.ts
978
+ var WalletConnectAdapter = class {
979
+ constructor(projectId) {
980
+ this.type = "walletconnect" /* WALLETCONNECT */;
981
+ this.connector = null;
982
+ this.session = null;
983
+ if (!projectId) {
984
+ throw new Error(
985
+ "WalletConnectAdapter requires a WalletConnect Project ID"
986
+ );
987
+ }
988
+ this.projectId = projectId;
989
+ }
990
+ async isAvailable() {
991
+ return typeof window !== "undefined";
992
+ }
993
+ async getConnector() {
994
+ if (!this.connector) {
995
+ this.connector = await getUniversalConnector(this.projectId);
996
+ }
997
+ return this.connector;
998
+ }
999
+ async connect() {
1000
+ const connector = await this.getConnector();
1001
+ const { session } = await connector.connect();
1002
+ this.session = session;
1003
+ const account = session.namespaces.stellar.accounts[0];
1004
+ const publicKey = account.split(":")[2];
1005
+ return {
1006
+ address: publicKey,
1007
+ publicKey
1008
+ };
1009
+ }
1010
+ async disconnect() {
1011
+ if (this.connector) {
1012
+ await this.connector.disconnect();
1013
+ }
1014
+ this.session = null;
1015
+ }
1016
+ async getPublicKey() {
1017
+ if (!this.session) return null;
1018
+ const account = this.session.namespaces.stellar.accounts[0];
1019
+ return account.split(":")[2];
1020
+ }
1021
+ async getNetwork() {
1022
+ throw new Error("Network detection not supported via WalletConnect");
1023
+ }
1024
+ async signTransaction(xdr, _options) {
1025
+ if (!this.connector || !this.session) {
1026
+ throw new Error("No active WalletConnect session");
1027
+ }
1028
+ const result = await this.connector.request({
1029
+ method: "stellar_signTransaction",
1030
+ params: { xdr }
1031
+ });
1032
+ return {
1033
+ signedTxXdr: result
1034
+ };
1035
+ }
1036
+ async signAuthEntry(_entryXdr, _options) {
1037
+ throw new Error(
1038
+ "Auth entry signing is not supported via WalletConnect"
1039
+ );
1040
+ }
1041
+ };
1042
+
1043
+ // src/wallets/createWalletAdapters.ts
1044
+ function createWalletAdapters(config) {
1045
+ return {
1046
+ // Extension wallet
1047
+ ["freighter" /* FREIGHTER */]: new FreighterAdapter(),
1048
+ // Web wallet
1049
+ ["albedo" /* ALBEDO */]: new AlbedoAdapter(),
1050
+ // WalletConnect (mobile wallets like Lobstr)
1051
+ ...config.walletConnectProjectId ? {
1052
+ ["walletconnect" /* WALLETCONNECT */]: new WalletConnectAdapter(
1053
+ config.walletConnectProjectId
1054
+ )
1055
+ } : {}
1056
+ };
1057
+ }
1009
1058
 
1010
1059
  exports.ConnectButton = ConnectButton;
1011
1060
  exports.FreighterAdapter = FreighterAdapter;
1012
1061
  exports.NetworkType = NetworkType;
1062
+ exports.WalletConnectAdapter = WalletConnectAdapter;
1013
1063
  exports.WalletModal = WalletModal;
1014
1064
  exports.WalletProvider = WalletProvider;
1015
1065
  exports.WalletType = WalletType;
1066
+ exports.createWalletAdapters = createWalletAdapters;
1016
1067
  exports.fetchAccountBalances = fetchAccountBalances;
1017
1068
  exports.formatBalance = formatBalance;
1018
1069
  exports.getAssetBalance = getAssetBalance;