stellar-wallet-kit 2.0.0 → 2.0.2

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.d.mts CHANGED
@@ -1,7 +1,8 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
2
 
3
3
  declare enum WalletType {
4
- FREIGHTER = "freighter"
4
+ FREIGHTER = "freighter",
5
+ ALBEDO = "albedo"
5
6
  }
6
7
  declare enum NetworkType {
7
8
  PUBLIC = "PUBLIC",
@@ -33,8 +34,18 @@ interface WalletInfo {
33
34
  name: string;
34
35
  icon: string;
35
36
  description?: string;
37
+ /** true = ready to use (installed OR web wallet) */
36
38
  installed: boolean;
39
+ /** Optional install link (extensions only) */
37
40
  downloadUrl?: string;
41
+ /** NEW: wallet delivery model */
42
+ kind?: WalletKind;
43
+ /** NEW: capability flags (future-proof) */
44
+ capabilities?: {
45
+ silentReconnect?: boolean;
46
+ networkDetection?: boolean;
47
+ authEntrySigning?: boolean;
48
+ };
38
49
  }
39
50
  interface ConnectWalletResponse {
40
51
  address: string;
@@ -73,7 +84,7 @@ interface StellarWalletKitConfig {
73
84
  appIcon?: string;
74
85
  }
75
86
  interface WalletTheme {
76
- mode?: 'light' | 'dark' | 'auto';
87
+ mode?: "light" | "dark" | "auto";
77
88
  primaryColor?: string;
78
89
  backgroundColor?: string;
79
90
  borderRadius?: string;
@@ -107,7 +118,13 @@ interface WalletContextValue {
107
118
  availableWallets: WalletInfo[];
108
119
  refreshBalances: () => Promise<void>;
109
120
  isLoadingBalances: boolean;
121
+ supports: {
122
+ silentReconnect: boolean;
123
+ networkDetection: boolean;
124
+ authEntrySigning: boolean;
125
+ };
110
126
  }
127
+ type WalletKind = "extension" | "web";
111
128
 
112
129
  type WalletProviderProps = PropsWithChildren<{
113
130
  config?: StellarWalletKitConfig;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
2
 
3
3
  declare enum WalletType {
4
- FREIGHTER = "freighter"
4
+ FREIGHTER = "freighter",
5
+ ALBEDO = "albedo"
5
6
  }
6
7
  declare enum NetworkType {
7
8
  PUBLIC = "PUBLIC",
@@ -33,8 +34,18 @@ interface WalletInfo {
33
34
  name: string;
34
35
  icon: string;
35
36
  description?: string;
37
+ /** true = ready to use (installed OR web wallet) */
36
38
  installed: boolean;
39
+ /** Optional install link (extensions only) */
37
40
  downloadUrl?: string;
41
+ /** NEW: wallet delivery model */
42
+ kind?: WalletKind;
43
+ /** NEW: capability flags (future-proof) */
44
+ capabilities?: {
45
+ silentReconnect?: boolean;
46
+ networkDetection?: boolean;
47
+ authEntrySigning?: boolean;
48
+ };
38
49
  }
39
50
  interface ConnectWalletResponse {
40
51
  address: string;
@@ -73,7 +84,7 @@ interface StellarWalletKitConfig {
73
84
  appIcon?: string;
74
85
  }
75
86
  interface WalletTheme {
76
- mode?: 'light' | 'dark' | 'auto';
87
+ mode?: "light" | "dark" | "auto";
77
88
  primaryColor?: string;
78
89
  backgroundColor?: string;
79
90
  borderRadius?: string;
@@ -107,7 +118,13 @@ interface WalletContextValue {
107
118
  availableWallets: WalletInfo[];
108
119
  refreshBalances: () => Promise<void>;
109
120
  isLoadingBalances: boolean;
121
+ supports: {
122
+ silentReconnect: boolean;
123
+ networkDetection: boolean;
124
+ authEntrySigning: boolean;
125
+ };
110
126
  }
127
+ type WalletKind = "extension" | "web";
111
128
 
112
129
  type WalletProviderProps = PropsWithChildren<{
113
130
  config?: StellarWalletKitConfig;
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ var React3__default = /*#__PURE__*/_interopDefault(React3);
12
12
  // src/types/index.ts
13
13
  var WalletType = /* @__PURE__ */ ((WalletType2) => {
14
14
  WalletType2["FREIGHTER"] = "freighter";
15
+ WalletType2["ALBEDO"] = "albedo";
15
16
  return WalletType2;
16
17
  })(WalletType || {});
17
18
  var NetworkType = /* @__PURE__ */ ((NetworkType2) => {
@@ -177,7 +178,131 @@ function groupBalancesByType(balances) {
177
178
  }, {});
178
179
  }
179
180
 
181
+ // src/utils/albedoCallback.ts
182
+ function openAlbedoPopup(url) {
183
+ const popup = window.open(
184
+ url,
185
+ "albedo",
186
+ "width=420,height=720,resizable=yes,scrollbars=yes"
187
+ );
188
+ if (!popup) {
189
+ throw new Error("Failed to open Albedo popup (blocked by browser)");
190
+ }
191
+ return popup;
192
+ }
193
+ function waitForAlbedoResult() {
194
+ return new Promise((resolve, reject) => {
195
+ const timeout = setTimeout(() => {
196
+ reject(new Error("Albedo response timeout"));
197
+ }, 2 * 60 * 1e3);
198
+ const parseResult = () => {
199
+ const params = new URLSearchParams(window.location.search);
200
+ if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) {
201
+ return;
202
+ }
203
+ clearTimeout(timeout);
204
+ const result = {};
205
+ params.forEach((value, key) => {
206
+ result[key] = value;
207
+ });
208
+ window.history.replaceState({}, document.title, window.location.pathname);
209
+ resolve(result);
210
+ };
211
+ parseResult();
212
+ window.addEventListener("popstate", parseResult);
213
+ });
214
+ }
215
+ function waitForAlbedoPopup() {
216
+ return new Promise((resolve, reject) => {
217
+ const timeout = setTimeout(() => {
218
+ reject(new Error("Albedo response timeout"));
219
+ }, 2 * 60 * 1e3);
220
+ function handler(event) {
221
+ if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") {
222
+ return;
223
+ }
224
+ clearTimeout(timeout);
225
+ window.removeEventListener("message", handler);
226
+ resolve(event.data.payload);
227
+ }
228
+ window.addEventListener("message", handler);
229
+ });
230
+ }
231
+
232
+ // src/adapters/AlbedoAdapter.ts
233
+ var AlbedoAdapter = class {
234
+ constructor() {
235
+ this.type = "albedo" /* ALBEDO */;
236
+ }
237
+ async isAvailable() {
238
+ return typeof window !== "undefined";
239
+ }
240
+ async connect() {
241
+ const url = new URL("https://albedo.link");
242
+ url.searchParams.set("intent", "public-key");
243
+ url.searchParams.set("app_name", "Stellar Wallet Kit");
244
+ url.searchParams.set("network", "testnet");
245
+ url.searchParams.set(
246
+ "callback",
247
+ `${window.location.origin}/albedo-callback`
248
+ );
249
+ url.searchParams.set("origin", window.location.origin);
250
+ openAlbedoPopup(url.toString());
251
+ const result = await waitForAlbedoPopup();
252
+ if (!result.pubkey) {
253
+ throw new Error("Albedo connection rejected");
254
+ }
255
+ return {
256
+ address: result.pubkey,
257
+ publicKey: result.pubkey
258
+ };
259
+ }
260
+ async disconnect() {
261
+ }
262
+ async getPublicKey() {
263
+ return null;
264
+ }
265
+ async getNetwork() {
266
+ throw new Error("Albedo does not expose network");
267
+ }
268
+ async signTransaction(xdr, _options) {
269
+ const url = new URL("https://albedo.link");
270
+ url.searchParams.set("intent", "tx");
271
+ url.searchParams.set("xdr", xdr);
272
+ url.searchParams.set("app_name", "Stellar Wallet Kit");
273
+ url.searchParams.set("network", "testnet");
274
+ url.searchParams.set("callback", window.location.href);
275
+ url.searchParams.set("origin", window.location.origin);
276
+ window.location.href = url.toString();
277
+ const result = await waitForAlbedoResult();
278
+ if (!result.signed_envelope_xdr) {
279
+ throw new Error("Albedo signing rejected");
280
+ }
281
+ return { signedTxXdr: result.signed_envelope_xdr };
282
+ }
283
+ async signAuthEntry(entryXdr, _options) {
284
+ const url = new URL("https://albedo.link");
285
+ url.searchParams.set("intent", "sign-auth-entry");
286
+ url.searchParams.set("xdr", entryXdr);
287
+ url.searchParams.set("app_name", "Stellar Wallet Kit");
288
+ url.searchParams.set("network", "testnet");
289
+ url.searchParams.set("callback", window.location.href);
290
+ url.searchParams.set("origin", window.location.origin);
291
+ window.location.href = url.toString();
292
+ const result = await waitForAlbedoResult();
293
+ if (!result.signed_xdr) {
294
+ throw new Error("Albedo auth entry rejected");
295
+ }
296
+ return { signedAuthEntry: result.signed_xdr };
297
+ }
298
+ };
299
+
180
300
  // src/context/WalletContext.tsx
301
+ var DEFAULT_SUPPORTS = {
302
+ silentReconnect: false,
303
+ networkDetection: false,
304
+ authEntrySigning: false
305
+ };
181
306
  var WalletContext = React3.createContext(void 0);
182
307
  var STORAGE_KEY = "stellar_wallet_kit";
183
308
  var getStorageData = () => {
@@ -202,7 +327,8 @@ var setStorageData = (data) => {
202
327
  };
203
328
  var isBrowser = typeof window !== "undefined";
204
329
  var walletAdapters = {
205
- ["freighter" /* FREIGHTER */]: new FreighterAdapter()
330
+ ["freighter" /* FREIGHTER */]: new FreighterAdapter(),
331
+ ["albedo" /* ALBEDO */]: new AlbedoAdapter()
206
332
  };
207
333
  var walletMetadata = {
208
334
  ["freighter" /* FREIGHTER */]: {
@@ -210,14 +336,34 @@ var walletMetadata = {
210
336
  name: "Freighter",
211
337
  icon: "https://stellar.creit.tech/wallet-icons/freighter.svg",
212
338
  description: "Freighter browser extension wallet",
213
- downloadUrl: "https://chrome.google.com/webstore/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk"
339
+ downloadUrl: "https://chrome.google.com/webstore/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk",
340
+ kind: "extension",
341
+ capabilities: {
342
+ silentReconnect: true,
343
+ networkDetection: true,
344
+ authEntrySigning: true
345
+ }
346
+ },
347
+ ["albedo" /* ALBEDO */]: {
348
+ id: "albedo" /* ALBEDO */,
349
+ name: "Albedo",
350
+ icon: "https://stellar.creit.tech/wallet-icons/albedo.svg",
351
+ description: "Web-based Stellar wallet",
352
+ kind: "web",
353
+ capabilities: {
354
+ silentReconnect: false,
355
+ networkDetection: false,
356
+ authEntrySigning: true
357
+ }
214
358
  }
215
359
  };
216
360
  function WalletProvider({ config = {}, children }) {
217
361
  const [account, setAccount] = React3.useState(null);
218
362
  const [isConnecting, setIsConnecting] = React3.useState(false);
219
363
  const [error, setError] = React3.useState(null);
220
- const [network, setNetwork] = React3.useState(config.network || "TESTNET" /* TESTNET */);
364
+ const [network, setNetwork] = React3.useState(
365
+ config.network || "TESTNET" /* TESTNET */
366
+ );
221
367
  const [selectedWallet, setSelectedWallet] = React3.useState(null);
222
368
  const [availableWallets, setAvailableWallets] = React3.useState([]);
223
369
  const [isLoadingBalances, setIsLoadingBalances] = React3.useState(false);
@@ -227,9 +373,14 @@ function WalletProvider({ config = {}, children }) {
227
373
  const checkWallets = async () => {
228
374
  const wallets = [];
229
375
  for (const [type, adapter] of Object.entries(walletAdapters)) {
230
- const installed = await adapter.isAvailable();
376
+ const walletType = type;
377
+ const meta = walletMetadata[walletType];
378
+ let installed = true;
379
+ if (meta.kind === "extension") {
380
+ installed = await adapter.isAvailable();
381
+ }
231
382
  wallets.push({
232
- ...walletMetadata[type],
383
+ ...meta,
233
384
  installed
234
385
  });
235
386
  }
@@ -265,42 +416,54 @@ function WalletProvider({ config = {}, children }) {
265
416
  setIsLoadingBalances(false);
266
417
  }
267
418
  }, [account?.publicKey, network]);
268
- const connect = React3.useCallback(async (walletType) => {
269
- setIsConnecting(true);
270
- setError(null);
271
- try {
272
- const typeToConnect = walletType || config.defaultWallet || "freighter" /* FREIGHTER */;
273
- const adapter = walletAdapters[typeToConnect];
274
- if (!adapter) {
275
- throw new Error(`Wallet adapter not found for ${typeToConnect}`);
276
- }
277
- const available = await adapter.isAvailable();
278
- if (!available) {
279
- throw new Error(`${walletMetadata[typeToConnect].name} is not installed`);
280
- }
281
- const response = await adapter.connect();
282
- const newAccount = {
283
- address: response.address,
284
- publicKey: response.publicKey,
285
- displayName: `${response.address.slice(0, 4)}...${response.address.slice(-4)}`
286
- };
287
- setAccount(newAccount);
288
- setSelectedWallet(typeToConnect);
289
- setStorageData({ selectedWallet: typeToConnect, autoConnect: true });
419
+ const connect = React3.useCallback(
420
+ async (walletType) => {
421
+ setIsConnecting(true);
422
+ setError(null);
290
423
  try {
291
- const balances = await fetchAccountBalances(response.publicKey, network);
292
- setAccount((prev) => prev ? { ...prev, balances } : null);
293
- } catch (balanceError) {
294
- console.error("Failed to fetch initial balances:", balanceError);
424
+ const typeToConnect = walletType || config.defaultWallet || "freighter" /* FREIGHTER */;
425
+ const adapter = walletAdapters[typeToConnect];
426
+ if (!adapter) {
427
+ throw new Error(`Wallet adapter not found for ${typeToConnect}`);
428
+ }
429
+ const meta = walletMetadata[typeToConnect];
430
+ if (meta.kind === "extension") {
431
+ const available = await adapter.isAvailable();
432
+ if (!available) {
433
+ throw new Error(`${meta.name} is not installed`);
434
+ }
435
+ }
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;
461
+ } finally {
462
+ setIsConnecting(false);
295
463
  }
296
- } catch (err) {
297
- const error2 = err instanceof Error ? err : new Error("Failed to connect wallet");
298
- setError(error2);
299
- throw error2;
300
- } finally {
301
- setIsConnecting(false);
302
- }
303
- }, [config.defaultWallet]);
464
+ },
465
+ [config.defaultWallet]
466
+ );
304
467
  const disconnect = React3.useCallback(async () => {
305
468
  try {
306
469
  if (selectedWallet) {
@@ -317,26 +480,35 @@ function WalletProvider({ config = {}, children }) {
317
480
  throw error2;
318
481
  }
319
482
  }, [selectedWallet]);
320
- const signTransaction2 = React3.useCallback(async (xdr, options) => {
321
- if (!selectedWallet) {
322
- throw new Error("No wallet connected");
323
- }
324
- const adapter = walletAdapters[selectedWallet];
325
- return adapter.signTransaction(xdr, options);
326
- }, [selectedWallet]);
327
- const signAuthEntry2 = React3.useCallback(async (entryXdr, options) => {
328
- if (!selectedWallet) {
329
- throw new Error("No wallet connected");
330
- }
331
- const adapter = walletAdapters[selectedWallet];
332
- return adapter.signAuthEntry(entryXdr, options);
333
- }, [selectedWallet]);
334
- const switchNetwork = React3.useCallback(async (newNetwork) => {
335
- setNetwork(newNetwork);
336
- if (account?.publicKey) {
337
- await refreshBalances();
338
- }
339
- }, [account?.publicKey, refreshBalances]);
483
+ const signTransaction2 = React3.useCallback(
484
+ async (xdr, options) => {
485
+ if (!selectedWallet) {
486
+ throw new Error("No wallet connected");
487
+ }
488
+ const adapter = walletAdapters[selectedWallet];
489
+ return adapter.signTransaction(xdr, options);
490
+ },
491
+ [selectedWallet]
492
+ );
493
+ const signAuthEntry2 = React3.useCallback(
494
+ async (entryXdr, options) => {
495
+ if (!selectedWallet) {
496
+ throw new Error("No wallet connected");
497
+ }
498
+ const adapter = walletAdapters[selectedWallet];
499
+ return adapter.signAuthEntry(entryXdr, options);
500
+ },
501
+ [selectedWallet]
502
+ );
503
+ const switchNetwork = React3.useCallback(
504
+ async (newNetwork) => {
505
+ setNetwork(newNetwork);
506
+ if (account?.publicKey) {
507
+ await refreshBalances();
508
+ }
509
+ },
510
+ [account?.publicKey, refreshBalances]
511
+ );
340
512
  React3.useEffect(() => {
341
513
  if (!account?.publicKey || !isBrowser) return;
342
514
  refreshBalances();
@@ -345,6 +517,17 @@ function WalletProvider({ config = {}, children }) {
345
517
  }, 3e4);
346
518
  return () => clearInterval(interval);
347
519
  }, [account?.publicKey, refreshBalances]);
520
+ const supports = React3.useMemo(() => {
521
+ if (!selectedWallet) {
522
+ return DEFAULT_SUPPORTS;
523
+ }
524
+ const meta = walletMetadata[selectedWallet];
525
+ return {
526
+ silentReconnect: !!meta.capabilities?.silentReconnect,
527
+ networkDetection: !!meta.capabilities?.networkDetection,
528
+ authEntrySigning: !!meta.capabilities?.authEntrySigning
529
+ };
530
+ }, [selectedWallet]);
348
531
  const value = React3.useMemo(
349
532
  () => ({
350
533
  account,
@@ -360,7 +543,8 @@ function WalletProvider({ config = {}, children }) {
360
543
  switchNetwork,
361
544
  availableWallets,
362
545
  refreshBalances,
363
- isLoadingBalances
546
+ isLoadingBalances,
547
+ supports
364
548
  }),
365
549
  [
366
550
  account,
@@ -376,7 +560,8 @@ function WalletProvider({ config = {}, children }) {
376
560
  switchNetwork,
377
561
  availableWallets,
378
562
  refreshBalances,
379
- isLoadingBalances
563
+ isLoadingBalances,
564
+ supports
380
565
  ]
381
566
  );
382
567
  return /* @__PURE__ */ React3__default.default.createElement(WalletContext.Provider, { value }, children);