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