@phantom/browser-sdk 1.0.0-beta.9 → 1.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.mjs CHANGED
@@ -2,13 +2,7 @@
2
2
  import { AddressType } from "@phantom/client";
3
3
 
4
4
  // src/providers/injected/index.ts
5
- import { AddressType as AddressType4 } from "@phantom/client";
6
- import { createPhantom, createExtensionPlugin } from "@phantom/browser-injected-sdk";
7
- import { createSolanaPlugin } from "@phantom/browser-injected-sdk/solana";
8
- import { createEthereumPlugin } from "@phantom/browser-injected-sdk/ethereum";
9
- import {
10
- createAutoConfirmPlugin
11
- } from "@phantom/browser-injected-sdk/auto-confirm";
5
+ import { AddressType as AddressType2 } from "@phantom/client";
12
6
 
13
7
  // src/debug.ts
14
8
  var DebugLevel = /* @__PURE__ */ ((DebugLevel2) => {
@@ -84,135 +78,606 @@ var DebugCategory = {
84
78
  SESSION: "Session"
85
79
  };
86
80
 
87
- // src/providers/injected/chains/SolanaChain.ts
81
+ // src/wallets/discovery.ts
82
+ import { AddressType as ClientAddressType } from "@phantom/client";
83
+ import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
84
+ import { createPhantom, createExtensionPlugin } from "@phantom/browser-injected-sdk";
85
+ import { createSolanaPlugin } from "@phantom/browser-injected-sdk/solana";
86
+ import { createEthereumPlugin } from "@phantom/browser-injected-sdk/ethereum";
87
+ import { createAutoConfirmPlugin } from "@phantom/browser-injected-sdk/auto-confirm";
88
+ function generateWalletIdFromEIP6963(info) {
89
+ if (info.rdns) {
90
+ return info.rdns.split(".").reverse().join("-");
91
+ }
92
+ return info.name.toLowerCase().replace(/\s+/g, "-");
93
+ }
94
+ function generateWalletIdFromName(name) {
95
+ return name.toLowerCase().replace(/\s+/g, "-");
96
+ }
97
+ function processEIP6963Providers(providers) {
98
+ const wallets = [];
99
+ debug.log(DebugCategory.BROWSER_SDK, "Processing EIP-6963 providers", {
100
+ providerCount: providers.size,
101
+ providerNames: Array.from(providers.values()).map((d) => d.info.name)
102
+ });
103
+ for (const [, detail] of providers) {
104
+ const { info, provider } = detail;
105
+ const isPhantom = info.name.toLowerCase().includes("phantom") || info.rdns && (info.rdns.toLowerCase().includes("phantom") || info.rdns.toLowerCase() === "app.phantom");
106
+ if (isPhantom) {
107
+ debug.log(DebugCategory.BROWSER_SDK, "Skipping Phantom from EIP-6963", { name: info.name, rdns: info.rdns });
108
+ continue;
109
+ }
110
+ const walletId = generateWalletIdFromEIP6963(info);
111
+ debug.log(DebugCategory.BROWSER_SDK, "Discovered EIP-6963 wallet", {
112
+ walletId,
113
+ walletName: info.name,
114
+ rdns: info.rdns
115
+ });
116
+ wallets.push({
117
+ id: walletId,
118
+ name: info.name,
119
+ icon: info.icon,
120
+ addressTypes: [ClientAddressType.ethereum],
121
+ providers: {
122
+ // EIP-6963 provider implements EIP-1193 interface (IEthereumChain)
123
+ ethereum: provider
124
+ },
125
+ rdns: info.rdns,
126
+ // Store rdns for potential future matching
127
+ discovery: "eip6963"
128
+ });
129
+ }
130
+ debug.log(DebugCategory.BROWSER_SDK, "EIP-6963 discovery completed", {
131
+ discoveredCount: wallets.length,
132
+ walletIds: wallets.map((w) => w.id)
133
+ });
134
+ return wallets;
135
+ }
136
+ function discoverEthereumWallets() {
137
+ return new Promise((resolve) => {
138
+ const discoveredProviders = /* @__PURE__ */ new Map();
139
+ if (typeof window === "undefined") {
140
+ resolve([]);
141
+ return;
142
+ }
143
+ const handleAnnounce = (event) => {
144
+ const detail = event.detail;
145
+ if (detail?.info && detail?.provider) {
146
+ discoveredProviders.set(detail.info.uuid, detail);
147
+ }
148
+ };
149
+ window.addEventListener("eip6963:announceProvider", handleAnnounce);
150
+ window.dispatchEvent(new Event("eip6963:requestProvider"));
151
+ const processProviders = () => {
152
+ const wallets = processEIP6963Providers(discoveredProviders);
153
+ window.removeEventListener("eip6963:announceProvider", handleAnnounce);
154
+ resolve(wallets);
155
+ };
156
+ setTimeout(processProviders, 400);
157
+ });
158
+ }
159
+ async function discoverSolanaWallets() {
160
+ const wallets = [];
161
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
162
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet Standard discovery skipped (not in browser environment)");
163
+ return wallets;
164
+ }
165
+ const registeredWalletsSet = /* @__PURE__ */ new Set();
166
+ let cachedWalletsArray;
167
+ function addRegisteredWallet(wallet) {
168
+ cachedWalletsArray = void 0;
169
+ registeredWalletsSet.add(wallet);
170
+ const featureKeys = wallet.features ? Object.keys(wallet.features) : [];
171
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet registered", {
172
+ name: wallet.name,
173
+ chains: wallet.chains,
174
+ featureKeys,
175
+ totalWallets: registeredWalletsSet.size
176
+ });
177
+ }
178
+ function removeRegisteredWallet(wallet) {
179
+ cachedWalletsArray = void 0;
180
+ registeredWalletsSet.delete(wallet);
181
+ }
182
+ function getRegisteredWallets() {
183
+ if (!cachedWalletsArray) {
184
+ cachedWalletsArray = [...registeredWalletsSet];
185
+ }
186
+ return cachedWalletsArray;
187
+ }
188
+ function register(...wallets2) {
189
+ wallets2 = wallets2.filter((wallet) => !registeredWalletsSet.has(wallet));
190
+ if (!wallets2.length) {
191
+ return () => {
192
+ };
193
+ }
194
+ wallets2.forEach((wallet) => addRegisteredWallet(wallet));
195
+ return function unregister() {
196
+ wallets2.forEach((wallet) => removeRegisteredWallet(wallet));
197
+ };
198
+ }
199
+ const registerAPI = Object.freeze({ register });
200
+ const handleRegisterWalletEvent = (event) => {
201
+ const callback = event.detail;
202
+ if (typeof callback === "function") {
203
+ try {
204
+ callback(registerAPI);
205
+ } catch (error) {
206
+ debug.warn(DebugCategory.BROWSER_SDK, "Error calling wallet registration callback", { error });
207
+ }
208
+ }
209
+ };
210
+ try {
211
+ window.addEventListener("wallet-standard:register-wallet", handleRegisterWalletEvent);
212
+ } catch (error) {
213
+ debug.warn(DebugCategory.BROWSER_SDK, "Could not add register-wallet event listener", { error });
214
+ }
215
+ class AppReadyEvent extends Event {
216
+ constructor(api) {
217
+ super("wallet-standard:app-ready", {
218
+ bubbles: false,
219
+ cancelable: false,
220
+ composed: false
221
+ });
222
+ this.detail = api;
223
+ }
224
+ }
225
+ try {
226
+ window.dispatchEvent(new AppReadyEvent(registerAPI));
227
+ debug.log(DebugCategory.BROWSER_SDK, "Dispatched wallet-standard:app-ready event");
228
+ } catch (error) {
229
+ debug.warn(DebugCategory.BROWSER_SDK, "Could not dispatch app-ready event", { error });
230
+ }
231
+ const walletsAPI = {
232
+ getWallets: () => {
233
+ return {
234
+ get: getRegisteredWallets,
235
+ on: (_event, _listener) => {
236
+ return () => {
237
+ };
238
+ },
239
+ register
240
+ };
241
+ }
242
+ };
243
+ if (!navigator.wallets) {
244
+ navigator.wallets = walletsAPI;
245
+ }
246
+ debug.log(DebugCategory.BROWSER_SDK, "Initialized Wallet Standard registry");
247
+ await new Promise((resolve) => setTimeout(resolve, 100));
248
+ const existingWalletsAPI = navigator.wallets || window.wallets;
249
+ if (!existingWalletsAPI || typeof existingWalletsAPI.getWallets !== "function") {
250
+ const logData = {
251
+ hasNavigator: !!navigator,
252
+ hasWindow: typeof window !== "undefined",
253
+ note: "Wallet Standard API not properly initialized"
254
+ };
255
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet Standard API not available", logData);
256
+ return wallets;
257
+ }
258
+ const walletsGetter = existingWalletsAPI.getWallets();
259
+ const getWalletsFn = () => Promise.resolve([...walletsGetter.get()]);
260
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet Standard API detected, starting discovery");
261
+ try {
262
+ let registeredWallets = [];
263
+ let attempts = 0;
264
+ const maxAttempts = 5;
265
+ const initialDelay = 100;
266
+ const eip6963Timeout = 400;
267
+ await new Promise((resolve) => setTimeout(resolve, initialDelay));
268
+ while (attempts < maxAttempts) {
269
+ registeredWallets = await getWalletsFn();
270
+ const logData = {
271
+ attempt: attempts + 1,
272
+ walletCount: registeredWallets.length,
273
+ walletNames: registeredWallets.map((w) => w.name),
274
+ chains: registeredWallets.flatMap((w) => w.chains)
275
+ };
276
+ debug.log(DebugCategory.BROWSER_SDK, `Wallet Standard getWallets attempt ${attempts + 1}`, logData);
277
+ if (registeredWallets.length > 0 || attempts === maxAttempts - 1) {
278
+ break;
279
+ }
280
+ await new Promise((resolve) => setTimeout(resolve, initialDelay));
281
+ attempts++;
282
+ }
283
+ const totalWaitTime = initialDelay + attempts * initialDelay;
284
+ if (totalWaitTime < eip6963Timeout) {
285
+ const remainingWait = eip6963Timeout - totalWaitTime;
286
+ await new Promise((resolve) => setTimeout(resolve, remainingWait));
287
+ registeredWallets = await getWalletsFn();
288
+ }
289
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet Standard getWallets final result", {
290
+ walletCount: registeredWallets.length,
291
+ walletNames: registeredWallets.map((w) => w.name),
292
+ attempts: attempts + 1
293
+ });
294
+ for (const wallet of registeredWallets) {
295
+ const supportsSolana = wallet.chains.some((chain) => {
296
+ const chainLower = chain.toLowerCase();
297
+ return chainLower.startsWith("solana:") || chainLower === "solana";
298
+ }) || wallet.features && typeof wallet.features === "object" && Object.keys(wallet.features).some((featureKey) => {
299
+ const featureLower = featureKey.toLowerCase();
300
+ return featureLower.startsWith("solana:");
301
+ });
302
+ if (!supportsSolana) {
303
+ const featureKeys = wallet.features ? Object.keys(wallet.features) : [];
304
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet does not support Solana", {
305
+ walletName: wallet.name,
306
+ chains: wallet.chains,
307
+ featureKeys
308
+ });
309
+ continue;
310
+ }
311
+ if (wallet.name.toLowerCase().includes("phantom")) {
312
+ debug.log(DebugCategory.BROWSER_SDK, "Skipping Phantom from Wallet Standard (handled separately)");
313
+ continue;
314
+ }
315
+ const walletId = generateWalletIdFromName(wallet.name);
316
+ const safeFeatures = wallet.features ? Object.keys(wallet.features) : [];
317
+ debug.log(DebugCategory.BROWSER_SDK, "Discovered Wallet Standard Solana wallet", {
318
+ walletId,
319
+ walletName: wallet.name,
320
+ chains: wallet.chains,
321
+ featureKeys: safeFeatures,
322
+ icon: wallet.icon,
323
+ version: wallet.version,
324
+ accountCount: wallet.accounts?.length || 0
325
+ });
326
+ wallets.push({
327
+ id: walletId,
328
+ name: wallet.name,
329
+ icon: wallet.icon,
330
+ addressTypes: [ClientAddressType.solana],
331
+ providers: {
332
+ // Cast to ISolanaChain - Wallet Standard wallets have compatible methods
333
+ // The InjectedWalletSolanaChain wrapper will handle the actual method calls
334
+ solana: wallet
335
+ },
336
+ discovery: "standard"
337
+ });
338
+ }
339
+ } catch (error) {
340
+ debug.warn(DebugCategory.BROWSER_SDK, "Wallet Standard API error", {
341
+ error: error instanceof Error ? error.message : String(error),
342
+ stack: error instanceof Error ? error.stack : void 0
343
+ });
344
+ }
345
+ const finalLogData = {
346
+ discoveredCount: wallets.length,
347
+ walletIds: wallets.map((w) => w.id),
348
+ walletNames: wallets.map((w) => w.name)
349
+ };
350
+ debug.log(DebugCategory.BROWSER_SDK, "Wallet Standard Solana discovery completed", finalLogData);
351
+ return wallets;
352
+ }
353
+ function discoverPhantomWallet(addressTypes) {
354
+ if (typeof window === "undefined") {
355
+ return null;
356
+ }
357
+ if (!isPhantomExtensionInstalled()) {
358
+ return null;
359
+ }
360
+ const plugins = [createExtensionPlugin()];
361
+ if (addressTypes.includes(ClientAddressType.solana)) {
362
+ plugins.push(createSolanaPlugin());
363
+ }
364
+ if (addressTypes.includes(ClientAddressType.ethereum)) {
365
+ plugins.push(createEthereumPlugin());
366
+ }
367
+ plugins.push(createAutoConfirmPlugin());
368
+ const phantomInstance = createPhantom({ plugins });
369
+ return {
370
+ id: "phantom",
371
+ name: "Phantom",
372
+ icon: void 0,
373
+ // Icon will be rendered from icons package in UI components
374
+ addressTypes,
375
+ providers: {
376
+ solana: addressTypes.includes(ClientAddressType.solana) ? phantomInstance.solana : void 0,
377
+ ethereum: addressTypes.includes(ClientAddressType.ethereum) ? phantomInstance.ethereum : void 0
378
+ },
379
+ isPhantom: true,
380
+ phantomInstance,
381
+ discovery: "phantom"
382
+ };
383
+ }
384
+ async function discoverWallets(addressTypes) {
385
+ const requestedAddressTypes = addressTypes || [];
386
+ debug.log(DebugCategory.BROWSER_SDK, "Starting all wallet discovery methods", {
387
+ addressTypes: requestedAddressTypes
388
+ });
389
+ const [solanaWallets, ethereumWallets] = await Promise.all([discoverSolanaWallets(), discoverEthereumWallets()]);
390
+ const phantomWallet = discoverPhantomWallet(requestedAddressTypes);
391
+ debug.log(DebugCategory.BROWSER_SDK, "All wallet discovery methods completed", {
392
+ phantomFound: !!phantomWallet,
393
+ solanaWalletsCount: solanaWallets.length,
394
+ ethereumWalletsCount: ethereumWallets.length,
395
+ solanaWalletIds: solanaWallets.map((w) => w.id),
396
+ ethereumWalletIds: ethereumWallets.map((w) => w.id)
397
+ });
398
+ const walletMap = /* @__PURE__ */ new Map();
399
+ if (phantomWallet) {
400
+ walletMap.set("phantom", phantomWallet);
401
+ }
402
+ for (const wallet of [...solanaWallets, ...ethereumWallets]) {
403
+ const existing = walletMap.get(wallet.id);
404
+ if (existing) {
405
+ const mergedAddressTypes = Array.from(/* @__PURE__ */ new Set([...existing.addressTypes, ...wallet.addressTypes]));
406
+ const mergedProviders = {
407
+ ...existing.providers,
408
+ ...wallet.providers
409
+ };
410
+ const mergedWallet = {
411
+ ...existing,
412
+ addressTypes: mergedAddressTypes,
413
+ // Prefer icon from the most recent discovery
414
+ icon: wallet.icon || existing.icon,
415
+ providers: mergedProviders
416
+ };
417
+ walletMap.set(wallet.id, mergedWallet);
418
+ debug.log(DebugCategory.BROWSER_SDK, "Merged wallet by ID", {
419
+ walletName: wallet.name,
420
+ walletId: wallet.id,
421
+ existingAddressTypes: existing.addressTypes,
422
+ newAddressTypes: wallet.addressTypes,
423
+ mergedAddressTypes,
424
+ existingProviders: Object.keys(existing.providers || {}),
425
+ newProviders: Object.keys(wallet.providers || {}),
426
+ mergedProviders: Object.keys(mergedProviders)
427
+ });
428
+ debug.log(DebugCategory.BROWSER_SDK, "Merged wallet from multiple discovery methods", {
429
+ walletId: wallet.id,
430
+ walletName: wallet.name,
431
+ existingAddressTypes: existing.addressTypes,
432
+ newAddressTypes: wallet.addressTypes,
433
+ mergedAddressTypes
434
+ });
435
+ } else {
436
+ walletMap.set(wallet.id, wallet);
437
+ }
438
+ }
439
+ return Array.from(walletMap.values());
440
+ }
441
+
442
+ // src/providers/injected/chains/InjectedWalletSolanaChain.ts
88
443
  import { EventEmitter } from "eventemitter3";
89
- import { AddressType as AddressType2 } from "@phantom/client";
90
444
  import { Buffer } from "buffer";
91
- var InjectedSolanaChain = class {
92
- constructor(phantom, callbacks) {
445
+ var InjectedWalletSolanaChain = class {
446
+ constructor(provider, walletId, walletName) {
447
+ // Expose eventEmitter for testing - allows tests to trigger events directly
448
+ this.eventEmitter = new EventEmitter();
93
449
  this._connected = false;
94
450
  this._publicKey = null;
95
- this.eventEmitter = new EventEmitter();
96
- this.phantom = phantom;
97
- this.callbacks = callbacks;
451
+ this.provider = provider;
452
+ this.walletId = walletId;
453
+ this.walletName = walletName;
98
454
  this.setupEventListeners();
99
- this.syncInitialState();
100
455
  }
101
- // Wallet adapter compliant properties
102
456
  get connected() {
103
457
  return this._connected;
104
458
  }
105
459
  get publicKey() {
106
460
  return this._publicKey;
107
461
  }
108
- // Connection methods - delegate to provider
109
- connect(_options) {
110
- if (!this.callbacks.isConnected()) {
111
- return Promise.reject(new Error("Provider not connected. Call provider connect first."));
112
- }
113
- const addresses = this.callbacks.getAddresses();
114
- const solanaAddress = addresses.find((addr) => addr.addressType === AddressType2.solana);
115
- if (!solanaAddress) {
116
- return Promise.reject(new Error("Solana not enabled for this provider"));
462
+ async connect(options) {
463
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connect", {
464
+ walletId: this.walletId,
465
+ walletName: this.walletName,
466
+ onlyIfTrusted: options?.onlyIfTrusted
467
+ });
468
+ try {
469
+ const result = await this.provider.connect(options);
470
+ if (typeof result === "string") {
471
+ this._connected = true;
472
+ this._publicKey = result;
473
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connected", {
474
+ walletId: this.walletId,
475
+ walletName: this.walletName,
476
+ publicKey: result
477
+ });
478
+ return { publicKey: result };
479
+ }
480
+ if (typeof result === "object" && result !== null && "publicKey" in result) {
481
+ this._connected = true;
482
+ this._publicKey = result.publicKey;
483
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connected", {
484
+ walletId: this.walletId,
485
+ walletName: this.walletName,
486
+ publicKey: result.publicKey
487
+ });
488
+ return result;
489
+ }
490
+ if (Array.isArray(result) && result.length > 0) {
491
+ const firstAccount = result[0];
492
+ if (typeof firstAccount === "object" && firstAccount !== null && "address" in firstAccount) {
493
+ this._connected = true;
494
+ this._publicKey = firstAccount.address;
495
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connected", {
496
+ walletId: this.walletId,
497
+ walletName: this.walletName,
498
+ publicKey: firstAccount.address
499
+ });
500
+ return { publicKey: firstAccount.address };
501
+ }
502
+ }
503
+ throw new Error("Unexpected connect result format");
504
+ } catch (error) {
505
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connect failed", {
506
+ walletId: this.walletId,
507
+ walletName: this.walletName,
508
+ error: error instanceof Error ? error.message : String(error)
509
+ });
510
+ throw error;
117
511
  }
118
- this.updateConnectionState(true, solanaAddress.address);
119
- return Promise.resolve({ publicKey: solanaAddress.address });
120
512
  }
121
513
  async disconnect() {
122
- await this.callbacks.disconnect();
514
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana disconnect", {
515
+ walletId: this.walletId,
516
+ walletName: this.walletName
517
+ });
518
+ try {
519
+ await this.provider.disconnect();
520
+ this._connected = false;
521
+ this._publicKey = null;
522
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana disconnected", {
523
+ walletId: this.walletId,
524
+ walletName: this.walletName
525
+ });
526
+ } catch (error) {
527
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana disconnect failed", {
528
+ walletId: this.walletId,
529
+ walletName: this.walletName,
530
+ error: error instanceof Error ? error.message : String(error)
531
+ });
532
+ throw error;
533
+ }
123
534
  }
124
- // Standard wallet adapter methods
125
535
  async signMessage(message) {
126
536
  const messageBytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
127
- const result = await this.phantom.solana.signMessage(messageBytes);
128
- return {
129
- signature: result.signature instanceof Uint8Array ? result.signature : new Uint8Array(Buffer.from(result.signature, "base64")),
130
- publicKey: this._publicKey || ""
131
- };
537
+ const messagePreview = typeof message === "string" ? message.substring(0, 50) : `${messageBytes.length} bytes`;
538
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signMessage", {
539
+ walletId: this.walletId,
540
+ walletName: this.walletName,
541
+ messagePreview,
542
+ messageLength: messageBytes.length
543
+ });
544
+ try {
545
+ const result = await this.provider.signMessage(message);
546
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signMessage success", {
547
+ walletId: this.walletId,
548
+ walletName: this.walletName,
549
+ signatureLength: result.signature.length
550
+ });
551
+ return {
552
+ signature: result.signature instanceof Uint8Array ? result.signature : new Uint8Array(Buffer.from(result.signature, "base64")),
553
+ publicKey: result.publicKey || this._publicKey || ""
554
+ };
555
+ } catch (error) {
556
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signMessage failed", {
557
+ walletId: this.walletId,
558
+ walletName: this.walletName,
559
+ error: error instanceof Error ? error.message : String(error)
560
+ });
561
+ throw error;
562
+ }
132
563
  }
133
564
  async signTransaction(transaction) {
134
- if (!this.callbacks.isConnected()) {
135
- return Promise.reject(new Error("Provider not connected. Call provider connect first."));
136
- }
565
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signTransaction", {
566
+ walletId: this.walletId,
567
+ walletName: this.walletName
568
+ });
137
569
  try {
138
- const result = await this.phantom.solana.signTransaction(transaction);
570
+ const result = await this.provider.signTransaction(transaction);
571
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signTransaction success", {
572
+ walletId: this.walletId,
573
+ walletName: this.walletName
574
+ });
139
575
  return result;
140
576
  } catch (error) {
141
- return Promise.reject(error);
577
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signTransaction failed", {
578
+ walletId: this.walletId,
579
+ walletName: this.walletName,
580
+ error: error instanceof Error ? error.message : String(error)
581
+ });
582
+ throw error;
142
583
  }
143
584
  }
144
585
  async signAndSendTransaction(transaction) {
145
- const result = await this.phantom.solana.signAndSendTransaction(transaction);
146
- return { signature: result.signature };
586
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendTransaction", {
587
+ walletId: this.walletId,
588
+ walletName: this.walletName
589
+ });
590
+ try {
591
+ const result = await this.provider.signAndSendTransaction(transaction);
592
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendTransaction success", {
593
+ walletId: this.walletId,
594
+ walletName: this.walletName,
595
+ signature: result.signature
596
+ });
597
+ return result;
598
+ } catch (error) {
599
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendTransaction failed", {
600
+ walletId: this.walletId,
601
+ walletName: this.walletName,
602
+ error: error instanceof Error ? error.message : String(error)
603
+ });
604
+ throw error;
605
+ }
147
606
  }
148
607
  async signAllTransactions(transactions) {
149
- if (!this.callbacks.isConnected()) {
150
- return Promise.reject(new Error("Provider not connected. Call provider connect first."));
151
- }
608
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAllTransactions", {
609
+ walletId: this.walletId,
610
+ walletName: this.walletName,
611
+ transactionCount: transactions.length
612
+ });
152
613
  try {
153
- const result = await this.phantom.solana.signAllTransactions(transactions);
614
+ const result = await this.provider.signAllTransactions(transactions);
615
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAllTransactions success", {
616
+ walletId: this.walletId,
617
+ walletName: this.walletName,
618
+ signedCount: result.length
619
+ });
154
620
  return result;
155
621
  } catch (error) {
156
- return Promise.reject(error);
622
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAllTransactions failed", {
623
+ walletId: this.walletId,
624
+ walletName: this.walletName,
625
+ error: error instanceof Error ? error.message : String(error)
626
+ });
627
+ throw error;
157
628
  }
158
629
  }
159
630
  async signAndSendAllTransactions(transactions) {
160
- if (!this.callbacks.isConnected()) {
161
- return Promise.reject(new Error("Provider not connected. Call provider connect first."));
162
- }
631
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendAllTransactions", {
632
+ walletId: this.walletId,
633
+ walletName: this.walletName,
634
+ transactionCount: transactions.length
635
+ });
163
636
  try {
164
- const result = await this.phantom.solana.signAndSendAllTransactions(transactions);
165
- return { signatures: result.signatures };
637
+ const result = await this.provider.signAndSendAllTransactions(transactions);
638
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendAllTransactions success", {
639
+ walletId: this.walletId,
640
+ walletName: this.walletName,
641
+ signatureCount: result.signatures.length
642
+ });
643
+ return result;
166
644
  } catch (error) {
167
- return Promise.reject(error);
645
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendAllTransactions failed", {
646
+ walletId: this.walletId,
647
+ walletName: this.walletName,
648
+ error: error instanceof Error ? error.message : String(error)
649
+ });
650
+ throw error;
168
651
  }
169
652
  }
170
653
  switchNetwork(_network) {
171
654
  return Promise.resolve();
172
655
  }
173
- // Legacy methods
174
656
  getPublicKey() {
175
657
  return Promise.resolve(this._publicKey);
176
658
  }
177
659
  isConnected() {
178
- return this._connected && this.callbacks.isConnected();
660
+ return this._connected;
179
661
  }
180
662
  setupEventListeners() {
181
- this.phantom.solana.addEventListener("connect", (publicKey) => {
182
- this.updateConnectionState(true, publicKey);
183
- this.eventEmitter.emit("connect", publicKey);
184
- });
185
- this.phantom.solana.addEventListener("disconnect", () => {
186
- this.updateConnectionState(false, null);
187
- this.eventEmitter.emit("disconnect");
188
- });
189
- this.phantom.solana.addEventListener("accountChanged", (publicKey) => {
190
- this._publicKey = publicKey;
191
- this.eventEmitter.emit("accountChanged", publicKey);
192
- });
193
- this.callbacks.on("connect", (data) => {
194
- const solanaAddress = data.addresses?.find((addr) => addr.addressType === AddressType2.solana);
195
- if (solanaAddress) {
196
- this.updateConnectionState(true, solanaAddress.address);
197
- }
198
- });
199
- this.callbacks.on("disconnect", () => {
200
- this.updateConnectionState(false, null);
201
- });
202
- }
203
- syncInitialState() {
204
- if (this.callbacks.isConnected()) {
205
- const solanaAddress = this.callbacks.getAddresses().find((addr) => addr.addressType === AddressType2.solana);
206
- if (solanaAddress) {
207
- this.updateConnectionState(true, solanaAddress.address);
208
- }
663
+ if (typeof this.provider.on === "function") {
664
+ this.provider.on("connect", (publicKey) => {
665
+ this._connected = true;
666
+ this._publicKey = publicKey;
667
+ this.eventEmitter.emit("connect", publicKey);
668
+ });
669
+ this.provider.on("disconnect", () => {
670
+ this._connected = false;
671
+ this._publicKey = null;
672
+ this.eventEmitter.emit("disconnect");
673
+ });
674
+ this.provider.on("accountChanged", (publicKey) => {
675
+ this._publicKey = publicKey;
676
+ this._connected = publicKey != null && publicKey.length > 0;
677
+ this.eventEmitter.emit("accountChanged", publicKey);
678
+ });
209
679
  }
210
680
  }
211
- updateConnectionState(connected, publicKey) {
212
- this._connected = connected;
213
- this._publicKey = publicKey;
214
- }
215
- // Event methods for interface compliance
216
681
  on(event, listener) {
217
682
  this.eventEmitter.on(event, listener);
218
683
  }
@@ -221,348 +686,1263 @@ var InjectedSolanaChain = class {
221
686
  }
222
687
  };
223
688
 
224
- // src/providers/injected/chains/EthereumChain.ts
689
+ // src/providers/injected/chains/WalletStandardSolanaAdapter.ts
225
690
  import { EventEmitter as EventEmitter2 } from "eventemitter3";
226
- import { AddressType as AddressType3 } from "@phantom/client";
227
- var InjectedEthereumChain = class {
228
- constructor(phantom, callbacks) {
229
- this._connected = false;
230
- this._chainId = "0x1";
231
- this._accounts = [];
691
+ import { deserializeSolanaTransaction } from "@phantom/parsers";
692
+ import { Buffer as Buffer2 } from "buffer";
693
+ import bs58 from "bs58";
694
+ var WalletStandardSolanaAdapter = class {
695
+ constructor(wallet, walletId, walletName) {
232
696
  this.eventEmitter = new EventEmitter2();
233
- this.phantom = phantom;
234
- this.callbacks = callbacks;
697
+ this._publicKey = null;
698
+ this.wallet = wallet;
699
+ this.walletId = walletId;
700
+ this.walletName = walletName;
235
701
  this.setupEventListeners();
236
- this.syncInitialState();
237
702
  }
238
- // EIP-1193 compliant properties
239
703
  get connected() {
240
- return this._connected;
241
- }
242
- get chainId() {
243
- return this._chainId;
244
- }
245
- get accounts() {
246
- return this._accounts;
704
+ return this._publicKey !== null;
247
705
  }
248
- // EIP-1193 core method with eth_signTransaction support
249
- async request(args) {
250
- if (args.method === "eth_signTransaction") {
251
- const [transaction] = args.params;
252
- const result = await this.signTransaction(transaction);
253
- return result;
254
- }
255
- const provider = await this.phantom.ethereum.getProvider();
256
- return await provider.request(args);
706
+ get publicKey() {
707
+ return this._publicKey;
257
708
  }
258
- // Connection methods - delegate to provider
259
- connect() {
260
- if (!this.callbacks.isConnected()) {
261
- return Promise.reject(new Error("Provider not connected. Call provider connect first."));
709
+ async connect(_options) {
710
+ try {
711
+ const connectFeature = this.wallet.features["standard:connect"];
712
+ if (!connectFeature) {
713
+ throw new Error("Wallet Standard connect feature not available");
714
+ }
715
+ const connectResult = await connectFeature.connect();
716
+ let accounts;
717
+ if (Array.isArray(connectResult) && connectResult.length > 0) {
718
+ accounts = connectResult;
719
+ } else if (this.wallet.accounts && this.wallet.accounts.length > 0) {
720
+ accounts = Array.from(this.wallet.accounts);
721
+ }
722
+ if (!accounts || accounts.length === 0) {
723
+ throw new Error("No accounts available after connecting to wallet");
724
+ }
725
+ const firstAccount = accounts[0];
726
+ if (!firstAccount) {
727
+ throw new Error("First account is null or undefined");
728
+ }
729
+ let address;
730
+ if (typeof firstAccount === "string") {
731
+ address = firstAccount;
732
+ } else if (typeof firstAccount === "object" && firstAccount !== null) {
733
+ address = firstAccount.address || firstAccount.publicKey?.toString() || (firstAccount.publicKey instanceof Uint8Array ? Buffer2.from(firstAccount.publicKey).toString("hex") : void 0);
734
+ }
735
+ if (!address) {
736
+ throw new Error(
737
+ `Could not extract address from account. Account structure: ${JSON.stringify(firstAccount, null, 2)}`
738
+ );
739
+ }
740
+ this._publicKey = address;
741
+ return { publicKey: address };
742
+ } catch (error) {
743
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana connect failed", {
744
+ walletId: this.walletId,
745
+ walletName: this.walletName,
746
+ error: error instanceof Error ? error.message : String(error)
747
+ });
748
+ throw error;
262
749
  }
263
- const addresses = this.callbacks.getAddresses();
264
- const ethAddresses = addresses.filter((addr) => addr.addressType === AddressType3.ethereum).map((addr) => addr.address);
265
- this.updateConnectionState(true, ethAddresses);
266
- return Promise.resolve(ethAddresses);
267
750
  }
268
751
  async disconnect() {
269
- await this.callbacks.disconnect();
270
- }
271
- // Standard compliant methods (return raw values, not wrapped objects)
272
- async signPersonalMessage(message, address) {
273
- return await this.phantom.ethereum.signPersonalMessage(message, address);
752
+ try {
753
+ const disconnectFeature = this.wallet.features["standard:disconnect"];
754
+ if (disconnectFeature) {
755
+ await disconnectFeature.disconnect();
756
+ }
757
+ this._publicKey = null;
758
+ } catch (error) {
759
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana disconnect failed", {
760
+ walletId: this.walletId,
761
+ walletName: this.walletName,
762
+ error: error instanceof Error ? error.message : String(error)
763
+ });
764
+ throw error;
765
+ }
274
766
  }
275
- async signTypedData(typedData, address) {
276
- return await this.phantom.ethereum.signTypedData(typedData, address);
767
+ async signMessage(message) {
768
+ try {
769
+ const signMessageFeature = this.wallet.features["solana:signMessage"];
770
+ if (!signMessageFeature) {
771
+ throw new Error("Wallet Standard signMessage feature not available");
772
+ }
773
+ const messageBytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
774
+ const result = await signMessageFeature.signMessage({
775
+ message: messageBytes,
776
+ account: this.wallet.accounts[0]
777
+ });
778
+ if (!Array.isArray(result) || result.length === 0) {
779
+ throw new Error(`Expected array result from signMessage, got: ${typeof result}`);
780
+ }
781
+ const signedMessageResult = result[0];
782
+ if (!signedMessageResult || !signedMessageResult.signature) {
783
+ throw new Error(`Invalid signMessage result structure: ${JSON.stringify(result)}`);
784
+ }
785
+ const signature = this.parseUint8Array(signedMessageResult.signature);
786
+ if (signature.length === 0) {
787
+ throw new Error(`Signature is empty`);
788
+ }
789
+ const publicKey = signedMessageResult.account?.address || this.wallet.accounts[0]?.address || this._publicKey || "";
790
+ return { signature, publicKey };
791
+ } catch (error) {
792
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signMessage failed", {
793
+ walletId: this.walletId,
794
+ walletName: this.walletName,
795
+ error: error instanceof Error ? error.message : String(error)
796
+ });
797
+ throw error;
798
+ }
277
799
  }
278
800
  async signTransaction(transaction) {
279
- return await this.phantom.ethereum.signTransaction(transaction);
280
- }
281
- async sendTransaction(transaction) {
282
- return await this.phantom.ethereum.sendTransaction(transaction);
283
- }
284
- async switchChain(chainId) {
285
- await this.phantom.ethereum.switchChain(`0x${chainId.toString(16)}`);
286
- this._chainId = `0x${chainId.toString(16)}`;
287
- this.eventEmitter.emit("chainChanged", this._chainId);
288
- }
289
- async getChainId() {
290
- const chainId = await this.phantom.ethereum.getChainId();
291
- return parseInt(chainId, 16);
292
- }
293
- async getAccounts() {
294
- return await this.phantom.ethereum.getAccounts();
295
- }
296
- isConnected() {
297
- return this._connected && this.callbacks.isConnected();
298
- }
299
- setupEventListeners() {
300
- this.phantom.ethereum.addEventListener("connect", (accounts) => {
301
- this.updateConnectionState(true, accounts);
302
- this.eventEmitter.emit("connect", { chainId: this._chainId });
303
- this.eventEmitter.emit("accountsChanged", accounts);
304
- });
305
- this.phantom.ethereum.addEventListener("disconnect", () => {
306
- this.updateConnectionState(false, []);
307
- this.eventEmitter.emit("disconnect", { code: 4900, message: "Provider disconnected" });
308
- this.eventEmitter.emit("accountsChanged", []);
309
- });
310
- this.phantom.ethereum.addEventListener("accountsChanged", (accounts) => {
311
- this._accounts = accounts;
312
- this.eventEmitter.emit("accountsChanged", accounts);
313
- });
314
- this.phantom.ethereum.addEventListener("chainChanged", (chainId) => {
315
- this._chainId = chainId;
316
- this.eventEmitter.emit("chainChanged", chainId);
317
- });
318
- this.callbacks.on("connect", (data) => {
319
- const ethAddresses = data.addresses?.filter((addr) => addr.addressType === AddressType3.ethereum)?.map((addr) => addr.address) || [];
320
- if (ethAddresses.length > 0) {
321
- this.updateConnectionState(true, ethAddresses);
801
+ try {
802
+ const signTransactionFeature = this.wallet.features["solana:signTransaction"];
803
+ if (!signTransactionFeature) {
804
+ throw new Error("Wallet Standard signTransaction feature not available");
322
805
  }
323
- });
324
- this.callbacks.on("disconnect", () => {
325
- this.updateConnectionState(false, []);
326
- });
327
- }
328
- syncInitialState() {
329
- if (this.callbacks.isConnected()) {
330
- const ethAddresses = this.callbacks.getAddresses().filter((addr) => addr.addressType === AddressType3.ethereum).map((addr) => addr.address);
331
- if (ethAddresses.length > 0) {
332
- this.updateConnectionState(true, ethAddresses);
806
+ if (!this.wallet.accounts || this.wallet.accounts.length === 0) {
807
+ throw new Error("No accounts available. Please connect first.");
333
808
  }
809
+ const account = this.wallet.accounts[0];
810
+ const serializedTransaction = this.serializeTransaction(transaction);
811
+ const results = await signTransactionFeature.signTransaction({
812
+ transaction: serializedTransaction,
813
+ account
814
+ });
815
+ let transactionData;
816
+ if (Array.isArray(results) && results.length > 0) {
817
+ const firstItem = results[0];
818
+ if (firstItem && typeof firstItem === "object") {
819
+ transactionData = firstItem.signedTransaction || firstItem.transaction;
820
+ }
821
+ } else if (results && typeof results === "object" && !Array.isArray(results)) {
822
+ const resultObj = results;
823
+ transactionData = resultObj.transaction || resultObj.signedTransaction;
824
+ }
825
+ if (!transactionData) {
826
+ throw new Error("No transaction data found in Wallet Standard result");
827
+ }
828
+ const signedBytes = this.parseUint8Array(transactionData);
829
+ if (signedBytes.length === 0) {
830
+ throw new Error("Empty signed transaction returned from Wallet Standard");
831
+ }
832
+ const signedTx = deserializeSolanaTransaction(signedBytes);
833
+ return signedTx;
834
+ } catch (error) {
835
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signTransaction failed", {
836
+ walletId: this.walletId,
837
+ walletName: this.walletName,
838
+ error: error instanceof Error ? error.message : String(error)
839
+ });
840
+ throw error;
334
841
  }
335
842
  }
336
- updateConnectionState(connected, accounts) {
337
- this._connected = connected;
338
- this._accounts = accounts;
339
- }
340
- // Event methods for interface compliance
341
- on(event, listener) {
342
- this.eventEmitter.on(event, listener);
343
- }
344
- off(event, listener) {
345
- this.eventEmitter.off(event, listener);
843
+ async signAndSendTransaction(transaction) {
844
+ try {
845
+ const signAndSendTransactionFeature = this.wallet.features["solana:signAndSendTransaction"];
846
+ if (!signAndSendTransactionFeature) {
847
+ throw new Error("Wallet Standard signAndSendTransaction feature not available");
848
+ }
849
+ if (!this.wallet.accounts || this.wallet.accounts.length === 0) {
850
+ throw new Error("No accounts available. Please connect first.");
851
+ }
852
+ const account = this.wallet.accounts[0];
853
+ const chain = account.chains?.[0] || "solana:mainnet";
854
+ const serializedTransaction = this.serializeTransaction(transaction);
855
+ const results = await signAndSendTransactionFeature.signAndSendTransaction({
856
+ transaction: serializedTransaction,
857
+ account,
858
+ chain
859
+ });
860
+ let signatureOutput;
861
+ if (Array.isArray(results) && results.length > 0) {
862
+ signatureOutput = results[0];
863
+ } else if (results && typeof results === "object" && !Array.isArray(results)) {
864
+ signatureOutput = results;
865
+ } else {
866
+ throw new Error("Invalid signAndSendTransaction result format");
867
+ }
868
+ if (!signatureOutput.signature) {
869
+ throw new Error("No signature found in signAndSendTransaction result");
870
+ }
871
+ const signatureBytes = this.parseUint8Array(signatureOutput.signature);
872
+ const signature = bs58.encode(signatureBytes);
873
+ return { signature };
874
+ } catch (error) {
875
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signAndSendTransaction failed", {
876
+ walletId: this.walletId,
877
+ walletName: this.walletName,
878
+ error: error instanceof Error ? error.message : String(error)
879
+ });
880
+ throw error;
881
+ }
882
+ }
883
+ async signAllTransactions(transactions) {
884
+ try {
885
+ const signedTransactions = [];
886
+ for (const transaction of transactions) {
887
+ const signedTx = await this.signTransaction(transaction);
888
+ signedTransactions.push(signedTx);
889
+ }
890
+ return signedTransactions;
891
+ } catch (error) {
892
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signAllTransactions failed", {
893
+ walletId: this.walletId,
894
+ walletName: this.walletName,
895
+ error: error instanceof Error ? error.message : String(error)
896
+ });
897
+ throw error;
898
+ }
899
+ }
900
+ async signAndSendAllTransactions(transactions) {
901
+ try {
902
+ const signatures = [];
903
+ for (const transaction of transactions) {
904
+ const result = await this.signAndSendTransaction(transaction);
905
+ signatures.push(result.signature);
906
+ }
907
+ return { signatures };
908
+ } catch (error) {
909
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signAndSendAllTransactions failed", {
910
+ walletId: this.walletId,
911
+ walletName: this.walletName,
912
+ error: error instanceof Error ? error.message : String(error)
913
+ });
914
+ throw error;
915
+ }
916
+ }
917
+ async switchNetwork(_network) {
918
+ return Promise.resolve();
919
+ }
920
+ getPublicKey() {
921
+ return Promise.resolve(this._publicKey);
922
+ }
923
+ isConnected() {
924
+ return this._publicKey !== null;
925
+ }
926
+ /**
927
+ * Set up event listeners for Wallet Standard events
928
+ * Maps Wallet Standard "change" events to "accountChanged" events
929
+ *
930
+ * Note: Wallet Standard only has a "change" event. There are no "connect" or "disconnect" events.
931
+ * Connection/disconnection is indicated by the presence or absence of accounts in the change event.
932
+ */
933
+ setupEventListeners() {
934
+ const eventsFeature = this.wallet.features["standard:events"];
935
+ if (!eventsFeature) {
936
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Wallet Standard events feature not available", {
937
+ walletId: this.walletId,
938
+ walletName: this.walletName
939
+ });
940
+ return;
941
+ }
942
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Wallet Standard event listeners", {
943
+ walletId: this.walletId,
944
+ walletName: this.walletName
945
+ });
946
+ eventsFeature.on("change", (properties) => {
947
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Wallet Standard change event received", {
948
+ walletId: this.walletId,
949
+ walletName: this.walletName,
950
+ hasAccounts: !!properties.accounts,
951
+ accountCount: properties.accounts?.length || 0
952
+ });
953
+ if (properties.accounts !== void 0) {
954
+ if (properties.accounts.length > 0) {
955
+ const firstAccount = properties.accounts[0];
956
+ const address = this.extractAccountAddress(firstAccount);
957
+ if (address) {
958
+ this._publicKey = address;
959
+ this.eventEmitter.emit("accountChanged", address);
960
+ this.eventEmitter.emit("connect", address);
961
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Emitted accountChanged and connect events", {
962
+ walletId: this.walletId,
963
+ walletName: this.walletName,
964
+ address
965
+ });
966
+ } else {
967
+ this._publicKey = null;
968
+ this.eventEmitter.emit("accountChanged", null);
969
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Emitted accountChanged event (null - invalid account)", {
970
+ walletId: this.walletId,
971
+ walletName: this.walletName
972
+ });
973
+ }
974
+ } else {
975
+ this._publicKey = null;
976
+ this.eventEmitter.emit("accountChanged", null);
977
+ this.eventEmitter.emit("disconnect");
978
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Emitted accountChanged and disconnect events", {
979
+ walletId: this.walletId,
980
+ walletName: this.walletName
981
+ });
982
+ }
983
+ }
984
+ });
985
+ }
986
+ extractAccountAddress(account) {
987
+ return account.address;
988
+ }
989
+ on(event, listener) {
990
+ this.eventEmitter.on(event, listener);
991
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Added event listener", {
992
+ walletId: this.walletId,
993
+ walletName: this.walletName,
994
+ event
995
+ });
996
+ }
997
+ off(event, listener) {
998
+ this.eventEmitter.off(event, listener);
999
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Removed event listener", {
1000
+ walletId: this.walletId,
1001
+ walletName: this.walletName,
1002
+ event
1003
+ });
1004
+ }
1005
+ /**
1006
+ * Serialize a transaction to Uint8Array for Wallet Standard API
1007
+ */
1008
+ serializeTransaction(transaction) {
1009
+ if (typeof transaction.serialize === "function") {
1010
+ return transaction.serialize({
1011
+ requireAllSignatures: false,
1012
+ verifySignatures: false
1013
+ });
1014
+ }
1015
+ if (transaction instanceof Uint8Array) {
1016
+ return transaction;
1017
+ }
1018
+ return new Uint8Array(0);
1019
+ }
1020
+ /**
1021
+ * Parse a Uint8Array from various formats
1022
+ * Handles: Uint8Array, Array, object with numeric keys (JSON-serialized Uint8Array)
1023
+ */
1024
+ parseUint8Array(value) {
1025
+ if (value instanceof Uint8Array) {
1026
+ return value;
1027
+ }
1028
+ if (Array.isArray(value)) {
1029
+ return new Uint8Array(value);
1030
+ }
1031
+ if (typeof value === "object" && value !== null) {
1032
+ const keys = Object.keys(value).map(Number).filter((k) => !isNaN(k) && k >= 0).sort((a, b) => a - b);
1033
+ if (keys.length > 0) {
1034
+ const maxKey = Math.max(...keys);
1035
+ const array = new Uint8Array(maxKey + 1);
1036
+ for (const key of keys) {
1037
+ array[key] = Number(value[key]) || 0;
1038
+ }
1039
+ return array;
1040
+ }
1041
+ }
1042
+ return new Uint8Array(0);
1043
+ }
1044
+ };
1045
+
1046
+ // src/providers/injected/chains/InjectedWalletEthereumChain.ts
1047
+ import { EventEmitter as EventEmitter3 } from "eventemitter3";
1048
+ var InjectedWalletEthereumChain = class {
1049
+ constructor(provider, walletId, walletName) {
1050
+ // Expose eventEmitter for testing - allows tests to trigger events directly
1051
+ this.eventEmitter = new EventEmitter3();
1052
+ this._connected = false;
1053
+ this._chainId = "0x1";
1054
+ this._accounts = [];
1055
+ this.provider = provider;
1056
+ this.walletId = walletId;
1057
+ this.walletName = walletName;
1058
+ this.setupEventListeners();
1059
+ }
1060
+ get connected() {
1061
+ return this._connected;
1062
+ }
1063
+ get chainId() {
1064
+ return this._chainId;
1065
+ }
1066
+ get accounts() {
1067
+ return this._accounts;
1068
+ }
1069
+ async request(args) {
1070
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request", {
1071
+ walletId: this.walletId,
1072
+ walletName: this.walletName,
1073
+ method: args.method
1074
+ });
1075
+ try {
1076
+ const requiresAuth = [
1077
+ "personal_sign",
1078
+ "eth_sign",
1079
+ "eth_signTypedData",
1080
+ "eth_signTypedData_v4",
1081
+ "eth_sendTransaction",
1082
+ "eth_signTransaction"
1083
+ ].includes(args.method);
1084
+ if (requiresAuth && (!this._connected || this._accounts.length === 0)) {
1085
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Method requires authorization, ensuring connection", {
1086
+ walletId: this.walletId,
1087
+ walletName: this.walletName,
1088
+ method: args.method
1089
+ });
1090
+ await this.connect();
1091
+ }
1092
+ const result = await this.provider.request(args);
1093
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request success", {
1094
+ walletId: this.walletId,
1095
+ walletName: this.walletName,
1096
+ method: args.method
1097
+ });
1098
+ return result;
1099
+ } catch (error) {
1100
+ if (error?.code === 4100) {
1101
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Got 4100 Unauthorized, attempting to re-authorize", {
1102
+ walletId: this.walletId,
1103
+ walletName: this.walletName,
1104
+ method: args.method
1105
+ });
1106
+ try {
1107
+ await this.provider.request({ method: "eth_requestAccounts" });
1108
+ const result = await this.provider.request(args);
1109
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request success (after re-auth)", {
1110
+ walletId: this.walletId,
1111
+ walletName: this.walletName,
1112
+ method: args.method
1113
+ });
1114
+ return result;
1115
+ } catch (retryError) {
1116
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Failed after re-authorization", {
1117
+ walletId: this.walletId,
1118
+ walletName: this.walletName,
1119
+ method: args.method,
1120
+ error: retryError instanceof Error ? retryError.message : String(retryError)
1121
+ });
1122
+ throw retryError;
1123
+ }
1124
+ }
1125
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request failed", {
1126
+ walletId: this.walletId,
1127
+ walletName: this.walletName,
1128
+ method: args.method,
1129
+ error: error instanceof Error ? error.message : String(error),
1130
+ errorCode: error?.code
1131
+ });
1132
+ throw error;
1133
+ }
1134
+ }
1135
+ async connect() {
1136
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum connect", {
1137
+ walletId: this.walletId,
1138
+ walletName: this.walletName
1139
+ });
1140
+ try {
1141
+ const accounts = await this.provider.request({ method: "eth_requestAccounts" });
1142
+ this._connected = accounts.length > 0;
1143
+ this._accounts = accounts;
1144
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum connected (via eth_requestAccounts)", {
1145
+ walletId: this.walletId,
1146
+ walletName: this.walletName,
1147
+ accountCount: accounts.length,
1148
+ accounts
1149
+ });
1150
+ return accounts;
1151
+ } catch (error) {
1152
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum connect failed", {
1153
+ walletId: this.walletId,
1154
+ walletName: this.walletName,
1155
+ error: error instanceof Error ? error.message : String(error)
1156
+ });
1157
+ throw error;
1158
+ }
1159
+ }
1160
+ async disconnect() {
1161
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum disconnect", {
1162
+ walletId: this.walletId,
1163
+ walletName: this.walletName
1164
+ });
1165
+ try {
1166
+ await this.provider.disconnect();
1167
+ this._connected = false;
1168
+ this._accounts = [];
1169
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum disconnected", {
1170
+ walletId: this.walletId,
1171
+ walletName: this.walletName
1172
+ });
1173
+ } catch (error) {
1174
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum disconnect failed", {
1175
+ walletId: this.walletId,
1176
+ walletName: this.walletName,
1177
+ error: error instanceof Error ? error.message : String(error)
1178
+ });
1179
+ throw error;
1180
+ }
1181
+ }
1182
+ async signPersonalMessage(message, address) {
1183
+ const messagePreview = message.length > 50 ? message.substring(0, 50) + "..." : message;
1184
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage", {
1185
+ walletId: this.walletId,
1186
+ walletName: this.walletName,
1187
+ messagePreview,
1188
+ messageLength: message.length,
1189
+ address
1190
+ });
1191
+ try {
1192
+ const providerConnected = this.provider.isConnected?.() || this.provider.connected || false;
1193
+ if (!this._connected || this._accounts.length === 0 || !providerConnected) {
1194
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Not connected, attempting to connect before signing", {
1195
+ walletId: this.walletId,
1196
+ walletName: this.walletName,
1197
+ internalConnected: this._connected,
1198
+ accountsLength: this._accounts.length,
1199
+ providerConnected
1200
+ });
1201
+ await this.connect();
1202
+ }
1203
+ const normalizedAddress = address.toLowerCase();
1204
+ const normalizedAccounts = this._accounts.map((acc) => acc.toLowerCase());
1205
+ if (!normalizedAccounts.includes(normalizedAddress)) {
1206
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Address not in connected accounts, refreshing connection", {
1207
+ walletId: this.walletId,
1208
+ walletName: this.walletName,
1209
+ requestedAddress: address,
1210
+ connectedAccounts: this._accounts
1211
+ });
1212
+ const currentAccounts = await this.getAccounts();
1213
+ const normalizedCurrentAccounts = currentAccounts.map((acc) => acc.toLowerCase());
1214
+ if (!normalizedCurrentAccounts.includes(normalizedAddress)) {
1215
+ throw new Error(`Address ${address} is not connected. Connected accounts: ${currentAccounts.join(", ")}`);
1216
+ }
1217
+ this._accounts = currentAccounts;
1218
+ }
1219
+ if (typeof this.provider.signPersonalMessage === "function") {
1220
+ const result2 = await this.provider.signPersonalMessage(message, address);
1221
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage success", {
1222
+ walletId: this.walletId,
1223
+ walletName: this.walletName,
1224
+ signatureLength: result2.length
1225
+ });
1226
+ return result2;
1227
+ }
1228
+ const result = await this.request({
1229
+ method: "personal_sign",
1230
+ params: [message, address]
1231
+ });
1232
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage success", {
1233
+ walletId: this.walletId,
1234
+ walletName: this.walletName,
1235
+ signatureLength: result.length
1236
+ });
1237
+ return result;
1238
+ } catch (error) {
1239
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage failed", {
1240
+ walletId: this.walletId,
1241
+ walletName: this.walletName,
1242
+ error: error instanceof Error ? error.message : String(error),
1243
+ errorCode: error?.code
1244
+ });
1245
+ throw error;
1246
+ }
1247
+ }
1248
+ async signTypedData(typedData, address) {
1249
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData", {
1250
+ walletId: this.walletId,
1251
+ walletName: this.walletName,
1252
+ primaryType: typedData?.primaryType,
1253
+ address
1254
+ });
1255
+ try {
1256
+ if (typeof this.provider.signTypedData === "function") {
1257
+ const result2 = await this.provider.signTypedData(typedData, address);
1258
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData success", {
1259
+ walletId: this.walletId,
1260
+ walletName: this.walletName,
1261
+ signatureLength: result2.length
1262
+ });
1263
+ return result2;
1264
+ }
1265
+ const result = await this.request({
1266
+ method: "eth_signTypedData_v4",
1267
+ params: [address, typedData]
1268
+ });
1269
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData success", {
1270
+ walletId: this.walletId,
1271
+ walletName: this.walletName,
1272
+ signatureLength: result.length
1273
+ });
1274
+ return result;
1275
+ } catch (error) {
1276
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData failed", {
1277
+ walletId: this.walletId,
1278
+ walletName: this.walletName,
1279
+ error: error instanceof Error ? error.message : String(error)
1280
+ });
1281
+ throw error;
1282
+ }
1283
+ }
1284
+ async signTransaction(transaction) {
1285
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction", {
1286
+ walletId: this.walletId,
1287
+ walletName: this.walletName,
1288
+ from: transaction.from,
1289
+ to: transaction.to
1290
+ });
1291
+ try {
1292
+ if (typeof this.provider.signTransaction === "function") {
1293
+ const result2 = await this.provider.signTransaction(transaction);
1294
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction success", {
1295
+ walletId: this.walletId,
1296
+ walletName: this.walletName,
1297
+ signatureLength: result2.length
1298
+ });
1299
+ return result2;
1300
+ }
1301
+ const result = await this.request({
1302
+ method: "eth_signTransaction",
1303
+ params: [transaction]
1304
+ });
1305
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction success", {
1306
+ walletId: this.walletId,
1307
+ walletName: this.walletName,
1308
+ signatureLength: result.length
1309
+ });
1310
+ return result;
1311
+ } catch (error) {
1312
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction failed", {
1313
+ walletId: this.walletId,
1314
+ walletName: this.walletName,
1315
+ error: error instanceof Error ? error.message : String(error)
1316
+ });
1317
+ throw error;
1318
+ }
1319
+ }
1320
+ async sendTransaction(transaction) {
1321
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction", {
1322
+ walletId: this.walletId,
1323
+ walletName: this.walletName,
1324
+ from: transaction.from,
1325
+ to: transaction.to,
1326
+ value: transaction.value
1327
+ });
1328
+ try {
1329
+ if (typeof this.provider.sendTransaction === "function") {
1330
+ const result2 = await this.provider.sendTransaction(transaction);
1331
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction success", {
1332
+ walletId: this.walletId,
1333
+ walletName: this.walletName,
1334
+ txHash: result2
1335
+ });
1336
+ return result2;
1337
+ }
1338
+ const result = await this.request({
1339
+ method: "eth_sendTransaction",
1340
+ params: [transaction]
1341
+ });
1342
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction success", {
1343
+ walletId: this.walletId,
1344
+ walletName: this.walletName,
1345
+ txHash: result
1346
+ });
1347
+ return result;
1348
+ } catch (error) {
1349
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction failed", {
1350
+ walletId: this.walletId,
1351
+ walletName: this.walletName,
1352
+ error: error instanceof Error ? error.message : String(error)
1353
+ });
1354
+ throw error;
1355
+ }
1356
+ }
1357
+ async switchChain(chainId) {
1358
+ debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum switchChain", {
1359
+ walletId: this.walletId,
1360
+ walletName: this.walletName,
1361
+ chainId
1362
+ });
1363
+ try {
1364
+ const hexChainId = typeof chainId === "string" ? chainId.toLowerCase().startsWith("0x") ? chainId : `0x${parseInt(chainId, 10).toString(16)}` : `0x${chainId.toString(16)}`;
1365
+ if (typeof this.provider.switchChain === "function") {
1366
+ await this.provider.switchChain(hexChainId);
1367
+ } else {
1368
+ await this.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }] });
1369
+ }
1370
+ this._chainId = hexChainId;
1371
+ this.eventEmitter.emit("chainChanged", this._chainId);
1372
+ debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum switchChain success", {
1373
+ walletId: this.walletId,
1374
+ walletName: this.walletName,
1375
+ chainId: hexChainId
1376
+ });
1377
+ } catch (error) {
1378
+ debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum switchChain failed", {
1379
+ walletId: this.walletId,
1380
+ walletName: this.walletName,
1381
+ error: error instanceof Error ? error.message : String(error)
1382
+ });
1383
+ throw error;
1384
+ }
1385
+ }
1386
+ async getChainId() {
1387
+ if (typeof this.provider.getChainId === "function") {
1388
+ const chainId2 = await this.provider.getChainId();
1389
+ this._chainId = `0x${chainId2.toString(16)}`;
1390
+ return chainId2;
1391
+ }
1392
+ const chainId = await this.request({ method: "eth_chainId" });
1393
+ const parsed = parseInt(chainId, 16);
1394
+ this._chainId = chainId;
1395
+ return parsed;
1396
+ }
1397
+ async getAccounts() {
1398
+ if (typeof this.provider.getAccounts === "function") {
1399
+ const accounts2 = await this.provider.getAccounts();
1400
+ this._accounts = accounts2;
1401
+ return accounts2;
1402
+ }
1403
+ const accounts = await this.request({ method: "eth_accounts" });
1404
+ this._accounts = accounts;
1405
+ return accounts;
1406
+ }
1407
+ isConnected() {
1408
+ return this._connected;
1409
+ }
1410
+ setupEventListeners() {
1411
+ if (typeof this.provider.on === "function") {
1412
+ this.provider.on("connect", (info) => {
1413
+ this._connected = true;
1414
+ this._chainId = info.chainId;
1415
+ this.eventEmitter.emit("connect", info);
1416
+ });
1417
+ this.provider.on("disconnect", (error) => {
1418
+ this._connected = false;
1419
+ this._accounts = [];
1420
+ this.eventEmitter.emit("disconnect", error);
1421
+ this.eventEmitter.emit("accountsChanged", []);
1422
+ });
1423
+ this.provider.on("accountsChanged", (accounts) => {
1424
+ this._accounts = accounts;
1425
+ this._connected = accounts.length > 0;
1426
+ this.eventEmitter.emit("accountsChanged", accounts);
1427
+ });
1428
+ this.provider.on("chainChanged", (chainId) => {
1429
+ this._chainId = chainId;
1430
+ this.eventEmitter.emit("chainChanged", chainId);
1431
+ });
1432
+ }
1433
+ }
1434
+ on(event, listener) {
1435
+ this.eventEmitter.on(event, listener);
1436
+ }
1437
+ off(event, listener) {
1438
+ this.eventEmitter.off(event, listener);
1439
+ }
1440
+ };
1441
+
1442
+ // src/wallets/registry.ts
1443
+ function isPhantomWallet(wallet) {
1444
+ return wallet !== void 0 && wallet.id === "phantom" && "isPhantom" in wallet && wallet.isPhantom === true;
1445
+ }
1446
+ function isWalletStandardWallet(provider) {
1447
+ return provider !== null && typeof provider === "object" && "features" in provider && typeof provider.features === "object";
1448
+ }
1449
+ var InjectedWalletRegistry = class {
1450
+ constructor() {
1451
+ this.wallets = /* @__PURE__ */ new Map();
1452
+ this.discoveryPromise = null;
1453
+ }
1454
+ register(info) {
1455
+ const wrappedProviders = {};
1456
+ if (info.providers?.solana) {
1457
+ if (isWalletStandardWallet(info.providers.solana)) {
1458
+ wrappedProviders.solana = new WalletStandardSolanaAdapter(info.providers.solana, info.id, info.name);
1459
+ debug.log(DebugCategory.BROWSER_SDK, "Wrapped Wallet Standard Solana wallet with adapter", {
1460
+ walletId: info.id,
1461
+ walletName: info.name
1462
+ });
1463
+ } else {
1464
+ wrappedProviders.solana = new InjectedWalletSolanaChain(info.providers.solana, info.id, info.name);
1465
+ debug.log(DebugCategory.BROWSER_SDK, "Wrapped Solana provider with InjectedWalletSolanaChain", {
1466
+ walletId: info.id,
1467
+ walletName: info.name
1468
+ });
1469
+ }
1470
+ }
1471
+ if (info.providers?.ethereum) {
1472
+ wrappedProviders.ethereum = new InjectedWalletEthereumChain(info.providers.ethereum, info.id, info.name);
1473
+ debug.log(DebugCategory.BROWSER_SDK, "Wrapped Ethereum provider with InjectedWalletEthereumChain", {
1474
+ walletId: info.id,
1475
+ walletName: info.name
1476
+ });
1477
+ }
1478
+ const wrappedInfo = {
1479
+ ...info,
1480
+ providers: Object.keys(wrappedProviders).length > 0 ? wrappedProviders : info.providers
1481
+ };
1482
+ this.wallets.set(info.id, wrappedInfo);
1483
+ }
1484
+ /**
1485
+ * Register Phantom wallet with its instance
1486
+ * This creates wrapped providers and stores the Phantom instance for auto-confirm access
1487
+ * Uses unified InjectedWallet chains for both Phantom and external wallets
1488
+ */
1489
+ registerPhantom(phantomInstance, addressTypes) {
1490
+ const wrappedProviders = {};
1491
+ if (addressTypes.includes(AddressType.solana) && phantomInstance.solana) {
1492
+ wrappedProviders.solana = new InjectedWalletSolanaChain(phantomInstance.solana, "phantom", "Phantom");
1493
+ debug.log(DebugCategory.BROWSER_SDK, "Created InjectedWalletSolanaChain wrapper for Phantom", {
1494
+ walletId: "phantom"
1495
+ });
1496
+ }
1497
+ if (addressTypes.includes(AddressType.ethereum) && phantomInstance.ethereum) {
1498
+ wrappedProviders.ethereum = new InjectedWalletEthereumChain(phantomInstance.ethereum, "phantom", "Phantom");
1499
+ debug.log(DebugCategory.BROWSER_SDK, "Created InjectedWalletEthereumChain wrapper for Phantom", {
1500
+ walletId: "phantom"
1501
+ });
1502
+ }
1503
+ const phantomWallet = {
1504
+ id: "phantom",
1505
+ name: "Phantom",
1506
+ icon: "",
1507
+ // Icon will be rendered from icons package in UI components
1508
+ addressTypes,
1509
+ providers: wrappedProviders,
1510
+ isPhantom: true,
1511
+ phantomInstance,
1512
+ discovery: "phantom"
1513
+ };
1514
+ this.wallets.set("phantom", phantomWallet);
1515
+ debug.log(DebugCategory.BROWSER_SDK, "Registered Phantom wallet with chain wrappers", {
1516
+ addressTypes,
1517
+ hasSolana: !!wrappedProviders.solana,
1518
+ hasEthereum: !!wrappedProviders.ethereum
1519
+ });
1520
+ }
1521
+ unregister(id) {
1522
+ this.wallets.delete(id);
1523
+ }
1524
+ has(id) {
1525
+ return this.wallets.has(id);
1526
+ }
1527
+ getById(id) {
1528
+ return this.wallets.get(id);
1529
+ }
1530
+ getAll() {
1531
+ return Array.from(this.wallets.values());
1532
+ }
1533
+ getByAddressTypes(addressTypes) {
1534
+ if (addressTypes.length === 0) {
1535
+ return this.getAll();
1536
+ }
1537
+ const allowed = new Set(addressTypes);
1538
+ return this.getAll().filter((wallet) => wallet.addressTypes.some((t) => allowed.has(t)));
1539
+ }
1540
+ discover(addressTypes) {
1541
+ if (this.discoveryPromise) {
1542
+ return this.discoveryPromise;
1543
+ }
1544
+ debug.log(DebugCategory.BROWSER_SDK, "Starting wallet discovery", { addressTypes });
1545
+ this.discoveryPromise = discoverWallets(addressTypes).then((discoveredWallets) => {
1546
+ const relevantWallets = addressTypes ? discoveredWallets.filter((wallet) => wallet.addressTypes.some((type) => addressTypes.includes(type))) : discoveredWallets;
1547
+ for (const wallet of relevantWallets) {
1548
+ if (wallet.id === "phantom" && isPhantomWallet(wallet)) {
1549
+ this.registerPhantom(wallet.phantomInstance, wallet.addressTypes);
1550
+ } else {
1551
+ this.register(wallet);
1552
+ }
1553
+ debug.log(DebugCategory.BROWSER_SDK, "Registered discovered wallet", {
1554
+ id: wallet.id,
1555
+ name: wallet.name,
1556
+ addressTypes: wallet.addressTypes
1557
+ });
1558
+ }
1559
+ debug.info(DebugCategory.BROWSER_SDK, "Wallet discovery completed", {
1560
+ totalDiscovered: discoveredWallets.length,
1561
+ relevantWallets: relevantWallets.length
1562
+ });
1563
+ }).catch((error) => {
1564
+ debug.warn(DebugCategory.BROWSER_SDK, "Wallet discovery failed", { error });
1565
+ this.discoveryPromise = null;
1566
+ throw error;
1567
+ });
1568
+ return this.discoveryPromise;
346
1569
  }
347
1570
  };
1571
+ var walletRegistry = null;
1572
+ function getWalletRegistry() {
1573
+ if (!walletRegistry) {
1574
+ walletRegistry = new InjectedWalletRegistry();
1575
+ }
1576
+ return walletRegistry;
1577
+ }
348
1578
 
349
1579
  // src/providers/injected/index.ts
1580
+ var WAS_CONNECTED_KEY = "phantom-injected-was-connected";
1581
+ var WAS_CONNECTED_VALUE = "true";
1582
+ var LAST_WALLET_ID_KEY = "phantom-injected-last-wallet-id";
350
1583
  var InjectedProvider = class {
1584
+ // Store cleanups per walletId
351
1585
  constructor(config) {
352
- this.connected = false;
353
- this.addresses = [];
1586
+ this.selectedWalletId = null;
1587
+ this.walletStates = /* @__PURE__ */ new Map();
354
1588
  // Event management
355
1589
  this.eventListeners = /* @__PURE__ */ new Map();
356
- this.browserInjectedCleanupFunctions = [];
357
1590
  this.eventsInitialized = false;
1591
+ this.eventListenersSetup = /* @__PURE__ */ new Set();
1592
+ // Track walletId that have listeners set up
1593
+ this.eventListenerCleanups = /* @__PURE__ */ new Map();
358
1594
  debug.log(DebugCategory.INJECTED_PROVIDER, "Initializing InjectedProvider", { config });
359
1595
  this.addressTypes = config.addressTypes;
1596
+ this.walletRegistry = getWalletRegistry();
360
1597
  debug.log(DebugCategory.INJECTED_PROVIDER, "Address types configured", { addressTypes: this.addressTypes });
361
- const plugins = [createExtensionPlugin()];
362
- if (this.addressTypes.includes(AddressType4.solana)) {
363
- plugins.push(createSolanaPlugin());
364
- debug.log(DebugCategory.INJECTED_PROVIDER, "Solana plugin added");
365
- }
366
- if (this.addressTypes.includes(AddressType4.ethereum)) {
367
- plugins.push(createEthereumPlugin());
368
- debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum plugin added");
369
- }
370
- plugins.push(createAutoConfirmPlugin());
371
- debug.log(DebugCategory.INJECTED_PROVIDER, "AutoConfirm plugin added");
372
- debug.log(DebugCategory.INJECTED_PROVIDER, "Creating Phantom instance with plugins", {
373
- pluginCount: plugins.length
1598
+ this.walletRegistry.discover(this.addressTypes).catch((error) => {
1599
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Wallet discovery failed during initialization", { error });
374
1600
  });
375
- this.phantom = createPhantom({ plugins });
376
- const callbacks = this.createCallbacks();
377
- if (this.addressTypes.includes(AddressType4.solana)) {
378
- this._solanaChain = new InjectedSolanaChain(this.phantom, callbacks);
1601
+ debug.info(DebugCategory.INJECTED_PROVIDER, "InjectedProvider initialized");
1602
+ }
1603
+ /**
1604
+ * Wait for wallet discovery to complete if the wallet is not yet in the registry
1605
+ * This is needed for auto-connect when the last wallet was an external wallet
1606
+ */
1607
+ async waitForWalletDiscovery(walletId) {
1608
+ if (this.walletRegistry.has(walletId)) {
1609
+ return;
379
1610
  }
380
- if (this.addressTypes.includes(AddressType4.ethereum)) {
381
- this._ethereumChain = new InjectedEthereumChain(this.phantom, callbacks);
1611
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Wallet not found in registry, waiting for discovery", {
1612
+ walletId
1613
+ });
1614
+ try {
1615
+ await this.walletRegistry.discover(this.addressTypes);
1616
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Wallet discovery completed", { walletId });
1617
+ } catch (error) {
1618
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Wallet discovery failed", {
1619
+ walletId,
1620
+ error: error instanceof Error ? error.message : String(error)
1621
+ });
382
1622
  }
383
- debug.info(DebugCategory.INJECTED_PROVIDER, "InjectedProvider initialized");
384
1623
  }
385
1624
  /**
386
- * Access to Solana chain operations
1625
+ * Helper method to get a chain provider with consistent error handling
387
1626
  */
1627
+ getChainProvider(addressType, providerKey, chainName) {
1628
+ if (!this.addressTypes.includes(addressType)) {
1629
+ throw new Error(`${chainName} not enabled for this provider`);
1630
+ }
1631
+ const walletId = this.selectedWalletId || "phantom";
1632
+ const walletInfo = this.walletRegistry.getById(walletId);
1633
+ if (!walletInfo) {
1634
+ const registry = this.walletRegistry;
1635
+ if (registry.discoveryPromise) {
1636
+ throw new Error(
1637
+ `Wallet "${walletId}" not found. Wallet discovery is still in progress. Please wait for sdk.discoverWallets() to complete before accessing chain properties.`
1638
+ );
1639
+ }
1640
+ throw new Error(
1641
+ `Wallet "${walletId}" not found. Please ensure wallet discovery has completed. Make sure you call sdk.discoverWallets() and await it before accessing chain properties.`
1642
+ );
1643
+ }
1644
+ const provider = walletInfo.providers?.[providerKey];
1645
+ if (!provider) {
1646
+ throw new Error(
1647
+ `Selected wallet "${walletInfo.name}" does not support ${chainName}. This wallet only supports: ${walletInfo.addressTypes.join(", ")}. Make sure your SDK config includes ${chainName} in addressTypes.`
1648
+ );
1649
+ }
1650
+ return provider;
1651
+ }
388
1652
  get solana() {
389
- if (!this.addressTypes.includes(AddressType4.solana)) {
390
- throw new Error("Solana not enabled for this provider");
1653
+ return this.getChainProvider(AddressType2.solana, "solana", "Solana");
1654
+ }
1655
+ /**
1656
+ * Access to Ethereum chain operations
1657
+ */
1658
+ get ethereum() {
1659
+ return this.getChainProvider(AddressType2.ethereum, "ethereum", "Ethereum");
1660
+ }
1661
+ validateAndSelectWallet(requestedWalletId) {
1662
+ if (!this.walletRegistry.has(requestedWalletId)) {
1663
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Unknown injected wallet id requested", {
1664
+ walletId: requestedWalletId
1665
+ });
1666
+ throw new Error(`Unknown injected wallet id: ${requestedWalletId}`);
1667
+ }
1668
+ const walletInfo = this.walletRegistry.getById(requestedWalletId);
1669
+ if (!walletInfo || !walletInfo.providers) {
1670
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Wallet not available for connection", {
1671
+ walletId: requestedWalletId
1672
+ });
1673
+ throw new Error(`Wallet not available for connection: ${requestedWalletId}`);
1674
+ }
1675
+ this.selectedWalletId = requestedWalletId;
1676
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Selected injected wallet for connection", {
1677
+ walletId: requestedWalletId
1678
+ });
1679
+ return walletInfo;
1680
+ }
1681
+ async connectToWallet(walletInfo, options) {
1682
+ if (!walletInfo.providers) {
1683
+ const error = new Error(`Wallet adapter not available for wallet: ${this.selectedWalletId}`);
1684
+ debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet adapter not available", { walletId: this.selectedWalletId });
1685
+ this.emit("connect_error", {
1686
+ error: error.message,
1687
+ source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
1688
+ });
1689
+ throw error;
1690
+ }
1691
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Connecting via wallet", {
1692
+ walletId: this.selectedWalletId,
1693
+ walletName: walletInfo.name,
1694
+ options
1695
+ });
1696
+ if (!options?.skipEventListeners) {
1697
+ this.setupEventListeners(walletInfo);
1698
+ }
1699
+ const connectedAddresses = [];
1700
+ if (this.addressTypes.includes(AddressType2.solana) && walletInfo.providers?.solana) {
1701
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection", {
1702
+ walletId: this.selectedWalletId,
1703
+ walletName: walletInfo.name,
1704
+ onlyIfTrusted: options?.onlyIfTrusted
1705
+ });
1706
+ try {
1707
+ const result = await walletInfo.providers.solana.connect(
1708
+ options?.onlyIfTrusted ? { onlyIfTrusted: true } : void 0
1709
+ );
1710
+ const address = result.publicKey;
1711
+ connectedAddresses.push({
1712
+ addressType: AddressType2.solana,
1713
+ address
1714
+ });
1715
+ debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", {
1716
+ address,
1717
+ walletId: this.selectedWalletId,
1718
+ walletName: walletInfo.name
1719
+ });
1720
+ } catch (err) {
1721
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana, stopping", {
1722
+ error: err,
1723
+ walletId: this.selectedWalletId,
1724
+ walletName: walletInfo.name
1725
+ });
1726
+ this.emit("connect_error", {
1727
+ error: err instanceof Error ? err.message : "Failed to connect",
1728
+ source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
1729
+ });
1730
+ throw err;
1731
+ }
391
1732
  }
392
- if (!this._solanaChain) {
393
- throw new Error("Solana chain not initialized");
1733
+ if (this.addressTypes.includes(AddressType2.ethereum) && walletInfo.providers?.ethereum) {
1734
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Ethereum connection", {
1735
+ walletId: this.selectedWalletId,
1736
+ walletName: walletInfo.name,
1737
+ silent: options?.silent
1738
+ });
1739
+ try {
1740
+ let accounts;
1741
+ if (options?.silent) {
1742
+ accounts = await walletInfo.providers.ethereum.request({ method: "eth_accounts" });
1743
+ } else {
1744
+ accounts = await walletInfo.providers.ethereum.connect();
1745
+ }
1746
+ if (accounts.length > 0) {
1747
+ connectedAddresses.push(
1748
+ ...accounts.map((address) => ({
1749
+ addressType: AddressType2.ethereum,
1750
+ address
1751
+ }))
1752
+ );
1753
+ debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum connected successfully", {
1754
+ addresses: accounts,
1755
+ walletId: this.selectedWalletId,
1756
+ walletName: walletInfo.name
1757
+ });
1758
+ }
1759
+ } catch (err) {
1760
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum, stopping", {
1761
+ error: err,
1762
+ walletId: this.selectedWalletId,
1763
+ walletName: walletInfo.name
1764
+ });
1765
+ this.emit("connect_error", {
1766
+ error: err instanceof Error ? err.message : "Failed to connect",
1767
+ source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
1768
+ });
1769
+ throw err;
1770
+ }
394
1771
  }
395
- return this._solanaChain;
1772
+ return connectedAddresses;
396
1773
  }
397
- /**
398
- * Access to Ethereum chain operations
399
- */
400
- get ethereum() {
401
- if (!this.addressTypes.includes(AddressType4.ethereum)) {
402
- throw new Error("Ethereum not enabled for this provider");
1774
+ async finalizeConnection(connectedAddresses, authProvider, walletId) {
1775
+ if (connectedAddresses.length === 0) {
1776
+ const error = new Error("Failed to connect to any supported wallet provider");
1777
+ this.emit("connect_error", {
1778
+ error: error.message,
1779
+ source: "manual-connect"
1780
+ });
1781
+ throw error;
403
1782
  }
404
- if (!this._ethereumChain) {
405
- throw new Error("Ethereum chain not initialized");
1783
+ if (this.selectedWalletId) {
1784
+ this.setWalletState(this.selectedWalletId, {
1785
+ connected: true,
1786
+ addresses: connectedAddresses
1787
+ });
406
1788
  }
407
- return this._ethereumChain;
1789
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Finalized connection with addresses", {
1790
+ addressCount: connectedAddresses.length,
1791
+ addresses: connectedAddresses.map((addr) => ({
1792
+ type: addr.addressType,
1793
+ address: addr.address.substring(0, 10) + "..."
1794
+ }))
1795
+ });
1796
+ const authUserId = await this.getAuthUserId("manual-connect");
1797
+ try {
1798
+ localStorage.setItem(WAS_CONNECTED_KEY, WAS_CONNECTED_VALUE);
1799
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Set was-connected flag - auto-reconnect enabled");
1800
+ if (this.selectedWalletId) {
1801
+ localStorage.setItem(LAST_WALLET_ID_KEY, this.selectedWalletId);
1802
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Stored last injected wallet id", {
1803
+ walletId: this.selectedWalletId
1804
+ });
1805
+ }
1806
+ } catch (error) {
1807
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to persist injected provider state", { error });
1808
+ }
1809
+ const walletInfo = walletId ? this.walletRegistry.getById(walletId) : void 0;
1810
+ const wallet = walletInfo ? {
1811
+ id: walletInfo.id,
1812
+ name: walletInfo.name,
1813
+ icon: walletInfo.icon,
1814
+ addressTypes: walletInfo.addressTypes,
1815
+ rdns: walletInfo.rdns,
1816
+ discovery: walletInfo.discovery
1817
+ } : void 0;
1818
+ const result = {
1819
+ addresses: connectedAddresses,
1820
+ status: "completed",
1821
+ authUserId,
1822
+ authProvider,
1823
+ walletId,
1824
+ wallet
1825
+ };
1826
+ this.emit("connect", {
1827
+ addresses: connectedAddresses,
1828
+ source: "manual-connect",
1829
+ authUserId
1830
+ });
1831
+ return result;
408
1832
  }
409
1833
  async connect(authOptions) {
410
1834
  debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider connect", {
411
1835
  addressTypes: this.addressTypes,
412
- authOptionsIgnored: !!authOptions
413
- // Note: authOptions are ignored for injected provider
1836
+ provider: authOptions.provider
414
1837
  });
1838
+ if (authOptions.provider !== "injected") {
1839
+ throw new Error(`Invalid provider for injected connection: ${authOptions.provider}. Must be "injected"`);
1840
+ }
415
1841
  this.emit("connect_start", {
416
1842
  source: "manual-connect",
417
1843
  providerType: "injected"
418
1844
  });
419
1845
  try {
420
- if (!this.phantom.extension?.isInstalled?.()) {
421
- debug.error(DebugCategory.INJECTED_PROVIDER, "Phantom wallet extension not found");
422
- const error = new Error("Phantom wallet not found");
423
- this.emit("connect_error", {
424
- error: error.message,
425
- source: "manual-connect"
426
- });
427
- throw error;
428
- }
429
- debug.log(DebugCategory.INJECTED_PROVIDER, "Phantom extension detected");
430
- const connectedAddresses = [];
431
- if (this.addressTypes.includes(AddressType4.solana)) {
432
- debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection");
1846
+ const requestedWalletId = authOptions.walletId || "phantom";
1847
+ const walletInfo = this.validateAndSelectWallet(requestedWalletId);
1848
+ const connectedAddresses = await this.connectToWallet(walletInfo);
1849
+ return await this.finalizeConnection(connectedAddresses, "injected", this.selectedWalletId || void 0);
1850
+ } catch (error) {
1851
+ this.emit("connect_error", {
1852
+ error: error instanceof Error ? error.message : "Failed to connect",
1853
+ source: "manual-connect"
1854
+ });
1855
+ throw error;
1856
+ }
1857
+ }
1858
+ async disconnect() {
1859
+ debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider disconnect");
1860
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
1861
+ if (walletInfo?.providers) {
1862
+ if (this.addressTypes.includes(AddressType2.solana) && walletInfo.providers.solana) {
433
1863
  try {
434
- const publicKey = await this.phantom.solana.connect();
435
- if (publicKey) {
436
- connectedAddresses.push({
437
- addressType: AddressType4.solana,
438
- address: publicKey
439
- });
440
- debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
441
- }
1864
+ await walletInfo.providers.solana.disconnect();
1865
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnected successfully");
442
1866
  } catch (err) {
443
- debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
1867
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Solana", { error: err });
444
1868
  }
445
1869
  }
446
- if (this.addressTypes.includes(AddressType4.ethereum)) {
1870
+ if (this.addressTypes.includes(AddressType2.ethereum) && walletInfo.providers.ethereum) {
447
1871
  try {
448
- const accounts = await this.phantom.ethereum.connect();
449
- if (accounts && accounts.length > 0) {
450
- connectedAddresses.push(
451
- ...accounts.map((address) => ({
452
- addressType: AddressType4.ethereum,
453
- address
454
- }))
455
- );
456
- debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum connected successfully", { addresses: accounts });
457
- }
1872
+ await walletInfo.providers.ethereum.disconnect();
1873
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected successfully");
458
1874
  } catch (err) {
459
- debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
1875
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Ethereum", { error: err });
460
1876
  }
461
1877
  }
462
- if (connectedAddresses.length === 0) {
463
- const error = new Error("Failed to connect to any supported wallet provider");
464
- this.emit("connect_error", {
465
- error: error.message,
466
- source: "manual-connect"
467
- });
468
- throw error;
469
- }
470
- this.addresses = connectedAddresses;
471
- this.connected = true;
472
- const result = {
473
- addresses: this.addresses,
474
- status: "completed"
475
- // walletId is not applicable for injected providers
476
- };
477
- this.emit("connect", {
478
- addresses: this.addresses,
479
- source: "manual-connect"
480
- });
481
- return result;
482
- } catch (error) {
483
- if (error instanceof Error && !error.message.includes("Phantom wallet not found") && !error.message.includes("Failed to connect to any supported wallet provider")) {
484
- this.emit("connect_error", {
485
- error: error.message,
486
- source: "manual-connect"
487
- });
488
- }
489
- throw error;
490
1878
  }
491
- }
492
- async disconnect() {
493
- debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider disconnect");
494
- if (this.addressTypes.includes(AddressType4.solana)) {
495
- try {
496
- await this.phantom.solana.disconnect();
497
- debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnected successfully");
498
- } catch (err) {
499
- debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Solana", { error: err });
500
- }
1879
+ const walletId = this.selectedWalletId || "phantom";
1880
+ const cleanups = this.eventListenerCleanups.get(walletId);
1881
+ if (cleanups) {
1882
+ cleanups.forEach((cleanup) => cleanup());
1883
+ this.eventListenerCleanups.delete(walletId);
1884
+ }
1885
+ this.eventListenersSetup.delete(walletId);
1886
+ if (this.selectedWalletId) {
1887
+ this.setWalletState(this.selectedWalletId, {
1888
+ connected: false,
1889
+ addresses: []
1890
+ });
501
1891
  }
502
- if (this.addressTypes.includes(AddressType4.ethereum)) {
503
- debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected (no-op)");
1892
+ try {
1893
+ localStorage.removeItem(WAS_CONNECTED_KEY);
1894
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Cleared was connected flag to prevent auto-reconnect");
1895
+ } catch (error) {
1896
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to clear was-connected flag", { error });
504
1897
  }
505
- this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
506
- this.browserInjectedCleanupFunctions = [];
507
- this.connected = false;
508
- this.addresses = [];
509
1898
  this.emit("disconnect", {
510
1899
  source: "manual-disconnect"
511
1900
  });
512
1901
  debug.info(DebugCategory.INJECTED_PROVIDER, "Injected provider disconnected successfully");
513
1902
  }
514
1903
  /**
515
- * Attempt auto-connection using onlyIfTrusted parameter
516
- * This will only connect if the dApp is already trusted by the user
1904
+ * Attempt auto-connection if user was previously connected
1905
+ * Only reconnects if the user connected before and didn't explicitly disconnect
517
1906
  * Should be called after setting up event listeners
518
1907
  */
519
1908
  async autoConnect() {
520
- debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting auto-connect with onlyIfTrusted=true");
1909
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting auto-connect");
1910
+ let lastWalletId = null;
1911
+ try {
1912
+ const wasConnected = localStorage.getItem(WAS_CONNECTED_KEY);
1913
+ if (wasConnected !== WAS_CONNECTED_VALUE) {
1914
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Skipping auto-connect: user was not previously connected");
1915
+ return;
1916
+ }
1917
+ lastWalletId = localStorage.getItem(LAST_WALLET_ID_KEY);
1918
+ debug.log(DebugCategory.INJECTED_PROVIDER, "User was previously connected, attempting auto-connect", {
1919
+ lastWalletId: lastWalletId || "phantom"
1920
+ });
1921
+ } catch (error) {
1922
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to check was-connected flag", { error });
1923
+ return;
1924
+ }
521
1925
  this.emit("connect_start", {
522
1926
  source: "auto-connect",
523
1927
  providerType: "injected"
524
1928
  });
525
1929
  try {
526
- if (!this.phantom.extension?.isInstalled?.()) {
527
- debug.warn(DebugCategory.INJECTED_PROVIDER, "Phantom wallet extension not found for auto-connect");
528
- this.emit("connect_error", {
529
- error: "Phantom wallet not found",
530
- source: "auto-connect"
1930
+ const walletId = lastWalletId || "phantom";
1931
+ await this.waitForWalletDiscovery(walletId);
1932
+ const walletInfo = this.validateAndSelectWallet(walletId);
1933
+ let connectedAddresses = [];
1934
+ try {
1935
+ connectedAddresses = await this.connectToWallet(walletInfo, {
1936
+ onlyIfTrusted: true,
1937
+ silent: true,
1938
+ skipEventListeners: true
1939
+ // Set up listeners only if connection succeeds
1940
+ });
1941
+ } catch (err) {
1942
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed (expected if not trusted)", {
1943
+ error: err,
1944
+ walletId: this.selectedWalletId
531
1945
  });
532
- return;
533
- }
534
- const connectedAddresses = [];
535
- if (this.addressTypes.includes(AddressType4.solana)) {
536
- debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana auto-connect");
537
- try {
538
- const publicKey = await this.phantom.solana.connect({ onlyIfTrusted: true });
539
- if (publicKey) {
540
- connectedAddresses.push({
541
- addressType: AddressType4.solana,
542
- address: publicKey
543
- });
544
- debug.info(DebugCategory.INJECTED_PROVIDER, "Solana auto-connected successfully", { address: publicKey });
545
- }
546
- } catch (err) {
547
- debug.log(DebugCategory.INJECTED_PROVIDER, "Solana auto-connect failed (expected if not trusted)", { error: err });
548
- }
549
- }
550
- if (this.addressTypes.includes(AddressType4.ethereum)) {
551
- debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Ethereum auto-connect");
552
- try {
553
- const accounts = await this.phantom.ethereum.connect({ onlyIfTrusted: true });
554
- if (accounts && accounts.length > 0) {
555
- connectedAddresses.push(
556
- ...accounts.map((address) => ({
557
- addressType: AddressType4.ethereum,
558
- address
559
- }))
560
- );
561
- debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum auto-connected successfully", { addresses: accounts });
562
- }
563
- } catch (err) {
564
- debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum auto-connect failed (expected if not trusted)", { error: err });
565
- }
566
1946
  }
567
1947
  if (connectedAddresses.length === 0) {
568
1948
  debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed: no trusted connections available");
@@ -572,15 +1952,27 @@ var InjectedProvider = class {
572
1952
  });
573
1953
  return;
574
1954
  }
575
- this.addresses = connectedAddresses;
576
- this.connected = true;
1955
+ this.setupEventListeners(walletInfo);
1956
+ if (this.selectedWalletId) {
1957
+ this.setWalletState(this.selectedWalletId, {
1958
+ connected: true,
1959
+ addresses: connectedAddresses
1960
+ });
1961
+ }
1962
+ const authUserId = await this.getAuthUserId("auto-connect");
577
1963
  this.emit("connect", {
578
- addresses: this.addresses,
579
- source: "auto-connect"
1964
+ addresses: connectedAddresses,
1965
+ source: "auto-connect",
1966
+ authUserId
580
1967
  });
581
1968
  debug.info(DebugCategory.INJECTED_PROVIDER, "Auto-connect successful", {
582
1969
  addressCount: connectedAddresses.length,
583
- addresses: connectedAddresses.map((addr) => ({ type: addr.addressType, address: addr.address.substring(0, 8) + "..." }))
1970
+ addresses: connectedAddresses.map((addr) => ({
1971
+ type: addr.addressType,
1972
+ address: addr.address.substring(0, 8) + "..."
1973
+ })),
1974
+ walletId: this.selectedWalletId,
1975
+ authUserId
584
1976
  });
585
1977
  } catch (error) {
586
1978
  debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed with error", {
@@ -592,46 +1984,284 @@ var InjectedProvider = class {
592
1984
  });
593
1985
  }
594
1986
  }
1987
+ getWalletState(walletId) {
1988
+ if (!this.walletStates.has(walletId)) {
1989
+ this.walletStates.set(walletId, { connected: false, addresses: [] });
1990
+ }
1991
+ return this.walletStates.get(walletId);
1992
+ }
1993
+ setWalletState(walletId, state) {
1994
+ this.walletStates.set(walletId, state);
1995
+ }
1996
+ /**
1997
+ * Update wallet state with new addresses for a specific address type
1998
+ * Replaces all existing addresses of the given type with the new addresses
1999
+ * @param walletId - The wallet ID to update
2000
+ * @param newAddresses - Array of new addresses (strings) for the address type
2001
+ * @param addressType - The type of addresses being updated
2002
+ * @returns The updated addresses array
2003
+ */
2004
+ updateWalletAddresses(walletId, newAddresses, addressType) {
2005
+ const state = this.getWalletState(walletId);
2006
+ const otherAddresses = state.addresses.filter((addr) => addr.addressType !== addressType);
2007
+ const addressesOfType = newAddresses.map((address) => ({ addressType, address }));
2008
+ const updatedAddresses = [...otherAddresses, ...addressesOfType];
2009
+ this.setWalletState(walletId, {
2010
+ connected: updatedAddresses.length > 0,
2011
+ addresses: updatedAddresses
2012
+ });
2013
+ return updatedAddresses;
2014
+ }
2015
+ /**
2016
+ * Helper to construct account change source string
2017
+ */
2018
+ getAccountChangeSource(source) {
2019
+ return `${source}-account-change`;
2020
+ }
2021
+ /**
2022
+ * Create a handler for Solana connect events
2023
+ */
2024
+ createSolanaConnectHandler(walletId, source) {
2025
+ return async (publicKey) => {
2026
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Solana connect event received", { publicKey, walletId });
2027
+ const newAddresses = this.updateWalletAddresses(walletId, [publicKey], AddressType2.solana);
2028
+ const authUserId = await this.getAuthUserId("Solana connect event");
2029
+ this.emit("connect", {
2030
+ addresses: newAddresses,
2031
+ source,
2032
+ authUserId
2033
+ });
2034
+ };
2035
+ }
2036
+ /**
2037
+ * Create a handler for Solana disconnect events
2038
+ */
2039
+ createSolanaDisconnectHandler(walletId, source) {
2040
+ return () => {
2041
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnect event received", { walletId });
2042
+ const state = this.getWalletState(walletId);
2043
+ const filteredAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.solana);
2044
+ this.setWalletState(walletId, {
2045
+ connected: filteredAddresses.length > 0,
2046
+ addresses: filteredAddresses
2047
+ });
2048
+ this.emit("disconnect", {
2049
+ source
2050
+ });
2051
+ };
2052
+ }
2053
+ /**
2054
+ * Create a handler for Solana account change events
2055
+ * Can receive string | null per Wallet Standard
2056
+ */
2057
+ createSolanaAccountChangeHandler(walletId, source) {
2058
+ return async (publicKey) => {
2059
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Solana account changed event received", { publicKey, walletId });
2060
+ if (publicKey) {
2061
+ const newAddresses = this.updateWalletAddresses(walletId, [publicKey], AddressType2.solana);
2062
+ const authUserId = await this.getAuthUserId("Solana account changed event");
2063
+ this.emit("connect", {
2064
+ addresses: newAddresses,
2065
+ source: this.getAccountChangeSource(source),
2066
+ authUserId
2067
+ });
2068
+ } else {
2069
+ const state = this.getWalletState(walletId);
2070
+ const otherAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.solana);
2071
+ this.setWalletState(walletId, {
2072
+ connected: otherAddresses.length > 0,
2073
+ addresses: otherAddresses
2074
+ });
2075
+ this.emit("disconnect", {
2076
+ source: this.getAccountChangeSource(source)
2077
+ });
2078
+ }
2079
+ };
2080
+ }
2081
+ /**
2082
+ * Create a handler for Ethereum connect events
2083
+ * EIP-1193 connect event receives { chainId: string }, but we need to get accounts separately
2084
+ */
2085
+ createEthereumConnectHandler(walletId, source) {
2086
+ return async (connectInfo) => {
2087
+ let accounts = [];
2088
+ if (Array.isArray(connectInfo)) {
2089
+ accounts = connectInfo;
2090
+ } else {
2091
+ try {
2092
+ const walletInfo = this.walletRegistry.getById(walletId);
2093
+ if (walletInfo?.providers?.ethereum) {
2094
+ accounts = await walletInfo.providers.ethereum.getAccounts();
2095
+ }
2096
+ } catch (error) {
2097
+ debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to get accounts on connect", { error });
2098
+ }
2099
+ }
2100
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum connect event received", { accounts, walletId });
2101
+ if (accounts.length > 0) {
2102
+ const newAddresses = this.updateWalletAddresses(walletId, accounts, AddressType2.ethereum);
2103
+ const authUserId = await this.getAuthUserId("Ethereum connect event");
2104
+ this.emit("connect", {
2105
+ addresses: newAddresses,
2106
+ source,
2107
+ authUserId
2108
+ });
2109
+ }
2110
+ };
2111
+ }
2112
+ /**
2113
+ * Create a handler for Ethereum disconnect events
2114
+ */
2115
+ createEthereumDisconnectHandler(walletId, source) {
2116
+ return () => {
2117
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnect event received", { walletId });
2118
+ const state = this.getWalletState(walletId);
2119
+ const filteredAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.ethereum);
2120
+ this.setWalletState(walletId, {
2121
+ connected: filteredAddresses.length > 0,
2122
+ addresses: filteredAddresses
2123
+ });
2124
+ this.emit("disconnect", {
2125
+ source
2126
+ });
2127
+ };
2128
+ }
2129
+ /**
2130
+ * Create a handler for Ethereum account change events
2131
+ */
2132
+ createEthereumAccountChangeHandler(walletId, source) {
2133
+ return async (accounts) => {
2134
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum accounts changed event received", { accounts, walletId });
2135
+ if (accounts && accounts.length > 0) {
2136
+ const newAddresses = this.updateWalletAddresses(walletId, accounts, AddressType2.ethereum);
2137
+ const authUserId = await this.getAuthUserId("Ethereum accounts changed event");
2138
+ this.emit("connect", {
2139
+ addresses: newAddresses,
2140
+ source: this.getAccountChangeSource(source),
2141
+ authUserId
2142
+ });
2143
+ } else {
2144
+ const state = this.getWalletState(walletId);
2145
+ const otherAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.ethereum);
2146
+ this.setWalletState(walletId, {
2147
+ connected: otherAddresses.length > 0,
2148
+ addresses: otherAddresses
2149
+ });
2150
+ this.emit("disconnect", {
2151
+ source: this.getAccountChangeSource(source)
2152
+ });
2153
+ }
2154
+ };
2155
+ }
595
2156
  getAddresses() {
596
- return this.addresses;
2157
+ const walletId = this.selectedWalletId || "phantom";
2158
+ return this.getWalletState(walletId).addresses;
2159
+ }
2160
+ /**
2161
+ * Get enabled address types for the current selected wallet
2162
+ * - For Phantom: returns config.addressTypes
2163
+ * - For external wallets: returns the wallet's addressTypes from registry
2164
+ */
2165
+ getEnabledAddressTypes() {
2166
+ if (!this.selectedWalletId || this.selectedWalletId === "phantom") {
2167
+ return this.addressTypes;
2168
+ }
2169
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId);
2170
+ if (walletInfo) {
2171
+ return walletInfo.addressTypes;
2172
+ }
2173
+ return this.addressTypes;
597
2174
  }
598
2175
  isConnected() {
599
- return this.connected;
2176
+ const walletId = this.selectedWalletId || "phantom";
2177
+ return this.getWalletState(walletId).connected;
600
2178
  }
601
- // AutoConfirm methods - only available for injected providers
2179
+ // AutoConfirm methods - only available for Phantom wallet
602
2180
  async enableAutoConfirm(params) {
2181
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
2182
+ if (!isPhantomWallet(walletInfo)) {
2183
+ throw new Error("Auto-confirm is only available for Phantom wallet");
2184
+ }
603
2185
  debug.log(DebugCategory.INJECTED_PROVIDER, "Enabling autoConfirm", { params });
604
- return await this.phantom.autoConfirm.autoConfirmEnable(params);
2186
+ return await walletInfo.phantomInstance.autoConfirm.autoConfirmEnable(params);
605
2187
  }
606
2188
  async disableAutoConfirm() {
2189
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
2190
+ if (!isPhantomWallet(walletInfo)) {
2191
+ throw new Error("Auto-confirm is only available for Phantom wallet");
2192
+ }
607
2193
  debug.log(DebugCategory.INJECTED_PROVIDER, "Disabling autoConfirm");
608
- await this.phantom.autoConfirm.autoConfirmDisable();
2194
+ await walletInfo.phantomInstance.autoConfirm.autoConfirmDisable();
609
2195
  }
610
2196
  async getAutoConfirmStatus() {
2197
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
2198
+ if (!isPhantomWallet(walletInfo)) {
2199
+ throw new Error("Auto-confirm is only available for Phantom wallet");
2200
+ }
611
2201
  debug.log(DebugCategory.INJECTED_PROVIDER, "Getting autoConfirm status");
612
- return await this.phantom.autoConfirm.autoConfirmStatus();
2202
+ return await walletInfo.phantomInstance.autoConfirm.autoConfirmStatus();
613
2203
  }
614
2204
  async getSupportedAutoConfirmChains() {
2205
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
2206
+ if (!isPhantomWallet(walletInfo)) {
2207
+ throw new Error("Auto-confirm is only available for Phantom wallet");
2208
+ }
615
2209
  debug.log(DebugCategory.INJECTED_PROVIDER, "Getting supported autoConfirm chains");
616
- return await this.phantom.autoConfirm.autoConfirmSupportedChains();
2210
+ return await walletInfo.phantomInstance.autoConfirm.autoConfirmSupportedChains();
2211
+ }
2212
+ /**
2213
+ * Helper method to get authUserId from window.phantom.app.getUser()
2214
+ * Returns undefined if the method is not available or fails, or if wallet is not Phantom
2215
+ */
2216
+ async getAuthUserId(context) {
2217
+ const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
2218
+ if (!isPhantomWallet(walletInfo)) {
2219
+ return void 0;
2220
+ }
2221
+ try {
2222
+ if (window.phantom?.app?.getUser) {
2223
+ const userInfo = await window.phantom.app.getUser();
2224
+ const authUserId = userInfo?.authUserId;
2225
+ if (authUserId) {
2226
+ debug.log(
2227
+ DebugCategory.INJECTED_PROVIDER,
2228
+ `Retrieved authUserId from window.phantom.app.getUser() during ${context}`,
2229
+ {
2230
+ authUserId
2231
+ }
2232
+ );
2233
+ }
2234
+ return authUserId;
2235
+ }
2236
+ } catch (error) {
2237
+ debug.log(
2238
+ DebugCategory.INJECTED_PROVIDER,
2239
+ `Failed to get user info during ${context} (method may not be supported)`,
2240
+ { error }
2241
+ );
2242
+ }
2243
+ return void 0;
617
2244
  }
618
2245
  // Event management methods - implementing unified event interface
619
2246
  on(event, callback) {
620
2247
  debug.log(DebugCategory.INJECTED_PROVIDER, "Adding event listener", { event });
621
2248
  if (!this.eventsInitialized) {
622
- this.setupBrowserInjectedEvents();
623
- this.eventsInitialized = true;
2249
+ const walletId = this.selectedWalletId || "phantom";
2250
+ const walletInfo = this.walletRegistry.getById(walletId);
2251
+ if (walletInfo) {
2252
+ this.setupEventListeners(walletInfo);
2253
+ }
624
2254
  }
625
2255
  if (!this.eventListeners.has(event)) {
626
2256
  this.eventListeners.set(event, /* @__PURE__ */ new Set());
627
2257
  }
628
- this.eventListeners.get(event).add(callback);
2258
+ this.eventListeners.get(event)?.add(callback);
629
2259
  }
630
2260
  off(event, callback) {
631
2261
  debug.log(DebugCategory.INJECTED_PROVIDER, "Removing event listener", { event });
632
2262
  if (this.eventListeners.has(event)) {
633
- this.eventListeners.get(event).delete(callback);
634
- if (this.eventListeners.get(event).size === 0) {
2263
+ this.eventListeners.get(event)?.delete(callback);
2264
+ if (this.eventListeners.get(event)?.size === 0) {
635
2265
  this.eventListeners.delete(event);
636
2266
  }
637
2267
  }
@@ -653,133 +2283,76 @@ var InjectedProvider = class {
653
2283
  });
654
2284
  }
655
2285
  }
656
- setupBrowserInjectedEvents() {
657
- debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up browser-injected-sdk event listeners");
658
- if (this.addressTypes.includes(AddressType4.solana)) {
659
- this.setupSolanaEvents();
660
- }
661
- if (this.addressTypes.includes(AddressType4.ethereum)) {
662
- this.setupEthereumEvents();
2286
+ /**
2287
+ * Set up Solana event listeners for any provider (Phantom or external)
2288
+ */
2289
+ setupSolanaEventListeners(provider, walletId, source) {
2290
+ if (typeof provider.on !== "function")
2291
+ return;
2292
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Solana event listeners", { walletId, source });
2293
+ const handlers = {
2294
+ connect: this.createSolanaConnectHandler(walletId, source),
2295
+ disconnect: this.createSolanaDisconnectHandler(walletId, source),
2296
+ accountChanged: this.createSolanaAccountChangeHandler(walletId, source)
2297
+ };
2298
+ provider.on("connect", handlers.connect);
2299
+ provider.on("disconnect", handlers.disconnect);
2300
+ provider.on("accountChanged", handlers.accountChanged);
2301
+ const cleanups = [];
2302
+ if (typeof provider.off === "function") {
2303
+ cleanups.push(
2304
+ () => provider.off("connect", handlers.connect),
2305
+ () => provider.off("disconnect", handlers.disconnect),
2306
+ () => provider.off("accountChanged", handlers.accountChanged)
2307
+ );
663
2308
  }
2309
+ const existingCleanups = this.eventListenerCleanups.get(walletId) || [];
2310
+ this.eventListenerCleanups.set(walletId, [...existingCleanups, ...cleanups]);
664
2311
  }
665
- setupSolanaEvents() {
666
- debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Solana event listeners");
667
- const handleSolanaConnect = (publicKey) => {
668
- debug.log(DebugCategory.INJECTED_PROVIDER, "Solana connect event received", { publicKey });
669
- const solanaAddress = { addressType: AddressType4.solana, address: publicKey };
670
- if (!this.addresses.find((addr) => addr.addressType === AddressType4.solana)) {
671
- this.addresses.push(solanaAddress);
672
- }
673
- this.connected = true;
674
- this.emit("connect", {
675
- addresses: this.addresses,
676
- source: "injected-extension"
677
- });
678
- };
679
- const handleSolanaDisconnect = () => {
680
- debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnect event received");
681
- this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.solana);
682
- this.connected = false;
683
- this.emit("disconnect", {
684
- source: "injected-extension"
685
- });
686
- };
687
- const handleSolanaAccountChanged = (publicKey) => {
688
- debug.log(DebugCategory.INJECTED_PROVIDER, "Solana account changed event received", { publicKey });
689
- const solanaIndex = this.addresses.findIndex((addr) => addr.addressType === AddressType4.solana);
690
- if (solanaIndex >= 0) {
691
- this.addresses[solanaIndex] = { addressType: AddressType4.solana, address: publicKey };
692
- } else {
693
- this.addresses.push({ addressType: AddressType4.solana, address: publicKey });
694
- }
695
- this.emit("connect", {
696
- addresses: this.addresses,
697
- source: "injected-extension-account-change"
698
- });
699
- };
700
- const cleanupConnect = this.phantom.solana.addEventListener("connect", handleSolanaConnect);
701
- const cleanupDisconnect = this.phantom.solana.addEventListener("disconnect", handleSolanaDisconnect);
702
- const cleanupAccountChanged = this.phantom.solana.addEventListener("accountChanged", handleSolanaAccountChanged);
703
- this.browserInjectedCleanupFunctions.push(cleanupConnect, cleanupDisconnect, cleanupAccountChanged);
704
- }
705
- setupEthereumEvents() {
706
- debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Ethereum event listeners");
707
- const handleEthereumConnect = (accounts) => {
708
- debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum connect event received", { accounts });
709
- this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.ethereum);
710
- if (accounts && accounts.length > 0) {
711
- this.addresses.push(
712
- ...accounts.map((address) => ({
713
- addressType: AddressType4.ethereum,
714
- address
715
- }))
716
- );
717
- }
718
- this.connected = this.addresses.length > 0;
719
- this.emit("connect", {
720
- addresses: this.addresses,
721
- source: "injected-extension"
722
- });
723
- };
724
- const handleEthereumDisconnect = () => {
725
- debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnect event received");
726
- this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.ethereum);
727
- this.connected = false;
728
- this.emit("disconnect", {
729
- source: "injected-extension"
730
- });
731
- };
732
- const handleEthereumAccountsChanged = (accounts) => {
733
- debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum accounts changed event received", { accounts });
734
- this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.ethereum);
735
- if (accounts && accounts.length > 0) {
736
- this.addresses.push(
737
- ...accounts.map((address) => ({
738
- addressType: AddressType4.ethereum,
739
- address
740
- }))
741
- );
742
- this.emit("connect", {
743
- addresses: this.addresses,
744
- source: "injected-extension-account-change"
745
- });
746
- } else {
747
- this.connected = false;
748
- this.emit("disconnect", {
749
- source: "injected-extension-account-change"
750
- });
751
- }
752
- };
753
- const cleanupConnect = this.phantom.ethereum.addEventListener("connect", handleEthereumConnect);
754
- const cleanupDisconnect = this.phantom.ethereum.addEventListener("disconnect", handleEthereumDisconnect);
755
- const cleanupAccountsChanged = this.phantom.ethereum.addEventListener(
756
- "accountsChanged",
757
- handleEthereumAccountsChanged
758
- );
759
- this.browserInjectedCleanupFunctions.push(cleanupConnect, cleanupDisconnect, cleanupAccountsChanged);
760
- }
761
- createCallbacks() {
762
- return {
763
- connect: async () => {
764
- const result = await this.connect();
765
- return result.addresses;
766
- },
767
- disconnect: async () => {
768
- await this.disconnect();
769
- },
770
- isConnected: () => {
771
- return this.isConnected();
772
- },
773
- getAddresses: () => {
774
- return this.getAddresses();
775
- },
776
- on: (event, callback) => {
777
- this.on(event, callback);
778
- },
779
- off: (event, callback) => {
780
- this.off(event, callback);
781
- }
2312
+ /**
2313
+ * Set up Ethereum event listeners for any provider (Phantom or external)
2314
+ */
2315
+ setupEthereumEventListeners(provider, walletId, source) {
2316
+ if (typeof provider.on !== "function")
2317
+ return;
2318
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Ethereum event listeners", { walletId, source });
2319
+ const handlers = {
2320
+ connect: this.createEthereumConnectHandler(walletId, source),
2321
+ disconnect: this.createEthereumDisconnectHandler(walletId, source),
2322
+ accountsChanged: this.createEthereumAccountChangeHandler(walletId, source)
782
2323
  };
2324
+ provider.on("connect", handlers.connect);
2325
+ provider.on("disconnect", handlers.disconnect);
2326
+ provider.on("accountsChanged", handlers.accountsChanged);
2327
+ const cleanups = [];
2328
+ if (typeof provider.off === "function") {
2329
+ cleanups.push(
2330
+ () => provider.off("connect", handlers.connect),
2331
+ () => provider.off("disconnect", handlers.disconnect),
2332
+ () => provider.off("accountsChanged", handlers.accountsChanged)
2333
+ );
2334
+ }
2335
+ const existingCleanups = this.eventListenerCleanups.get(walletId) || [];
2336
+ this.eventListenerCleanups.set(walletId, [...existingCleanups, ...cleanups]);
2337
+ }
2338
+ /**
2339
+ * Unified event listener setup for all wallet types (Phantom and external)
2340
+ */
2341
+ setupEventListeners(walletInfo) {
2342
+ const walletId = this.selectedWalletId || "phantom";
2343
+ if (this.eventListenersSetup.has(walletId)) {
2344
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Event listeners already set up for wallet", { walletId });
2345
+ return;
2346
+ }
2347
+ debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up event listeners", { walletId });
2348
+ if (this.addressTypes.includes(AddressType2.solana) && walletInfo.providers?.solana) {
2349
+ this.setupSolanaEventListeners(walletInfo.providers.solana, walletId, "wallet");
2350
+ }
2351
+ if (this.addressTypes.includes(AddressType2.ethereum) && walletInfo.providers?.ethereum) {
2352
+ this.setupEthereumEventListeners(walletInfo.providers.ethereum, walletId, "wallet");
2353
+ }
2354
+ this.eventListenersSetup.add(walletId);
2355
+ this.eventsInitialized = true;
783
2356
  }
784
2357
  };
785
2358
 
@@ -866,6 +2439,47 @@ var BrowserStorage = class {
866
2439
  };
867
2440
  });
868
2441
  }
2442
+ async getShouldClearPreviousSession() {
2443
+ debug.log(DebugCategory.STORAGE, "Getting shouldClearPreviousSession flag from IndexedDB");
2444
+ const db = await this.getDB();
2445
+ return new Promise((resolve, reject) => {
2446
+ const transaction = db.transaction([this.storeName], "readonly");
2447
+ const store = transaction.objectStore(this.storeName);
2448
+ const request = store.get("shouldClearPreviousSession");
2449
+ request.onsuccess = () => {
2450
+ const shouldClear = request.result ?? false;
2451
+ debug.log(DebugCategory.STORAGE, "Retrieved shouldClearPreviousSession flag from IndexedDB", {
2452
+ shouldClear
2453
+ });
2454
+ resolve(shouldClear);
2455
+ };
2456
+ request.onerror = () => {
2457
+ debug.error(DebugCategory.STORAGE, "Failed to get shouldClearPreviousSession flag from IndexedDB", {
2458
+ error: request.error
2459
+ });
2460
+ reject(request.error);
2461
+ };
2462
+ });
2463
+ }
2464
+ async setShouldClearPreviousSession(should) {
2465
+ debug.log(DebugCategory.STORAGE, "Setting shouldClearPreviousSession flag in IndexedDB", { should });
2466
+ const db = await this.getDB();
2467
+ return new Promise((resolve, reject) => {
2468
+ const transaction = db.transaction([this.storeName], "readwrite");
2469
+ const store = transaction.objectStore(this.storeName);
2470
+ const request = store.put(should, "shouldClearPreviousSession");
2471
+ request.onsuccess = () => {
2472
+ debug.log(DebugCategory.STORAGE, "Successfully set shouldClearPreviousSession flag in IndexedDB");
2473
+ resolve();
2474
+ };
2475
+ request.onerror = () => {
2476
+ debug.error(DebugCategory.STORAGE, "Failed to set shouldClearPreviousSession flag in IndexedDB", {
2477
+ error: request.error
2478
+ });
2479
+ reject(request.error);
2480
+ };
2481
+ });
2482
+ }
869
2483
  };
870
2484
 
871
2485
  // src/providers/embedded/adapters/url-params.ts
@@ -879,6 +2493,147 @@ var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
879
2493
 
880
2494
  // src/providers/embedded/adapters/auth.ts
881
2495
  import { DEFAULT_AUTH_URL } from "@phantom/constants";
2496
+
2497
+ // src/utils/browser-detection.ts
2498
+ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
2499
+ let name = "unknown";
2500
+ let version = "unknown";
2501
+ if (!userAgent || typeof userAgent !== "string") {
2502
+ return { name, version, userAgent: "unknown" };
2503
+ }
2504
+ try {
2505
+ if (userAgent.includes("Edg/")) {
2506
+ name = "edge";
2507
+ const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
2508
+ if (match)
2509
+ version = match[1].split(".")[0];
2510
+ } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
2511
+ name = "opera";
2512
+ const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
2513
+ if (match)
2514
+ version = match[1].split(".")[0];
2515
+ } else if (userAgent.includes("SamsungBrowser/")) {
2516
+ name = "samsung";
2517
+ const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
2518
+ if (match)
2519
+ version = match[1].split(".")[0];
2520
+ } else if (userAgent.includes("DuckDuckGo/")) {
2521
+ name = "duckduckgo";
2522
+ const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
2523
+ if (match)
2524
+ version = match[1].split(".")[0];
2525
+ } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
2526
+ name = "brave";
2527
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
2528
+ if (match)
2529
+ version = match[1].split(".")[0];
2530
+ } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
2531
+ if (userAgent.includes("Chrome/")) {
2532
+ name = "chrome-mobile";
2533
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
2534
+ if (match)
2535
+ version = match[1].split(".")[0];
2536
+ } else if (userAgent.includes("Firefox/")) {
2537
+ name = "firefox-mobile";
2538
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
2539
+ if (match)
2540
+ version = match[1].split(".")[0];
2541
+ } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
2542
+ name = "safari-mobile";
2543
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
2544
+ if (match)
2545
+ version = match[1].split(".")[0];
2546
+ } else {
2547
+ name = "mobile";
2548
+ }
2549
+ } else if (userAgent.includes("Chrome/")) {
2550
+ name = "chrome";
2551
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
2552
+ if (match)
2553
+ version = match[1].split(".")[0];
2554
+ } else if (userAgent.includes("Firefox/")) {
2555
+ name = "firefox";
2556
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
2557
+ if (match)
2558
+ version = match[1].split(".")[0];
2559
+ } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
2560
+ name = "safari";
2561
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
2562
+ if (match)
2563
+ version = match[1].split(".")[0];
2564
+ }
2565
+ if (name === "unknown") {
2566
+ const patterns = [
2567
+ { regex: /Chrome\/([0-9]+)/, name: "chrome" },
2568
+ { regex: /Firefox\/([0-9]+)/, name: "firefox" },
2569
+ { regex: /Safari\/([0-9]+)/, name: "safari" },
2570
+ { regex: /Edge\/([0-9]+)/, name: "edge" },
2571
+ { regex: /Opera\/([0-9]+)/, name: "opera" }
2572
+ ];
2573
+ for (const pattern of patterns) {
2574
+ const match = userAgent.match(pattern.regex);
2575
+ if (match) {
2576
+ name = pattern.name;
2577
+ version = match[1];
2578
+ break;
2579
+ }
2580
+ }
2581
+ }
2582
+ } catch (error) {
2583
+ }
2584
+ return { name, version, userAgent };
2585
+ }
2586
+ function detectBrowser() {
2587
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
2588
+ return { name: "unknown", version: "unknown", userAgent: "unknown" };
2589
+ }
2590
+ const userAgent = window.navigator.userAgent;
2591
+ const hasBraveAPI = !!navigator.brave;
2592
+ return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
2593
+ }
2594
+ function getPlatformName() {
2595
+ const { name, version } = detectBrowser();
2596
+ return version !== "unknown" ? `${name}-v${version}` : name;
2597
+ }
2598
+ function getBrowserDisplayName() {
2599
+ const { name, version } = detectBrowser();
2600
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
2601
+ return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
2602
+ }
2603
+ function isMobileDevice() {
2604
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
2605
+ return false;
2606
+ }
2607
+ const userAgent = window.navigator.userAgent.toLowerCase();
2608
+ const mobilePatterns = [
2609
+ /android/,
2610
+ /iphone|ipad|ipod/,
2611
+ /blackberry/,
2612
+ /windows phone/,
2613
+ /mobile/,
2614
+ /tablet/,
2615
+ /silk/,
2616
+ /kindle/,
2617
+ /opera mini/,
2618
+ /opera mobi/
2619
+ ];
2620
+ const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
2621
+ let isSmallScreen = false;
2622
+ try {
2623
+ isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
2624
+ } catch (error) {
2625
+ isSmallScreen = false;
2626
+ }
2627
+ let isTouchDevice = false;
2628
+ try {
2629
+ isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
2630
+ } catch (error) {
2631
+ isTouchDevice = false;
2632
+ }
2633
+ return isMobileUA || isSmallScreen && isTouchDevice;
2634
+ }
2635
+
2636
+ // src/providers/embedded/adapters/auth.ts
882
2637
  var BrowserAuthProvider = class {
883
2638
  constructor(urlParamsAccessor) {
884
2639
  this.urlParamsAccessor = urlParamsAccessor;
@@ -897,21 +2652,24 @@ var BrowserAuthProvider = class {
897
2652
  }
898
2653
  const phantomOptions = options;
899
2654
  debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Starting Phantom Connect authentication", {
900
- organizationId: phantomOptions.organizationId,
2655
+ publicKey: phantomOptions.publicKey,
901
2656
  appId: phantomOptions.appId,
902
2657
  provider: phantomOptions.provider,
903
- authUrl: phantomOptions.authUrl,
904
- hasCustomData: !!phantomOptions.customAuthData
2658
+ authUrl: phantomOptions.authUrl
905
2659
  });
906
2660
  const baseUrl = phantomOptions.authUrl || DEFAULT_AUTH_URL;
907
2661
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
908
2662
  const params = new URLSearchParams({
909
- organization_id: phantomOptions.organizationId,
2663
+ public_key: phantomOptions.publicKey,
910
2664
  app_id: phantomOptions.appId,
911
2665
  redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
912
2666
  session_id: phantomOptions.sessionId,
913
- clear_previous_session: true.toString(),
914
- sdk_version: "1.0.0-beta.9"
2667
+ // OAuth session management - defaults to allow refresh unless explicitly clearing after logout
2668
+ clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
2669
+ allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
2670
+ sdk_version: "1.0.2",
2671
+ sdk_type: "browser",
2672
+ platform: detectBrowser().name
915
2673
  });
916
2674
  if (phantomOptions.provider) {
917
2675
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
@@ -922,12 +2680,8 @@ var BrowserAuthProvider = class {
922
2680
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
923
2681
  params.append("provider", "google");
924
2682
  }
925
- if (phantomOptions.customAuthData) {
926
- debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Adding custom auth data");
927
- params.append("authData", JSON.stringify(phantomOptions.customAuthData));
928
- }
929
2683
  const authContext = {
930
- organizationId: phantomOptions.organizationId,
2684
+ publicKey: phantomOptions.publicKey,
931
2685
  appId: phantomOptions.appId,
932
2686
  provider: phantomOptions.provider,
933
2687
  sessionId: phantomOptions.sessionId
@@ -936,14 +2690,14 @@ var BrowserAuthProvider = class {
936
2690
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Stored auth context in session storage", { authContext });
937
2691
  const authUrl = `${baseUrl}?${params.toString()}`;
938
2692
  debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Redirecting to Phantom Connect", { authUrl });
939
- if (!authUrl.startsWith("https:")) {
2693
+ if (!authUrl.startsWith("https:") && !authUrl.startsWith("http://localhost")) {
940
2694
  throw new Error("Invalid auth URL - only HTTPS URLs are allowed for authentication");
941
2695
  }
942
2696
  window.location.href = authUrl;
943
2697
  resolve();
944
2698
  });
945
2699
  }
946
- resumeAuthFromRedirect() {
2700
+ resumeAuthFromRedirect(provider) {
947
2701
  try {
948
2702
  const walletId = this.urlParamsAccessor.getParam("wallet_id");
949
2703
  const sessionId = this.urlParamsAccessor.getParam("session_id");
@@ -994,10 +2748,37 @@ var BrowserAuthProvider = class {
994
2748
  sessionId,
995
2749
  accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
996
2750
  });
2751
+ const organizationId = this.urlParamsAccessor.getParam("organization_id");
2752
+ const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
2753
+ const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
2754
+ debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
2755
+ walletId,
2756
+ organizationId,
2757
+ sessionId,
2758
+ accountDerivationIndex,
2759
+ expiresInMs,
2760
+ authUserId
2761
+ });
2762
+ if (!organizationId) {
2763
+ debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
2764
+ throw new Error("Missing organization_id in auth response");
2765
+ }
2766
+ if (organizationId.startsWith("temp-")) {
2767
+ debug.warn(
2768
+ DebugCategory.PHANTOM_CONNECT_AUTH,
2769
+ "Received temporary organization_id, server may not be configured properly",
2770
+ {
2771
+ organizationId
2772
+ }
2773
+ );
2774
+ }
997
2775
  return {
998
2776
  walletId,
999
- userInfo: context,
1000
- accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
2777
+ organizationId,
2778
+ accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
2779
+ expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
2780
+ authUserId: authUserId || void 0,
2781
+ provider
1001
2782
  };
1002
2783
  } catch (error) {
1003
2784
  sessionStorage.removeItem("phantom-auth-context");
@@ -1006,160 +2787,120 @@ var BrowserAuthProvider = class {
1006
2787
  }
1007
2788
  };
1008
2789
 
1009
- // src/providers/embedded/adapters/logger.ts
1010
- var BrowserLogger = class {
1011
- info(category, message, data) {
1012
- debug.info(category, message, data);
1013
- }
1014
- warn(category, message, data) {
1015
- debug.warn(category, message, data);
1016
- }
1017
- error(category, message, data) {
1018
- debug.error(category, message, data);
1019
- }
1020
- log(category, message, data) {
1021
- debug.log(category, message, data);
1022
- }
1023
- };
2790
+ // src/providers/embedded/adapters/phantom-app.ts
2791
+ import { isPhantomExtensionInstalled as isPhantomExtensionInstalled3 } from "@phantom/browser-injected-sdk";
1024
2792
 
1025
- // src/utils/browser-detection.ts
1026
- function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
1027
- let name = "unknown";
1028
- let version = "unknown";
1029
- if (!userAgent || typeof userAgent !== "string") {
1030
- return { name, version, userAgent: "unknown" };
2793
+ // src/isPhantomLoginAvailable.ts
2794
+ import { isPhantomExtensionInstalled as isPhantomExtensionInstalled2 } from "@phantom/browser-injected-sdk";
2795
+ async function isPhantomLoginAvailable(timeoutMs = 3e3) {
2796
+ const extensionInstalled = await waitForExtension(timeoutMs);
2797
+ if (!extensionInstalled) {
2798
+ return false;
1031
2799
  }
1032
2800
  try {
1033
- if (userAgent.includes("Edg/")) {
1034
- name = "edge";
1035
- const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
1036
- if (match)
1037
- version = match[1].split(".")[0];
1038
- } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
1039
- name = "opera";
1040
- const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
1041
- if (match)
1042
- version = match[1].split(".")[0];
1043
- } else if (userAgent.includes("SamsungBrowser/")) {
1044
- name = "samsung";
1045
- const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
1046
- if (match)
1047
- version = match[1].split(".")[0];
1048
- } else if (userAgent.includes("DuckDuckGo/")) {
1049
- name = "duckduckgo";
1050
- const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
1051
- if (match)
1052
- version = match[1].split(".")[0];
1053
- } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
1054
- name = "brave";
1055
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1056
- if (match)
1057
- version = match[1].split(".")[0];
1058
- } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
1059
- if (userAgent.includes("Chrome/")) {
1060
- name = "chrome-mobile";
1061
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1062
- if (match)
1063
- version = match[1].split(".")[0];
1064
- } else if (userAgent.includes("Firefox/")) {
1065
- name = "firefox-mobile";
1066
- const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1067
- if (match)
1068
- version = match[1].split(".")[0];
1069
- } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
1070
- name = "safari-mobile";
1071
- const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1072
- if (match)
1073
- version = match[1].split(".")[0];
1074
- } else {
1075
- name = "mobile";
1076
- }
1077
- } else if (userAgent.includes("Chrome/")) {
1078
- name = "chrome";
1079
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1080
- if (match)
1081
- version = match[1].split(".")[0];
1082
- } else if (userAgent.includes("Firefox/")) {
1083
- name = "firefox";
1084
- const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1085
- if (match)
1086
- version = match[1].split(".")[0];
1087
- } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
1088
- name = "safari";
1089
- const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1090
- if (match)
1091
- version = match[1].split(".")[0];
2801
+ if (!window.phantom?.app?.features || typeof window.phantom.app.features !== "function") {
2802
+ return false;
1092
2803
  }
1093
- if (name === "unknown") {
1094
- const patterns = [
1095
- { regex: /Chrome\/([0-9]+)/, name: "chrome" },
1096
- { regex: /Firefox\/([0-9]+)/, name: "firefox" },
1097
- { regex: /Safari\/([0-9]+)/, name: "safari" },
1098
- { regex: /Edge\/([0-9]+)/, name: "edge" },
1099
- { regex: /Opera\/([0-9]+)/, name: "opera" }
1100
- ];
1101
- for (const pattern of patterns) {
1102
- const match = userAgent.match(pattern.regex);
1103
- if (match) {
1104
- name = pattern.name;
1105
- version = match[1];
1106
- break;
2804
+ const response = await window.phantom.app.features();
2805
+ if (!Array.isArray(response.features)) {
2806
+ return false;
2807
+ }
2808
+ return response.features.includes("phantom_login");
2809
+ } catch (error) {
2810
+ console.error("Error checking Phantom extension features", error);
2811
+ return false;
2812
+ }
2813
+ }
2814
+ async function waitForExtension(timeoutMs) {
2815
+ return new Promise((resolve) => {
2816
+ const startTime = Date.now();
2817
+ const checkInterval = 100;
2818
+ const checkForExtension = () => {
2819
+ try {
2820
+ if (isPhantomExtensionInstalled2()) {
2821
+ resolve(true);
2822
+ return;
1107
2823
  }
2824
+ } catch (error) {
2825
+ }
2826
+ const elapsed = Date.now() - startTime;
2827
+ if (elapsed >= timeoutMs) {
2828
+ resolve(false);
2829
+ return;
1108
2830
  }
2831
+ setTimeout(checkForExtension, checkInterval);
2832
+ };
2833
+ checkForExtension();
2834
+ });
2835
+ }
2836
+
2837
+ // src/providers/embedded/adapters/phantom-app.ts
2838
+ var BrowserPhantomAppProvider = class {
2839
+ /**
2840
+ * Check if the Phantom extension is installed in the browser
2841
+ */
2842
+ isAvailable() {
2843
+ return isPhantomExtensionInstalled3();
2844
+ }
2845
+ /**
2846
+ * Authenticate using the Phantom browser extension
2847
+ */
2848
+ async authenticate(options) {
2849
+ if (!this.isAvailable()) {
2850
+ throw new Error(
2851
+ "Phantom extension is not installed. Please install the Phantom browser extension to use this authentication method."
2852
+ );
2853
+ }
2854
+ const loginAvailable = await isPhantomLoginAvailable();
2855
+ if (!loginAvailable) {
2856
+ throw new Error(
2857
+ "Phantom Login is not available. Please update your Phantom extension to use this authentication method."
2858
+ );
2859
+ }
2860
+ try {
2861
+ if (!window.phantom?.app?.login) {
2862
+ throw new Error("Phantom extension login method not found");
2863
+ }
2864
+ const result = await window.phantom.app.login({
2865
+ publicKey: options.publicKey,
2866
+ appId: options.appId,
2867
+ sessionId: options.sessionId
2868
+ });
2869
+ if (!result || !result.walletId || !result.organizationId) {
2870
+ throw new Error("Invalid authentication response from Phantom extension");
2871
+ }
2872
+ return {
2873
+ walletId: result.walletId,
2874
+ organizationId: result.organizationId,
2875
+ provider: "phantom",
2876
+ accountDerivationIndex: result.accountDerivationIndex ?? 0,
2877
+ expiresInMs: result.expiresInMs ?? 0,
2878
+ authUserId: result.authUserId
2879
+ };
2880
+ } catch (error) {
2881
+ if (error instanceof Error) {
2882
+ throw error;
2883
+ }
2884
+ throw new Error(`Phantom extension authentication failed: ${String(error)}`);
1109
2885
  }
1110
- } catch (error) {
1111
2886
  }
1112
- return { name, version, userAgent };
1113
- }
1114
- function detectBrowser() {
1115
- if (typeof window === "undefined" || !window.navigator?.userAgent) {
1116
- return { name: "unknown", version: "unknown", userAgent: "unknown" };
2887
+ };
2888
+
2889
+ // src/providers/embedded/adapters/logger.ts
2890
+ var BrowserLogger = class {
2891
+ info(category, message, data) {
2892
+ debug.info(category, message, data);
1117
2893
  }
1118
- const userAgent = window.navigator.userAgent;
1119
- const hasBraveAPI = !!navigator.brave;
1120
- return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
1121
- }
1122
- function getPlatformName() {
1123
- const { name, version } = detectBrowser();
1124
- return version !== "unknown" ? `${name}-v${version}` : name;
1125
- }
1126
- function getBrowserDisplayName() {
1127
- const { name, version } = detectBrowser();
1128
- const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
1129
- return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
1130
- }
1131
- function isMobileDevice() {
1132
- if (typeof window === "undefined" || !window.navigator?.userAgent) {
1133
- return false;
2894
+ warn(category, message, data) {
2895
+ debug.warn(category, message, data);
1134
2896
  }
1135
- const userAgent = window.navigator.userAgent.toLowerCase();
1136
- const mobilePatterns = [
1137
- /android/,
1138
- /iphone|ipad|ipod/,
1139
- /blackberry/,
1140
- /windows phone/,
1141
- /mobile/,
1142
- /tablet/,
1143
- /silk/,
1144
- /kindle/,
1145
- /opera mini/,
1146
- /opera mobi/
1147
- ];
1148
- const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
1149
- let isSmallScreen = false;
1150
- try {
1151
- isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
1152
- } catch (error) {
1153
- isSmallScreen = false;
2897
+ error(category, message, data) {
2898
+ debug.error(category, message, data);
1154
2899
  }
1155
- let isTouchDevice = false;
1156
- try {
1157
- isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1158
- } catch (error) {
1159
- isTouchDevice = false;
2900
+ log(category, message, data) {
2901
+ debug.log(category, message, data);
1160
2902
  }
1161
- return isMobileUA || isSmallScreen && isTouchDevice;
1162
- }
2903
+ };
1163
2904
 
1164
2905
  // src/providers/embedded/index.ts
1165
2906
  import { ANALYTICS_HEADERS } from "@phantom/constants";
@@ -1177,6 +2918,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
1177
2918
  const platform = {
1178
2919
  storage: new BrowserStorage(),
1179
2920
  authProvider: new BrowserAuthProvider(urlParamsAccessor),
2921
+ phantomAppProvider: new BrowserPhantomAppProvider(),
1180
2922
  urlParamsAccessor,
1181
2923
  stamper,
1182
2924
  name: platformName,
@@ -1189,26 +2931,61 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
1189
2931
  // Full user agent for more detailed info
1190
2932
  [ANALYTICS_HEADERS.APP_ID]: config.appId,
1191
2933
  [ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
1192
- [ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.9"
2934
+ [ANALYTICS_HEADERS.SDK_VERSION]: "1.0.2"
1193
2935
  // Replaced at build time
1194
2936
  }
1195
2937
  };
1196
2938
  debug.log(DebugCategory.EMBEDDED_PROVIDER, "Detected platform", { platformName });
1197
2939
  const logger = new BrowserLogger();
1198
2940
  super(config, platform, logger);
2941
+ this.addressTypes = config.addressTypes;
1199
2942
  debug.info(DebugCategory.EMBEDDED_PROVIDER, "Browser EmbeddedProvider initialized");
1200
2943
  }
2944
+ getEnabledAddressTypes() {
2945
+ return this.addressTypes;
2946
+ }
1201
2947
  };
1202
2948
 
1203
2949
  // src/ProviderManager.ts
2950
+ import {
2951
+ EMBEDDED_PROVIDER_AUTH_TYPES
2952
+ } from "@phantom/embedded-provider-core";
1204
2953
  import { DEFAULT_WALLET_API_URL, DEFAULT_EMBEDDED_WALLET_TYPE, DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2 } from "@phantom/constants";
2954
+
2955
+ // src/utils/auth-callback.ts
2956
+ function isAuthFailureCallback(searchParams) {
2957
+ if (typeof window === "undefined" && !searchParams)
2958
+ return false;
2959
+ const params = searchParams || new URLSearchParams(window.location.search);
2960
+ const responseType = params.get("response_type");
2961
+ const sessionId = params.get("session_id");
2962
+ return responseType === "failure" && !!sessionId;
2963
+ }
2964
+ function isAuthCallbackUrl(searchParams) {
2965
+ if (typeof window === "undefined" && !searchParams)
2966
+ return false;
2967
+ const params = searchParams || new URLSearchParams(window.location.search);
2968
+ const sessionId = params.get("session_id");
2969
+ return !!(sessionId && (params.has("response_type") || params.has("wallet_id")));
2970
+ }
2971
+
2972
+ // src/utils/deeplink.ts
2973
+ function getDeeplinkToPhantom(ref) {
2974
+ if (!window.location.href.startsWith("http:") && !window.location.href.startsWith("https:")) {
2975
+ throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported for deeplinks");
2976
+ }
2977
+ const currentUrl = encodeURIComponent(window.location.href);
2978
+ const refParam = ref ? `?ref=${encodeURIComponent(ref)}` : "";
2979
+ return `https://phantom.app/ul/browse/${currentUrl}${refParam}`;
2980
+ }
2981
+
2982
+ // src/ProviderManager.ts
1205
2983
  var ProviderManager = class {
1206
2984
  // Track which providers have forwarding set up
1207
2985
  constructor(config) {
1208
2986
  this.providers = /* @__PURE__ */ new Map();
1209
2987
  this.currentProvider = null;
1210
2988
  this.currentProviderKey = null;
1211
- this.walletId = null;
1212
2989
  // Event management for forwarding provider events
1213
2990
  this.eventListeners = /* @__PURE__ */ new Map();
1214
2991
  this.providerForwardingSetup = /* @__PURE__ */ new WeakSet();
@@ -1244,7 +3021,6 @@ var ProviderManager = class {
1244
3021
  }
1245
3022
  this.currentProvider = this.providers.get(key);
1246
3023
  this.currentProviderKey = key;
1247
- this.walletId = null;
1248
3024
  this.ensureProviderEventForwarding();
1249
3025
  return this.currentProvider;
1250
3026
  }
@@ -1267,29 +3043,78 @@ var ProviderManager = class {
1267
3043
  embeddedWalletType
1268
3044
  };
1269
3045
  }
3046
+ /**
3047
+ * Check if a provider is allowed by the config
3048
+ */
3049
+ isProviderAllowed(provider) {
3050
+ return this.config.providers.includes(provider);
3051
+ }
1270
3052
  /**
1271
3053
  * Connect using the current provider
3054
+ * Automatically switches provider based on authOptions.provider
1272
3055
  */
1273
3056
  async connect(authOptions) {
1274
3057
  debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
1275
3058
  currentProviderKey: this.currentProviderKey,
1276
- authOptions: authOptions ? { provider: authOptions.provider, hasJwtToken: !!authOptions.jwtToken } : void 0
3059
+ authOptions: { provider: authOptions.provider }
1277
3060
  });
3061
+ if (!this.isProviderAllowed(authOptions.provider)) {
3062
+ const error = `Provider "${authOptions.provider}" is not in the allowed providers list: ${JSON.stringify(this.config.providers)}`;
3063
+ debug.error(DebugCategory.PROVIDER_MANAGER, error);
3064
+ throw new Error(error);
3065
+ }
3066
+ const requestedProvider = authOptions.provider;
3067
+ let targetProviderType = null;
3068
+ if (requestedProvider === "injected") {
3069
+ targetProviderType = "injected";
3070
+ } else if (requestedProvider === "deeplink") {
3071
+ try {
3072
+ const deeplinkUrl = getDeeplinkToPhantom();
3073
+ if (typeof window !== "undefined") {
3074
+ window.location.href = deeplinkUrl;
3075
+ }
3076
+ return {
3077
+ addresses: [],
3078
+ walletId: void 0,
3079
+ authUserId: void 0
3080
+ };
3081
+ } catch (error) {
3082
+ const errorMessage = error instanceof Error ? error.message : "Failed to open deeplink";
3083
+ debug.error(DebugCategory.PROVIDER_MANAGER, "Deeplink error", { error: errorMessage });
3084
+ throw new Error(`Failed to open deeplink: ${errorMessage}`);
3085
+ }
3086
+ } else if (EMBEDDED_PROVIDER_AUTH_TYPES.includes(requestedProvider)) {
3087
+ targetProviderType = "embedded";
3088
+ }
3089
+ if (targetProviderType) {
3090
+ const currentInfo = this.getCurrentProviderInfo();
3091
+ if (currentInfo?.type !== targetProviderType) {
3092
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-switching provider based on auth options", {
3093
+ from: currentInfo?.type,
3094
+ to: targetProviderType,
3095
+ requestedProvider
3096
+ });
3097
+ const switchOptions = {};
3098
+ if (targetProviderType === "embedded") {
3099
+ switchOptions.embeddedWalletType = currentInfo?.embeddedWalletType || this.config.embeddedWalletType;
3100
+ }
3101
+ this.switchProvider(targetProviderType, switchOptions);
3102
+ }
3103
+ }
1278
3104
  if (!this.currentProvider) {
1279
3105
  debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
1280
3106
  throw new Error("No provider selected");
1281
3107
  }
1282
3108
  debug.log(DebugCategory.PROVIDER_MANAGER, "Delegating to provider connect method");
1283
3109
  const result = await this.currentProvider.connect(authOptions);
1284
- this.walletId = result.walletId || null;
1285
3110
  debug.log(DebugCategory.PROVIDER_MANAGER, "Connection successful, saving preferences", {
1286
- walletId: this.walletId,
1287
- addressCount: result.addresses?.length || 0
3111
+ addressCount: result.addresses?.length || 0,
3112
+ provider: authOptions.provider
1288
3113
  });
1289
3114
  this.saveProviderPreference();
1290
3115
  debug.info(DebugCategory.PROVIDER_MANAGER, "Connect completed", {
1291
- walletId: this.walletId,
1292
- addresses: result.addresses
3116
+ addresses: result.addresses,
3117
+ provider: authOptions.provider
1293
3118
  });
1294
3119
  return result;
1295
3120
  }
@@ -1300,7 +3125,6 @@ var ProviderManager = class {
1300
3125
  if (!this.currentProvider)
1301
3126
  return;
1302
3127
  await this.currentProvider.disconnect();
1303
- this.walletId = null;
1304
3128
  }
1305
3129
  /**
1306
3130
  * Get addresses from current provider
@@ -1318,10 +3142,71 @@ var ProviderManager = class {
1318
3142
  return this.currentProvider?.isConnected() ?? false;
1319
3143
  }
1320
3144
  /**
1321
- * Get current wallet ID
3145
+ * Attempt auto-connect with fallback strategy
3146
+ * Tries embedded provider first if it exists and is allowed, then injected provider if allowed
3147
+ * Returns true if any provider successfully connected
1322
3148
  */
1323
- getWalletId() {
1324
- return this.walletId;
3149
+ async autoConnect() {
3150
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Starting auto-connect with fallback strategy");
3151
+ if (isAuthFailureCallback()) {
3152
+ debug.warn(DebugCategory.PROVIDER_MANAGER, "Auth failure detected in URL, skipping autoConnect fallback");
3153
+ return false;
3154
+ }
3155
+ const embeddedWalletType = this.config.embeddedWalletType || "user-wallet";
3156
+ const embeddedKey = this.getProviderKey("embedded", embeddedWalletType);
3157
+ const embeddedAllowed = this.config.providers.some((p) => p !== "injected");
3158
+ if (embeddedAllowed && this.providers.has(embeddedKey)) {
3159
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Trying auto-connect with existing embedded provider");
3160
+ const embeddedProvider = this.providers.get(embeddedKey);
3161
+ try {
3162
+ const previousProvider = this.currentProvider;
3163
+ const previousKey = this.currentProviderKey;
3164
+ this.currentProvider = embeddedProvider;
3165
+ this.currentProviderKey = embeddedKey;
3166
+ this.ensureProviderEventForwarding();
3167
+ await embeddedProvider.autoConnect();
3168
+ if (embeddedProvider.isConnected()) {
3169
+ debug.info(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect successful");
3170
+ this.saveProviderPreference();
3171
+ return true;
3172
+ } else {
3173
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded provider did not connect, restoring previous provider");
3174
+ this.currentProvider = previousProvider;
3175
+ this.currentProviderKey = previousKey;
3176
+ }
3177
+ } catch (error) {
3178
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect failed", {
3179
+ error: error.message
3180
+ });
3181
+ if (isAuthCallbackUrl()) {
3182
+ debug.log(DebugCategory.PROVIDER_MANAGER, "In auth callback URL, not attempting injected fallback");
3183
+ return false;
3184
+ }
3185
+ }
3186
+ }
3187
+ const injectedAllowed = this.config.providers.includes("injected");
3188
+ const injectedKey = this.getProviderKey("injected");
3189
+ if (injectedAllowed && this.providers.has(injectedKey)) {
3190
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Trying auto-connect with existing injected provider");
3191
+ const injectedProvider = this.providers.get(injectedKey);
3192
+ try {
3193
+ this.currentProvider = injectedProvider;
3194
+ this.currentProviderKey = injectedKey;
3195
+ this.ensureProviderEventForwarding();
3196
+ await injectedProvider.autoConnect();
3197
+ if (injectedProvider.isConnected()) {
3198
+ debug.info(DebugCategory.PROVIDER_MANAGER, "Injected auto-connect successful");
3199
+ this.saveProviderPreference();
3200
+ return true;
3201
+ }
3202
+ } catch (error) {
3203
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Injected auto-connect failed", {
3204
+ error: error.message
3205
+ });
3206
+ }
3207
+ }
3208
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-connect failed for all allowed providers");
3209
+ return false;
1325
3210
  }
1326
3211
  /**
1327
3212
  * Add event listener - stores callback and ensures current provider forwards events to ProviderManager
@@ -1331,7 +3216,7 @@ var ProviderManager = class {
1331
3216
  if (!this.eventListeners.has(event)) {
1332
3217
  this.eventListeners.set(event, /* @__PURE__ */ new Set());
1333
3218
  }
1334
- this.eventListeners.get(event).add(callback);
3219
+ this.eventListeners.get(event)?.add(callback);
1335
3220
  this.ensureProviderEventForwarding();
1336
3221
  }
1337
3222
  /**
@@ -1340,8 +3225,8 @@ var ProviderManager = class {
1340
3225
  off(event, callback) {
1341
3226
  debug.log(DebugCategory.PROVIDER_MANAGER, "Removing event listener", { event });
1342
3227
  if (this.eventListeners.has(event)) {
1343
- this.eventListeners.get(event).delete(callback);
1344
- if (this.eventListeners.get(event).size === 0) {
3228
+ this.eventListeners.get(event)?.delete(callback);
3229
+ if (this.eventListeners.get(event)?.size === 0) {
1345
3230
  this.eventListeners.delete(event);
1346
3231
  }
1347
3232
  }
@@ -1390,7 +3275,8 @@ var ProviderManager = class {
1390
3275
  "connect",
1391
3276
  "connect_error",
1392
3277
  "disconnect",
1393
- "error"
3278
+ "error",
3279
+ "spending_limit_reached"
1394
3280
  ];
1395
3281
  for (const event of eventsToForward) {
1396
3282
  const forwardingCallback = (data) => {
@@ -1404,12 +3290,33 @@ var ProviderManager = class {
1404
3290
  }
1405
3291
  /**
1406
3292
  * Set default provider based on initial config
3293
+ * Creates providers based on the allowed providers array
1407
3294
  */
1408
3295
  setDefaultProvider() {
1409
- const defaultType = this.config.providerType || "embedded";
1410
3296
  const defaultEmbeddedType = this.config.embeddedWalletType || "user-wallet";
1411
- this.createProvider(defaultType, defaultEmbeddedType);
1412
- this.switchProvider(defaultType, { embeddedWalletType: defaultEmbeddedType });
3297
+ const hasInjected = this.config.providers.includes("injected");
3298
+ const hasEmbedded = this.config.providers.some((p) => p !== "injected" && p !== "deeplink");
3299
+ if (hasInjected) {
3300
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Creating injected provider (allowed by providers array)");
3301
+ this.createProvider("injected");
3302
+ }
3303
+ if (hasEmbedded) {
3304
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Creating embedded provider (allowed by providers array)");
3305
+ this.createProvider("embedded", defaultEmbeddedType);
3306
+ }
3307
+ let defaultType;
3308
+ if (hasEmbedded && this.providers.has(`embedded-${defaultEmbeddedType}`)) {
3309
+ defaultType = "embedded";
3310
+ } else if (hasInjected && this.providers.has("injected")) {
3311
+ defaultType = "injected";
3312
+ } else {
3313
+ throw new Error("No valid providers could be created from the providers array");
3314
+ }
3315
+ const switchOptions = {};
3316
+ if (defaultType === "embedded") {
3317
+ switchOptions.embeddedWalletType = defaultEmbeddedType;
3318
+ }
3319
+ this.switchProvider(defaultType, switchOptions);
1413
3320
  }
1414
3321
  /**
1415
3322
  * Create a provider instance
@@ -1469,66 +3376,48 @@ var ProviderManager = class {
1469
3376
  console.error("Failed to save provider preference:", error);
1470
3377
  }
1471
3378
  }
1472
- /**
1473
- * Restore provider preference from localStorage
1474
- */
1475
- /*
1476
- private restoreProviderPreference(): void {
1477
- try {
1478
- const saved = localStorage.getItem("phantom-provider-preference");
1479
- if (saved) {
1480
- const preference: ProviderPreference = JSON.parse(saved);
1481
- this.switchProvider(preference.type, {
1482
- embeddedWalletType: preference.embeddedWalletType,
1483
- });
1484
- }
1485
- } catch (error) {
1486
- // Ignore localStorage errors - just use default provider
1487
- console.error("Failed to restore provider preference:", error);
1488
- }
1489
- }*/
1490
3379
  };
1491
3380
 
1492
- // src/waitForPhantomExtension.ts
1493
- import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
1494
- async function waitForPhantomExtension(timeoutMs = 3e3) {
1495
- return new Promise((resolve) => {
1496
- const startTime = Date.now();
1497
- const checkInterval = 100;
1498
- const checkForExtension = () => {
1499
- try {
1500
- if (isPhantomExtensionInstalled()) {
1501
- resolve(true);
1502
- return;
1503
- }
1504
- } catch (error) {
1505
- }
1506
- const elapsed = Date.now() - startTime;
1507
- if (elapsed >= timeoutMs) {
1508
- resolve(false);
1509
- return;
1510
- }
1511
- setTimeout(checkForExtension, checkInterval);
1512
- };
1513
- checkForExtension();
1514
- });
1515
- }
1516
-
1517
3381
  // src/BrowserSDK.ts
3382
+ import { EMBEDDED_PROVIDER_AUTH_TYPES as EMBEDDED_PROVIDER_AUTH_TYPES2 } from "@phantom/embedded-provider-core";
1518
3383
  import { DEFAULT_EMBEDDED_WALLET_TYPE as DEFAULT_EMBEDDED_WALLET_TYPE2 } from "@phantom/constants";
3384
+ var BROWSER_SDK_PROVIDER_TYPES = [
3385
+ ...EMBEDDED_PROVIDER_AUTH_TYPES2,
3386
+ "injected",
3387
+ "deeplink"
3388
+ ];
1519
3389
  var BrowserSDK = class {
1520
3390
  constructor(config) {
3391
+ this.walletRegistry = getWalletRegistry();
3392
+ this.isLoading = true;
1521
3393
  debug.info(DebugCategory.BROWSER_SDK, "Initializing BrowserSDK", {
1522
- providerType: config.providerType,
3394
+ providers: config.providers,
1523
3395
  embeddedWalletType: config.embeddedWalletType,
1524
3396
  addressTypes: config.addressTypes
1525
3397
  });
1526
- if (!["injected", "embedded"].includes(config.providerType)) {
1527
- debug.error(DebugCategory.BROWSER_SDK, "Invalid providerType", { providerType: config.providerType });
1528
- throw new Error(`Invalid providerType: ${config.providerType}. Must be "injected" or "embedded".`);
3398
+ if (!Array.isArray(config.providers) || config.providers.length === 0) {
3399
+ debug.error(DebugCategory.BROWSER_SDK, "Invalid providers array", { providers: config.providers });
3400
+ throw new Error("providers must be a non-empty array of AuthProviderType");
3401
+ }
3402
+ const invalidProviders = config.providers.filter((p) => !BROWSER_SDK_PROVIDER_TYPES.includes(p));
3403
+ if (invalidProviders.length > 0) {
3404
+ debug.error(DebugCategory.BROWSER_SDK, "Invalid provider types", {
3405
+ invalidProviders,
3406
+ validProviders: BROWSER_SDK_PROVIDER_TYPES
3407
+ });
3408
+ throw new Error(
3409
+ `Invalid provider type(s): ${invalidProviders.join(", ")}. Valid providers are: ${BROWSER_SDK_PROVIDER_TYPES.join(", ")}`
3410
+ );
3411
+ }
3412
+ const hasEmbeddedProviders = config.providers.some((p) => p !== "injected");
3413
+ if (hasEmbeddedProviders && !config.appId) {
3414
+ debug.error(DebugCategory.BROWSER_SDK, "appId required for embedded providers", {
3415
+ providers: config.providers
3416
+ });
3417
+ throw new Error("appId is required when using embedded providers (google, apple, phantom, etc.)");
1529
3418
  }
1530
3419
  const embeddedWalletType = config.embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE2;
1531
- if (config.providerType === "embedded" && !["app-wallet", "user-wallet"].includes(embeddedWalletType)) {
3420
+ if (!["app-wallet", "user-wallet"].includes(embeddedWalletType)) {
1532
3421
  debug.error(DebugCategory.BROWSER_SDK, "Invalid embeddedWalletType", {
1533
3422
  embeddedWalletType: config.embeddedWalletType
1534
3423
  });
@@ -1536,7 +3425,14 @@ var BrowserSDK = class {
1536
3425
  `Invalid embeddedWalletType: ${config.embeddedWalletType}. Must be "app-wallet" or "user-wallet".`
1537
3426
  );
1538
3427
  }
3428
+ this.config = config;
1539
3429
  this.providerManager = new ProviderManager(config);
3430
+ void this.discoverWallets();
3431
+ }
3432
+ discoverWallets() {
3433
+ return this.walletRegistry.discover(this.config.addressTypes).finally(() => {
3434
+ this.isLoading = false;
3435
+ });
1540
3436
  }
1541
3437
  // ===== CHAIN API =====
1542
3438
  /**
@@ -1569,7 +3465,6 @@ var BrowserSDK = class {
1569
3465
  const result = await this.providerManager.connect(options);
1570
3466
  debug.info(DebugCategory.BROWSER_SDK, "Connection successful", {
1571
3467
  addressCount: result.addresses.length,
1572
- walletId: result.walletId,
1573
3468
  status: result.status
1574
3469
  });
1575
3470
  return result;
@@ -1591,22 +3486,6 @@ var BrowserSDK = class {
1591
3486
  throw error;
1592
3487
  }
1593
3488
  }
1594
- /**
1595
- * Switch between provider types (injected vs embedded)
1596
- */
1597
- async switchProvider(type, options) {
1598
- debug.info(DebugCategory.BROWSER_SDK, "Switching provider", { type, options });
1599
- try {
1600
- await this.providerManager.switchProvider(type, options);
1601
- debug.info(DebugCategory.BROWSER_SDK, "Provider switch successful", { type });
1602
- } catch (error) {
1603
- debug.error(DebugCategory.BROWSER_SDK, "Provider switch failed", {
1604
- type,
1605
- error: error.message
1606
- });
1607
- throw error;
1608
- }
1609
- }
1610
3489
  // ===== STATE QUERIES =====
1611
3490
  /**
1612
3491
  * Check if the SDK is connected to a wallet
@@ -1627,18 +3506,19 @@ var BrowserSDK = class {
1627
3506
  return this.providerManager.getCurrentProviderInfo();
1628
3507
  }
1629
3508
  /**
1630
- * Get the wallet ID (for embedded wallets)
3509
+ * Get enabled address types for the current provider
3510
+ * - For embedded provider: returns config.addressTypes
3511
+ * - For Phantom injected: returns config.addressTypes
3512
+ * - For discovered wallets: returns the wallet's addressTypes from registry
1631
3513
  */
1632
- getWalletId() {
1633
- return this.providerManager.getWalletId();
3514
+ getEnabledAddressTypes() {
3515
+ const currentProvider = this.providerManager.getCurrentProvider();
3516
+ if (!currentProvider) {
3517
+ return [];
3518
+ }
3519
+ return currentProvider.getEnabledAddressTypes();
1634
3520
  }
1635
3521
  // ===== UTILITY METHODS =====
1636
- /**
1637
- * Check if Phantom extension is installed
1638
- */
1639
- static async isPhantomInstalled(timeoutMs) {
1640
- return waitForPhantomExtension(timeoutMs);
1641
- }
1642
3522
  /**
1643
3523
  * Add event listener for provider events (connect, connect_start, connect_error, disconnect, error)
1644
3524
  * Works with both embedded and injected providers
@@ -1658,53 +3538,34 @@ var BrowserSDK = class {
1658
3538
  /**
1659
3539
  * Attempt auto-connection using existing session
1660
3540
  * Should be called after setting up event listeners
1661
- * Only works with embedded providers
3541
+ * Tries embedded provider first, then injected provider as fallback
1662
3542
  */
1663
3543
  async autoConnect() {
1664
- debug.log(DebugCategory.BROWSER_SDK, "Attempting auto-connect");
1665
- const currentProvider = this.providerManager.getCurrentProvider();
1666
- if (currentProvider) {
1667
- await currentProvider.autoConnect();
1668
- } else {
1669
- debug.warn(DebugCategory.BROWSER_SDK, "Current provider does not support auto-connect", {
3544
+ debug.log(DebugCategory.BROWSER_SDK, "Attempting auto-connect with fallback strategy");
3545
+ const result = await this.providerManager.autoConnect();
3546
+ if (result) {
3547
+ debug.info(DebugCategory.BROWSER_SDK, "Auto-connect successful", {
1670
3548
  providerType: this.getCurrentProviderInfo()?.type
1671
3549
  });
3550
+ } else {
3551
+ debug.log(DebugCategory.BROWSER_SDK, "Auto-connect failed for all providers");
1672
3552
  }
1673
3553
  }
1674
- /**
1675
- * Debug configuration methods
1676
- * These allow dynamic debug configuration without SDK reinstantiation
1677
- */
1678
- /**
1679
- * Enable debug logging
1680
- */
1681
3554
  enableDebug() {
1682
3555
  debug.enable();
1683
3556
  debug.info(DebugCategory.BROWSER_SDK, "Debug logging enabled");
1684
3557
  }
1685
- /**
1686
- * Disable debug logging
1687
- */
1688
3558
  disableDebug() {
1689
3559
  debug.disable();
1690
3560
  }
1691
- /**
1692
- * Set debug level
1693
- */
1694
3561
  setDebugLevel(level) {
1695
3562
  debug.setLevel(level);
1696
3563
  debug.info(DebugCategory.BROWSER_SDK, "Debug level updated", { level });
1697
3564
  }
1698
- /**
1699
- * Set debug callback function
1700
- */
1701
3565
  setDebugCallback(callback) {
1702
3566
  debug.setCallback(callback);
1703
3567
  debug.info(DebugCategory.BROWSER_SDK, "Debug callback updated");
1704
3568
  }
1705
- /**
1706
- * Configure debug settings all at once
1707
- */
1708
3569
  configureDebug(config) {
1709
3570
  if (config.enabled !== void 0) {
1710
3571
  if (config.enabled) {
@@ -1810,23 +3671,54 @@ var BrowserSDK = class {
1810
3671
  throw error;
1811
3672
  }
1812
3673
  }
3674
+ getDiscoveredWallets() {
3675
+ debug.log(DebugCategory.BROWSER_SDK, "Getting discovered wallets");
3676
+ try {
3677
+ const allWallets = this.walletRegistry.getByAddressTypes(this.config.addressTypes);
3678
+ debug.log(DebugCategory.BROWSER_SDK, "Retrieved discovered wallets", {
3679
+ count: allWallets.length,
3680
+ walletIds: allWallets.map((w) => w.id)
3681
+ });
3682
+ return allWallets;
3683
+ } catch (error) {
3684
+ debug.error(DebugCategory.BROWSER_SDK, "Failed to get discovered wallets", {
3685
+ error: error.message
3686
+ });
3687
+ return [];
3688
+ }
3689
+ }
1813
3690
  };
1814
3691
 
1815
- // src/utils/deeplink.ts
1816
- function getDeeplinkToPhantom(ref) {
1817
- if (!window.location.href.startsWith("http:") && !window.location.href.startsWith("https:")) {
1818
- throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported for deeplinks");
1819
- }
1820
- const currentUrl = encodeURIComponent(window.location.href);
1821
- const refParam = ref ? `?ref=${encodeURIComponent(ref)}` : "";
1822
- return `https://phantom.app/ul/browse/${currentUrl}${refParam}`;
3692
+ // src/waitForPhantomExtension.ts
3693
+ import { isPhantomExtensionInstalled as isPhantomExtensionInstalled4 } from "@phantom/browser-injected-sdk";
3694
+ async function waitForPhantomExtension(timeoutMs = 3e3) {
3695
+ return new Promise((resolve) => {
3696
+ const startTime = Date.now();
3697
+ const checkInterval = 100;
3698
+ const checkForExtension = () => {
3699
+ try {
3700
+ if (isPhantomExtensionInstalled4()) {
3701
+ resolve(true);
3702
+ return;
3703
+ }
3704
+ } catch (error) {
3705
+ }
3706
+ const elapsed = Date.now() - startTime;
3707
+ if (elapsed >= timeoutMs) {
3708
+ resolve(false);
3709
+ return;
3710
+ }
3711
+ setTimeout(checkForExtension, checkInterval);
3712
+ };
3713
+ checkForExtension();
3714
+ });
1823
3715
  }
1824
3716
 
1825
3717
  // src/index.ts
1826
3718
  import { NetworkId } from "@phantom/constants";
1827
- import { AddressType as AddressType5 } from "@phantom/client";
3719
+ import { AddressType as AddressType3 } from "@phantom/client";
1828
3720
  export {
1829
- AddressType5 as AddressType,
3721
+ AddressType3 as AddressType,
1830
3722
  BrowserSDK,
1831
3723
  DebugCategory,
1832
3724
  DebugLevel,
@@ -1837,6 +3729,7 @@ export {
1837
3729
  getDeeplinkToPhantom,
1838
3730
  getPlatformName,
1839
3731
  isMobileDevice,
3732
+ isPhantomLoginAvailable,
1840
3733
  parseBrowserFromUserAgent,
1841
3734
  waitForPhantomExtension
1842
3735
  };