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