@phantom/browser-sdk 1.0.0-beta.9 → 1.0.0
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 +2755 -678
- package/dist/index.mjs +2750 -683
- 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,604 @@ 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.includes("solana") || featureLower.includes("standard:connect") || featureLower.includes("standard:signTransaction");
|
|
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 {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
444
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
445
|
+
var InjectedWalletSolanaChain = class {
|
|
446
|
+
constructor(provider, walletId, walletName) {
|
|
447
|
+
this.eventEmitter = new EventEmitter();
|
|
93
448
|
this._connected = false;
|
|
94
449
|
this._publicKey = null;
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
450
|
+
this.provider = provider;
|
|
451
|
+
this.walletId = walletId;
|
|
452
|
+
this.walletName = walletName;
|
|
98
453
|
this.setupEventListeners();
|
|
99
|
-
this.syncInitialState();
|
|
100
454
|
}
|
|
101
|
-
// Wallet adapter compliant properties
|
|
102
455
|
get connected() {
|
|
103
456
|
return this._connected;
|
|
104
457
|
}
|
|
105
458
|
get publicKey() {
|
|
106
459
|
return this._publicKey;
|
|
107
460
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
461
|
+
async connect(options) {
|
|
462
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connect", {
|
|
463
|
+
walletId: this.walletId,
|
|
464
|
+
walletName: this.walletName,
|
|
465
|
+
onlyIfTrusted: options?.onlyIfTrusted
|
|
466
|
+
});
|
|
467
|
+
try {
|
|
468
|
+
const result = await this.provider.connect(options);
|
|
469
|
+
if (typeof result === "string") {
|
|
470
|
+
this._connected = true;
|
|
471
|
+
this._publicKey = result;
|
|
472
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connected", {
|
|
473
|
+
walletId: this.walletId,
|
|
474
|
+
walletName: this.walletName,
|
|
475
|
+
publicKey: result
|
|
476
|
+
});
|
|
477
|
+
return { publicKey: result };
|
|
478
|
+
}
|
|
479
|
+
if (typeof result === "object" && result !== null && "publicKey" in result) {
|
|
480
|
+
this._connected = true;
|
|
481
|
+
this._publicKey = result.publicKey;
|
|
482
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connected", {
|
|
483
|
+
walletId: this.walletId,
|
|
484
|
+
walletName: this.walletName,
|
|
485
|
+
publicKey: result.publicKey
|
|
486
|
+
});
|
|
487
|
+
return result;
|
|
488
|
+
}
|
|
489
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
490
|
+
const firstAccount = result[0];
|
|
491
|
+
if (typeof firstAccount === "object" && firstAccount !== null && "address" in firstAccount) {
|
|
492
|
+
this._connected = true;
|
|
493
|
+
this._publicKey = firstAccount.address;
|
|
494
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connected", {
|
|
495
|
+
walletId: this.walletId,
|
|
496
|
+
walletName: this.walletName,
|
|
497
|
+
publicKey: firstAccount.address
|
|
498
|
+
});
|
|
499
|
+
return { publicKey: firstAccount.address };
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
throw new Error("Unexpected connect result format");
|
|
503
|
+
} catch (error) {
|
|
504
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana connect failed", {
|
|
505
|
+
walletId: this.walletId,
|
|
506
|
+
walletName: this.walletName,
|
|
507
|
+
error: error instanceof Error ? error.message : String(error)
|
|
508
|
+
});
|
|
509
|
+
throw error;
|
|
117
510
|
}
|
|
118
|
-
this.updateConnectionState(true, solanaAddress.address);
|
|
119
|
-
return Promise.resolve({ publicKey: solanaAddress.address });
|
|
120
511
|
}
|
|
121
512
|
async disconnect() {
|
|
122
|
-
|
|
513
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana disconnect", {
|
|
514
|
+
walletId: this.walletId,
|
|
515
|
+
walletName: this.walletName
|
|
516
|
+
});
|
|
517
|
+
try {
|
|
518
|
+
await this.provider.disconnect();
|
|
519
|
+
this._connected = false;
|
|
520
|
+
this._publicKey = null;
|
|
521
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana disconnected", {
|
|
522
|
+
walletId: this.walletId,
|
|
523
|
+
walletName: this.walletName
|
|
524
|
+
});
|
|
525
|
+
} catch (error) {
|
|
526
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana disconnect failed", {
|
|
527
|
+
walletId: this.walletId,
|
|
528
|
+
walletName: this.walletName,
|
|
529
|
+
error: error instanceof Error ? error.message : String(error)
|
|
530
|
+
});
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
123
533
|
}
|
|
124
|
-
// Standard wallet adapter methods
|
|
125
534
|
async signMessage(message) {
|
|
126
535
|
const messageBytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
536
|
+
const messagePreview = typeof message === "string" ? message.substring(0, 50) : `${messageBytes.length} bytes`;
|
|
537
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signMessage", {
|
|
538
|
+
walletId: this.walletId,
|
|
539
|
+
walletName: this.walletName,
|
|
540
|
+
messagePreview,
|
|
541
|
+
messageLength: messageBytes.length
|
|
542
|
+
});
|
|
543
|
+
try {
|
|
544
|
+
const result = await this.provider.signMessage(message);
|
|
545
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signMessage success", {
|
|
546
|
+
walletId: this.walletId,
|
|
547
|
+
walletName: this.walletName,
|
|
548
|
+
signatureLength: result.signature.length
|
|
549
|
+
});
|
|
550
|
+
return {
|
|
551
|
+
signature: result.signature instanceof Uint8Array ? result.signature : new Uint8Array(Buffer2.from(result.signature, "base64")),
|
|
552
|
+
publicKey: result.publicKey || this._publicKey || ""
|
|
553
|
+
};
|
|
554
|
+
} catch (error) {
|
|
555
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signMessage failed", {
|
|
556
|
+
walletId: this.walletId,
|
|
557
|
+
walletName: this.walletName,
|
|
558
|
+
error: error instanceof Error ? error.message : String(error)
|
|
559
|
+
});
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
132
562
|
}
|
|
133
563
|
async signTransaction(transaction) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
564
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signTransaction", {
|
|
565
|
+
walletId: this.walletId,
|
|
566
|
+
walletName: this.walletName
|
|
567
|
+
});
|
|
137
568
|
try {
|
|
138
|
-
const result = await this.
|
|
569
|
+
const result = await this.provider.signTransaction(transaction);
|
|
570
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signTransaction success", {
|
|
571
|
+
walletId: this.walletId,
|
|
572
|
+
walletName: this.walletName
|
|
573
|
+
});
|
|
139
574
|
return result;
|
|
140
575
|
} catch (error) {
|
|
141
|
-
|
|
576
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signTransaction failed", {
|
|
577
|
+
walletId: this.walletId,
|
|
578
|
+
walletName: this.walletName,
|
|
579
|
+
error: error instanceof Error ? error.message : String(error)
|
|
580
|
+
});
|
|
581
|
+
throw error;
|
|
142
582
|
}
|
|
143
583
|
}
|
|
144
584
|
async signAndSendTransaction(transaction) {
|
|
145
|
-
|
|
146
|
-
|
|
585
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendTransaction", {
|
|
586
|
+
walletId: this.walletId,
|
|
587
|
+
walletName: this.walletName
|
|
588
|
+
});
|
|
589
|
+
try {
|
|
590
|
+
const result = await this.provider.signAndSendTransaction(transaction);
|
|
591
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendTransaction success", {
|
|
592
|
+
walletId: this.walletId,
|
|
593
|
+
walletName: this.walletName,
|
|
594
|
+
signature: result.signature
|
|
595
|
+
});
|
|
596
|
+
return result;
|
|
597
|
+
} catch (error) {
|
|
598
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendTransaction failed", {
|
|
599
|
+
walletId: this.walletId,
|
|
600
|
+
walletName: this.walletName,
|
|
601
|
+
error: error instanceof Error ? error.message : String(error)
|
|
602
|
+
});
|
|
603
|
+
throw error;
|
|
604
|
+
}
|
|
147
605
|
}
|
|
148
606
|
async signAllTransactions(transactions) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
607
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAllTransactions", {
|
|
608
|
+
walletId: this.walletId,
|
|
609
|
+
walletName: this.walletName,
|
|
610
|
+
transactionCount: transactions.length
|
|
611
|
+
});
|
|
152
612
|
try {
|
|
153
|
-
const result = await this.
|
|
613
|
+
const result = await this.provider.signAllTransactions(transactions);
|
|
614
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAllTransactions success", {
|
|
615
|
+
walletId: this.walletId,
|
|
616
|
+
walletName: this.walletName,
|
|
617
|
+
signedCount: result.length
|
|
618
|
+
});
|
|
154
619
|
return result;
|
|
155
620
|
} catch (error) {
|
|
156
|
-
|
|
621
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAllTransactions failed", {
|
|
622
|
+
walletId: this.walletId,
|
|
623
|
+
walletName: this.walletName,
|
|
624
|
+
error: error instanceof Error ? error.message : String(error)
|
|
625
|
+
});
|
|
626
|
+
throw error;
|
|
157
627
|
}
|
|
158
628
|
}
|
|
159
629
|
async signAndSendAllTransactions(transactions) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
630
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendAllTransactions", {
|
|
631
|
+
walletId: this.walletId,
|
|
632
|
+
walletName: this.walletName,
|
|
633
|
+
transactionCount: transactions.length
|
|
634
|
+
});
|
|
163
635
|
try {
|
|
164
|
-
const result = await this.
|
|
165
|
-
|
|
636
|
+
const result = await this.provider.signAndSendAllTransactions(transactions);
|
|
637
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendAllTransactions success", {
|
|
638
|
+
walletId: this.walletId,
|
|
639
|
+
walletName: this.walletName,
|
|
640
|
+
signatureCount: result.signatures.length
|
|
641
|
+
});
|
|
642
|
+
return result;
|
|
166
643
|
} catch (error) {
|
|
167
|
-
|
|
644
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Solana signAndSendAllTransactions failed", {
|
|
645
|
+
walletId: this.walletId,
|
|
646
|
+
walletName: this.walletName,
|
|
647
|
+
error: error instanceof Error ? error.message : String(error)
|
|
648
|
+
});
|
|
649
|
+
throw error;
|
|
168
650
|
}
|
|
169
651
|
}
|
|
170
652
|
switchNetwork(_network) {
|
|
171
653
|
return Promise.resolve();
|
|
172
654
|
}
|
|
173
|
-
// Legacy methods
|
|
174
655
|
getPublicKey() {
|
|
175
656
|
return Promise.resolve(this._publicKey);
|
|
176
657
|
}
|
|
177
658
|
isConnected() {
|
|
178
|
-
return this._connected
|
|
659
|
+
return this._connected;
|
|
179
660
|
}
|
|
180
661
|
setupEventListeners() {
|
|
181
|
-
this.
|
|
182
|
-
this.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
this.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.updateConnectionState(true, solanaAddress.address);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
this.callbacks.on("disconnect", () => {
|
|
200
|
-
this.updateConnectionState(false, null);
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
syncInitialState() {
|
|
204
|
-
if (this.callbacks.isConnected()) {
|
|
205
|
-
const solanaAddress = this.callbacks.getAddresses().find((addr) => addr.addressType === AddressType2.solana);
|
|
206
|
-
if (solanaAddress) {
|
|
207
|
-
this.updateConnectionState(true, solanaAddress.address);
|
|
208
|
-
}
|
|
662
|
+
if (typeof this.provider.on === "function") {
|
|
663
|
+
this.provider.on("connect", (publicKey) => {
|
|
664
|
+
this._connected = true;
|
|
665
|
+
this._publicKey = publicKey;
|
|
666
|
+
this.eventEmitter.emit("connect", publicKey);
|
|
667
|
+
});
|
|
668
|
+
this.provider.on("disconnect", () => {
|
|
669
|
+
this._connected = false;
|
|
670
|
+
this._publicKey = null;
|
|
671
|
+
this.eventEmitter.emit("disconnect");
|
|
672
|
+
});
|
|
673
|
+
this.provider.on("accountChanged", (publicKey) => {
|
|
674
|
+
this._publicKey = publicKey;
|
|
675
|
+
this.eventEmitter.emit("accountChanged", publicKey);
|
|
676
|
+
});
|
|
209
677
|
}
|
|
210
678
|
}
|
|
211
|
-
updateConnectionState(connected, publicKey) {
|
|
212
|
-
this._connected = connected;
|
|
213
|
-
this._publicKey = publicKey;
|
|
214
|
-
}
|
|
215
|
-
// Event methods for interface compliance
|
|
216
679
|
on(event, listener) {
|
|
217
680
|
this.eventEmitter.on(event, listener);
|
|
218
681
|
}
|
|
@@ -221,56 +684,855 @@ var InjectedSolanaChain = class {
|
|
|
221
684
|
}
|
|
222
685
|
};
|
|
223
686
|
|
|
224
|
-
// src/providers/injected/chains/
|
|
225
|
-
import {
|
|
226
|
-
import
|
|
227
|
-
var
|
|
228
|
-
constructor(
|
|
687
|
+
// src/providers/injected/chains/WalletStandardSolanaAdapter.ts
|
|
688
|
+
import { deserializeSolanaTransaction } from "@phantom/parsers";
|
|
689
|
+
import bs58 from "bs58";
|
|
690
|
+
var WalletStandardSolanaAdapter = class {
|
|
691
|
+
constructor(wallet, walletId, walletName) {
|
|
229
692
|
this._connected = false;
|
|
230
|
-
this.
|
|
231
|
-
this.
|
|
232
|
-
this.
|
|
233
|
-
this.
|
|
234
|
-
this.callbacks = callbacks;
|
|
235
|
-
this.setupEventListeners();
|
|
236
|
-
this.syncInitialState();
|
|
693
|
+
this._publicKey = null;
|
|
694
|
+
this.wallet = wallet;
|
|
695
|
+
this.walletId = walletId;
|
|
696
|
+
this.walletName = walletName;
|
|
237
697
|
}
|
|
238
|
-
// EIP-1193 compliant properties
|
|
239
698
|
get connected() {
|
|
240
699
|
return this._connected;
|
|
241
700
|
}
|
|
242
|
-
get
|
|
243
|
-
return this.
|
|
244
|
-
}
|
|
245
|
-
get accounts() {
|
|
246
|
-
return this._accounts;
|
|
701
|
+
get publicKey() {
|
|
702
|
+
return this._publicKey;
|
|
247
703
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
704
|
+
async connect(_options) {
|
|
705
|
+
try {
|
|
706
|
+
const connectFeature = this.wallet.features?.["standard:connect"];
|
|
707
|
+
if (!connectFeature || typeof connectFeature.connect !== "function") {
|
|
708
|
+
throw new Error("Wallet Standard connect feature not available");
|
|
709
|
+
}
|
|
710
|
+
const connectResult = await connectFeature.connect();
|
|
711
|
+
let accounts;
|
|
712
|
+
if (Array.isArray(connectResult) && connectResult.length > 0) {
|
|
713
|
+
accounts = connectResult;
|
|
714
|
+
} else if (this.wallet.accounts && this.wallet.accounts.length > 0) {
|
|
715
|
+
accounts = Array.from(this.wallet.accounts);
|
|
716
|
+
}
|
|
717
|
+
if (!accounts || accounts.length === 0) {
|
|
718
|
+
throw new Error("No accounts available after connecting to wallet");
|
|
719
|
+
}
|
|
720
|
+
const firstAccount = accounts[0];
|
|
721
|
+
if (!firstAccount) {
|
|
722
|
+
throw new Error("First account is null or undefined");
|
|
723
|
+
}
|
|
724
|
+
let address;
|
|
725
|
+
if (typeof firstAccount === "string") {
|
|
726
|
+
address = firstAccount;
|
|
727
|
+
} else if (typeof firstAccount === "object" && firstAccount !== null) {
|
|
728
|
+
address = firstAccount.address || firstAccount.publicKey?.toString() || (firstAccount.publicKey instanceof Uint8Array ? Buffer.from(firstAccount.publicKey).toString("hex") : void 0);
|
|
729
|
+
}
|
|
730
|
+
if (!address) {
|
|
731
|
+
throw new Error(
|
|
732
|
+
`Could not extract address from account. Account structure: ${JSON.stringify(firstAccount, null, 2)}`
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
this._connected = true;
|
|
736
|
+
this._publicKey = address;
|
|
737
|
+
return { publicKey: address };
|
|
738
|
+
} catch (error) {
|
|
739
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana connect failed", {
|
|
740
|
+
walletId: this.walletId,
|
|
741
|
+
walletName: this.walletName,
|
|
742
|
+
error: error instanceof Error ? error.message : String(error)
|
|
743
|
+
});
|
|
744
|
+
throw error;
|
|
254
745
|
}
|
|
255
|
-
const provider = await this.phantom.ethereum.getProvider();
|
|
256
|
-
return await provider.request(args);
|
|
257
746
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
747
|
+
async disconnect() {
|
|
748
|
+
try {
|
|
749
|
+
const disconnectFeature = this.wallet.features?.["standard:disconnect"];
|
|
750
|
+
if (disconnectFeature && typeof disconnectFeature.disconnect === "function") {
|
|
751
|
+
await disconnectFeature.disconnect();
|
|
752
|
+
}
|
|
753
|
+
this._connected = false;
|
|
754
|
+
this._publicKey = null;
|
|
755
|
+
} catch (error) {
|
|
756
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana disconnect failed", {
|
|
757
|
+
walletId: this.walletId,
|
|
758
|
+
walletName: this.walletName,
|
|
759
|
+
error: error instanceof Error ? error.message : String(error)
|
|
760
|
+
});
|
|
761
|
+
throw error;
|
|
262
762
|
}
|
|
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
763
|
}
|
|
268
|
-
async
|
|
269
|
-
|
|
764
|
+
async signMessage(message) {
|
|
765
|
+
try {
|
|
766
|
+
const signMessageFeature = this.wallet.features?.["solana:signMessage"];
|
|
767
|
+
if (!signMessageFeature || typeof signMessageFeature.signMessage !== "function") {
|
|
768
|
+
throw new Error("Wallet Standard signMessage feature not available");
|
|
769
|
+
}
|
|
770
|
+
const messageBytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
771
|
+
const result = await signMessageFeature.signMessage({
|
|
772
|
+
message: messageBytes,
|
|
773
|
+
account: this.wallet.accounts?.[0]
|
|
774
|
+
});
|
|
775
|
+
if (!Array.isArray(result) || result.length === 0) {
|
|
776
|
+
throw new Error(`Expected array result from signMessage, got: ${typeof result}`);
|
|
777
|
+
}
|
|
778
|
+
const signedMessageResult = result[0];
|
|
779
|
+
if (!signedMessageResult || !signedMessageResult.signature) {
|
|
780
|
+
throw new Error(`Invalid signMessage result structure: ${JSON.stringify(result)}`);
|
|
781
|
+
}
|
|
782
|
+
const signature = this.parseUint8Array(signedMessageResult.signature);
|
|
783
|
+
if (signature.length === 0) {
|
|
784
|
+
throw new Error(`Signature is empty`);
|
|
785
|
+
}
|
|
786
|
+
const publicKey = signedMessageResult.account?.address || this.wallet.accounts?.[0]?.address || this._publicKey || "";
|
|
787
|
+
return { signature, publicKey };
|
|
788
|
+
} catch (error) {
|
|
789
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signMessage failed", {
|
|
790
|
+
walletId: this.walletId,
|
|
791
|
+
walletName: this.walletName,
|
|
792
|
+
error: error instanceof Error ? error.message : String(error)
|
|
793
|
+
});
|
|
794
|
+
throw error;
|
|
795
|
+
}
|
|
270
796
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
797
|
+
async signTransaction(transaction) {
|
|
798
|
+
try {
|
|
799
|
+
const signTransactionFeature = this.wallet.features?.["solana:signTransaction"];
|
|
800
|
+
if (!signTransactionFeature || typeof signTransactionFeature.signTransaction !== "function") {
|
|
801
|
+
throw new Error("Wallet Standard signTransaction feature not available");
|
|
802
|
+
}
|
|
803
|
+
if (!this.wallet.accounts || this.wallet.accounts.length === 0) {
|
|
804
|
+
throw new Error("No accounts available. Please connect first.");
|
|
805
|
+
}
|
|
806
|
+
const account = this.wallet.accounts[0];
|
|
807
|
+
const serializedTransaction = this.serializeTransaction(transaction);
|
|
808
|
+
const results = await signTransactionFeature.signTransaction({
|
|
809
|
+
transaction: serializedTransaction,
|
|
810
|
+
account
|
|
811
|
+
});
|
|
812
|
+
let transactionData;
|
|
813
|
+
if (Array.isArray(results) && results.length > 0) {
|
|
814
|
+
const firstItem = results[0];
|
|
815
|
+
if (firstItem && typeof firstItem === "object") {
|
|
816
|
+
transactionData = firstItem.signedTransaction || firstItem.transaction;
|
|
817
|
+
}
|
|
818
|
+
} else if (results && typeof results === "object" && !Array.isArray(results)) {
|
|
819
|
+
transactionData = results.transaction || results.signedTransaction;
|
|
820
|
+
}
|
|
821
|
+
if (!transactionData) {
|
|
822
|
+
throw new Error("No transaction data found in Wallet Standard result");
|
|
823
|
+
}
|
|
824
|
+
const signedBytes = this.parseUint8Array(transactionData);
|
|
825
|
+
if (signedBytes.length === 0) {
|
|
826
|
+
throw new Error("Empty signed transaction returned from Wallet Standard");
|
|
827
|
+
}
|
|
828
|
+
const signedTx = deserializeSolanaTransaction(signedBytes);
|
|
829
|
+
return signedTx;
|
|
830
|
+
} catch (error) {
|
|
831
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signTransaction failed", {
|
|
832
|
+
walletId: this.walletId,
|
|
833
|
+
walletName: this.walletName,
|
|
834
|
+
error: error instanceof Error ? error.message : String(error)
|
|
835
|
+
});
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async signAndSendTransaction(transaction) {
|
|
840
|
+
try {
|
|
841
|
+
const signAndSendTransactionFeature = this.wallet.features?.["solana:signAndSendTransaction"];
|
|
842
|
+
if (!signAndSendTransactionFeature || typeof signAndSendTransactionFeature.signAndSendTransaction !== "function") {
|
|
843
|
+
throw new Error("Wallet Standard signAndSendTransaction feature not available");
|
|
844
|
+
}
|
|
845
|
+
if (!this.wallet.accounts || this.wallet.accounts.length === 0) {
|
|
846
|
+
throw new Error("No accounts available. Please connect first.");
|
|
847
|
+
}
|
|
848
|
+
const account = this.wallet.accounts[0];
|
|
849
|
+
const chain = account.chains?.[0] || "solana:mainnet";
|
|
850
|
+
const serializedTransaction = this.serializeTransaction(transaction);
|
|
851
|
+
const results = await signAndSendTransactionFeature.signAndSendTransaction({
|
|
852
|
+
transaction: serializedTransaction,
|
|
853
|
+
account,
|
|
854
|
+
chain
|
|
855
|
+
});
|
|
856
|
+
let signatureOutput;
|
|
857
|
+
if (Array.isArray(results) && results.length > 0) {
|
|
858
|
+
signatureOutput = results[0];
|
|
859
|
+
} else if (results && typeof results === "object" && !Array.isArray(results)) {
|
|
860
|
+
signatureOutput = results;
|
|
861
|
+
} else {
|
|
862
|
+
throw new Error("Invalid signAndSendTransaction result format");
|
|
863
|
+
}
|
|
864
|
+
if (!signatureOutput.signature) {
|
|
865
|
+
throw new Error("No signature found in signAndSendTransaction result");
|
|
866
|
+
}
|
|
867
|
+
const signatureBytes = this.parseUint8Array(signatureOutput.signature);
|
|
868
|
+
const signature = bs58.encode(signatureBytes);
|
|
869
|
+
return { signature };
|
|
870
|
+
} catch (error) {
|
|
871
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signAndSendTransaction failed", {
|
|
872
|
+
walletId: this.walletId,
|
|
873
|
+
walletName: this.walletName,
|
|
874
|
+
error: error instanceof Error ? error.message : String(error)
|
|
875
|
+
});
|
|
876
|
+
throw error;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async signAllTransactions(transactions) {
|
|
880
|
+
try {
|
|
881
|
+
const signedTransactions = [];
|
|
882
|
+
for (const transaction of transactions) {
|
|
883
|
+
const signedTx = await this.signTransaction(transaction);
|
|
884
|
+
signedTransactions.push(signedTx);
|
|
885
|
+
}
|
|
886
|
+
return signedTransactions;
|
|
887
|
+
} catch (error) {
|
|
888
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signAllTransactions failed", {
|
|
889
|
+
walletId: this.walletId,
|
|
890
|
+
walletName: this.walletName,
|
|
891
|
+
error: error instanceof Error ? error.message : String(error)
|
|
892
|
+
});
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
async signAndSendAllTransactions(transactions) {
|
|
897
|
+
try {
|
|
898
|
+
const signatures = [];
|
|
899
|
+
for (const transaction of transactions) {
|
|
900
|
+
const result = await this.signAndSendTransaction(transaction);
|
|
901
|
+
signatures.push(result.signature);
|
|
902
|
+
}
|
|
903
|
+
return { signatures };
|
|
904
|
+
} catch (error) {
|
|
905
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana signAndSendAllTransactions failed", {
|
|
906
|
+
walletId: this.walletId,
|
|
907
|
+
walletName: this.walletName,
|
|
908
|
+
error: error instanceof Error ? error.message : String(error)
|
|
909
|
+
});
|
|
910
|
+
throw error;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
async switchNetwork(network) {
|
|
914
|
+
try {
|
|
915
|
+
const switchNetworkFeature = this.wallet.features?.["standard:switchNetwork"];
|
|
916
|
+
if (switchNetworkFeature && typeof switchNetworkFeature.switchNetwork === "function") {
|
|
917
|
+
const chainId = network === "mainnet" ? "solana:mainnet" : "solana:devnet";
|
|
918
|
+
await switchNetworkFeature.switchNetwork({ chain: chainId });
|
|
919
|
+
}
|
|
920
|
+
} catch (error) {
|
|
921
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet Standard Solana switchNetwork failed", {
|
|
922
|
+
walletId: this.walletId,
|
|
923
|
+
walletName: this.walletName,
|
|
924
|
+
network,
|
|
925
|
+
error: error instanceof Error ? error.message : String(error)
|
|
926
|
+
});
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
getPublicKey() {
|
|
931
|
+
return Promise.resolve(this._publicKey);
|
|
932
|
+
}
|
|
933
|
+
isConnected() {
|
|
934
|
+
return this._connected;
|
|
935
|
+
}
|
|
936
|
+
on(_event, _listener) {
|
|
937
|
+
const eventsFeature = this.wallet.features?.["standard:events"];
|
|
938
|
+
if (eventsFeature && typeof eventsFeature.on === "function") {
|
|
939
|
+
eventsFeature.on(_event, _listener);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
off(_event, _listener) {
|
|
943
|
+
const eventsFeature = this.wallet.features?.["standard:events"];
|
|
944
|
+
if (eventsFeature && typeof eventsFeature.off === "function") {
|
|
945
|
+
eventsFeature.off(_event, _listener);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Serialize a transaction to Uint8Array for Wallet Standard API
|
|
950
|
+
*/
|
|
951
|
+
serializeTransaction(transaction) {
|
|
952
|
+
if (typeof transaction.serialize === "function") {
|
|
953
|
+
return transaction.serialize({
|
|
954
|
+
requireAllSignatures: false,
|
|
955
|
+
verifySignatures: false
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
if (transaction instanceof Uint8Array) {
|
|
959
|
+
return transaction;
|
|
960
|
+
}
|
|
961
|
+
return new Uint8Array(0);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Parse a Uint8Array from various formats
|
|
965
|
+
* Handles: Uint8Array, Array, object with numeric keys (JSON-serialized Uint8Array)
|
|
966
|
+
*/
|
|
967
|
+
parseUint8Array(value) {
|
|
968
|
+
if (value instanceof Uint8Array) {
|
|
969
|
+
return value;
|
|
970
|
+
}
|
|
971
|
+
if (Array.isArray(value)) {
|
|
972
|
+
return new Uint8Array(value);
|
|
973
|
+
}
|
|
974
|
+
if (typeof value === "object" && value !== null) {
|
|
975
|
+
const keys = Object.keys(value).map(Number).filter((k) => !isNaN(k) && k >= 0).sort((a, b) => a - b);
|
|
976
|
+
if (keys.length > 0) {
|
|
977
|
+
const maxKey = Math.max(...keys);
|
|
978
|
+
const array = new Uint8Array(maxKey + 1);
|
|
979
|
+
for (const key of keys) {
|
|
980
|
+
array[key] = Number(value[key]) || 0;
|
|
981
|
+
}
|
|
982
|
+
return array;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return new Uint8Array(0);
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
// src/providers/injected/chains/InjectedWalletEthereumChain.ts
|
|
990
|
+
import { EventEmitter as EventEmitter2 } from "eventemitter3";
|
|
991
|
+
var InjectedWalletEthereumChain = class {
|
|
992
|
+
constructor(provider, walletId, walletName) {
|
|
993
|
+
this.eventEmitter = new EventEmitter2();
|
|
994
|
+
this._connected = false;
|
|
995
|
+
this._chainId = "0x1";
|
|
996
|
+
this._accounts = [];
|
|
997
|
+
this.provider = provider;
|
|
998
|
+
this.walletId = walletId;
|
|
999
|
+
this.walletName = walletName;
|
|
1000
|
+
this.setupEventListeners();
|
|
1001
|
+
}
|
|
1002
|
+
get connected() {
|
|
1003
|
+
return this._connected;
|
|
1004
|
+
}
|
|
1005
|
+
get chainId() {
|
|
1006
|
+
return this._chainId;
|
|
1007
|
+
}
|
|
1008
|
+
get accounts() {
|
|
1009
|
+
return this._accounts;
|
|
1010
|
+
}
|
|
1011
|
+
async request(args) {
|
|
1012
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request", {
|
|
1013
|
+
walletId: this.walletId,
|
|
1014
|
+
walletName: this.walletName,
|
|
1015
|
+
method: args.method
|
|
1016
|
+
});
|
|
1017
|
+
try {
|
|
1018
|
+
const requiresAuth = [
|
|
1019
|
+
"personal_sign",
|
|
1020
|
+
"eth_sign",
|
|
1021
|
+
"eth_signTypedData",
|
|
1022
|
+
"eth_signTypedData_v4",
|
|
1023
|
+
"eth_sendTransaction",
|
|
1024
|
+
"eth_signTransaction"
|
|
1025
|
+
].includes(args.method);
|
|
1026
|
+
if (requiresAuth && (!this._connected || this._accounts.length === 0)) {
|
|
1027
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Method requires authorization, ensuring connection", {
|
|
1028
|
+
walletId: this.walletId,
|
|
1029
|
+
walletName: this.walletName,
|
|
1030
|
+
method: args.method
|
|
1031
|
+
});
|
|
1032
|
+
await this.connect();
|
|
1033
|
+
}
|
|
1034
|
+
const result = await this.provider.request(args);
|
|
1035
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request success", {
|
|
1036
|
+
walletId: this.walletId,
|
|
1037
|
+
walletName: this.walletName,
|
|
1038
|
+
method: args.method
|
|
1039
|
+
});
|
|
1040
|
+
return result;
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
if (error?.code === 4100) {
|
|
1043
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Got 4100 Unauthorized, attempting to re-authorize", {
|
|
1044
|
+
walletId: this.walletId,
|
|
1045
|
+
walletName: this.walletName,
|
|
1046
|
+
method: args.method
|
|
1047
|
+
});
|
|
1048
|
+
try {
|
|
1049
|
+
await this.provider.request({ method: "eth_requestAccounts" });
|
|
1050
|
+
const result = await this.provider.request(args);
|
|
1051
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request success (after re-auth)", {
|
|
1052
|
+
walletId: this.walletId,
|
|
1053
|
+
walletName: this.walletName,
|
|
1054
|
+
method: args.method
|
|
1055
|
+
});
|
|
1056
|
+
return result;
|
|
1057
|
+
} catch (retryError) {
|
|
1058
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Failed after re-authorization", {
|
|
1059
|
+
walletId: this.walletId,
|
|
1060
|
+
walletName: this.walletName,
|
|
1061
|
+
method: args.method,
|
|
1062
|
+
error: retryError instanceof Error ? retryError.message : String(retryError)
|
|
1063
|
+
});
|
|
1064
|
+
throw retryError;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum request failed", {
|
|
1068
|
+
walletId: this.walletId,
|
|
1069
|
+
walletName: this.walletName,
|
|
1070
|
+
method: args.method,
|
|
1071
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1072
|
+
errorCode: error?.code
|
|
1073
|
+
});
|
|
1074
|
+
throw error;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
async connect() {
|
|
1078
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum connect", {
|
|
1079
|
+
walletId: this.walletId,
|
|
1080
|
+
walletName: this.walletName
|
|
1081
|
+
});
|
|
1082
|
+
try {
|
|
1083
|
+
const accounts = await this.provider.request({ method: "eth_requestAccounts" });
|
|
1084
|
+
this._connected = accounts.length > 0;
|
|
1085
|
+
this._accounts = accounts;
|
|
1086
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum connected (via eth_requestAccounts)", {
|
|
1087
|
+
walletId: this.walletId,
|
|
1088
|
+
walletName: this.walletName,
|
|
1089
|
+
accountCount: accounts.length,
|
|
1090
|
+
accounts
|
|
1091
|
+
});
|
|
1092
|
+
return accounts;
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum connect failed", {
|
|
1095
|
+
walletId: this.walletId,
|
|
1096
|
+
walletName: this.walletName,
|
|
1097
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1098
|
+
});
|
|
1099
|
+
throw error;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
async disconnect() {
|
|
1103
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum disconnect", {
|
|
1104
|
+
walletId: this.walletId,
|
|
1105
|
+
walletName: this.walletName
|
|
1106
|
+
});
|
|
1107
|
+
try {
|
|
1108
|
+
await this.provider.disconnect();
|
|
1109
|
+
this._connected = false;
|
|
1110
|
+
this._accounts = [];
|
|
1111
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum disconnected", {
|
|
1112
|
+
walletId: this.walletId,
|
|
1113
|
+
walletName: this.walletName
|
|
1114
|
+
});
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum disconnect failed", {
|
|
1117
|
+
walletId: this.walletId,
|
|
1118
|
+
walletName: this.walletName,
|
|
1119
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1120
|
+
});
|
|
1121
|
+
throw error;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
async signPersonalMessage(message, address) {
|
|
1125
|
+
const messagePreview = message.length > 50 ? message.substring(0, 50) + "..." : message;
|
|
1126
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage", {
|
|
1127
|
+
walletId: this.walletId,
|
|
1128
|
+
walletName: this.walletName,
|
|
1129
|
+
messagePreview,
|
|
1130
|
+
messageLength: message.length,
|
|
1131
|
+
address
|
|
1132
|
+
});
|
|
1133
|
+
try {
|
|
1134
|
+
const providerConnected = this.provider.isConnected?.() || this.provider.connected || false;
|
|
1135
|
+
if (!this._connected || this._accounts.length === 0 || !providerConnected) {
|
|
1136
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Not connected, attempting to connect before signing", {
|
|
1137
|
+
walletId: this.walletId,
|
|
1138
|
+
walletName: this.walletName,
|
|
1139
|
+
internalConnected: this._connected,
|
|
1140
|
+
accountsLength: this._accounts.length,
|
|
1141
|
+
providerConnected
|
|
1142
|
+
});
|
|
1143
|
+
await this.connect();
|
|
1144
|
+
}
|
|
1145
|
+
const normalizedAddress = address.toLowerCase();
|
|
1146
|
+
const normalizedAccounts = this._accounts.map((acc) => acc.toLowerCase());
|
|
1147
|
+
if (!normalizedAccounts.includes(normalizedAddress)) {
|
|
1148
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Address not in connected accounts, refreshing connection", {
|
|
1149
|
+
walletId: this.walletId,
|
|
1150
|
+
walletName: this.walletName,
|
|
1151
|
+
requestedAddress: address,
|
|
1152
|
+
connectedAccounts: this._accounts
|
|
1153
|
+
});
|
|
1154
|
+
const currentAccounts = await this.getAccounts();
|
|
1155
|
+
const normalizedCurrentAccounts = currentAccounts.map((acc) => acc.toLowerCase());
|
|
1156
|
+
if (!normalizedCurrentAccounts.includes(normalizedAddress)) {
|
|
1157
|
+
throw new Error(`Address ${address} is not connected. Connected accounts: ${currentAccounts.join(", ")}`);
|
|
1158
|
+
}
|
|
1159
|
+
this._accounts = currentAccounts;
|
|
1160
|
+
}
|
|
1161
|
+
if (typeof this.provider.signPersonalMessage === "function") {
|
|
1162
|
+
const result2 = await this.provider.signPersonalMessage(message, address);
|
|
1163
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage success", {
|
|
1164
|
+
walletId: this.walletId,
|
|
1165
|
+
walletName: this.walletName,
|
|
1166
|
+
signatureLength: result2.length
|
|
1167
|
+
});
|
|
1168
|
+
return result2;
|
|
1169
|
+
}
|
|
1170
|
+
const result = await this.request({
|
|
1171
|
+
method: "personal_sign",
|
|
1172
|
+
params: [message, address]
|
|
1173
|
+
});
|
|
1174
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage success", {
|
|
1175
|
+
walletId: this.walletId,
|
|
1176
|
+
walletName: this.walletName,
|
|
1177
|
+
signatureLength: result.length
|
|
1178
|
+
});
|
|
1179
|
+
return result;
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signPersonalMessage failed", {
|
|
1182
|
+
walletId: this.walletId,
|
|
1183
|
+
walletName: this.walletName,
|
|
1184
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1185
|
+
errorCode: error?.code
|
|
1186
|
+
});
|
|
1187
|
+
throw error;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
async signTypedData(typedData, address) {
|
|
1191
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData", {
|
|
1192
|
+
walletId: this.walletId,
|
|
1193
|
+
walletName: this.walletName,
|
|
1194
|
+
primaryType: typedData?.primaryType,
|
|
1195
|
+
address
|
|
1196
|
+
});
|
|
1197
|
+
try {
|
|
1198
|
+
if (typeof this.provider.signTypedData === "function") {
|
|
1199
|
+
const result2 = await this.provider.signTypedData(typedData, address);
|
|
1200
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData success", {
|
|
1201
|
+
walletId: this.walletId,
|
|
1202
|
+
walletName: this.walletName,
|
|
1203
|
+
signatureLength: result2.length
|
|
1204
|
+
});
|
|
1205
|
+
return result2;
|
|
1206
|
+
}
|
|
1207
|
+
const result = await this.request({
|
|
1208
|
+
method: "eth_signTypedData_v4",
|
|
1209
|
+
params: [address, typedData]
|
|
1210
|
+
});
|
|
1211
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData success", {
|
|
1212
|
+
walletId: this.walletId,
|
|
1213
|
+
walletName: this.walletName,
|
|
1214
|
+
signatureLength: result.length
|
|
1215
|
+
});
|
|
1216
|
+
return result;
|
|
1217
|
+
} catch (error) {
|
|
1218
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTypedData failed", {
|
|
1219
|
+
walletId: this.walletId,
|
|
1220
|
+
walletName: this.walletName,
|
|
1221
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1222
|
+
});
|
|
1223
|
+
throw error;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
async signTransaction(transaction) {
|
|
1227
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction", {
|
|
1228
|
+
walletId: this.walletId,
|
|
1229
|
+
walletName: this.walletName,
|
|
1230
|
+
from: transaction.from,
|
|
1231
|
+
to: transaction.to
|
|
1232
|
+
});
|
|
1233
|
+
try {
|
|
1234
|
+
if (typeof this.provider.signTransaction === "function") {
|
|
1235
|
+
const result2 = await this.provider.signTransaction(transaction);
|
|
1236
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction success", {
|
|
1237
|
+
walletId: this.walletId,
|
|
1238
|
+
walletName: this.walletName,
|
|
1239
|
+
signatureLength: result2.length
|
|
1240
|
+
});
|
|
1241
|
+
return result2;
|
|
1242
|
+
}
|
|
1243
|
+
const result = await this.request({
|
|
1244
|
+
method: "eth_signTransaction",
|
|
1245
|
+
params: [transaction]
|
|
1246
|
+
});
|
|
1247
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction success", {
|
|
1248
|
+
walletId: this.walletId,
|
|
1249
|
+
walletName: this.walletName,
|
|
1250
|
+
signatureLength: result.length
|
|
1251
|
+
});
|
|
1252
|
+
return result;
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum signTransaction failed", {
|
|
1255
|
+
walletId: this.walletId,
|
|
1256
|
+
walletName: this.walletName,
|
|
1257
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1258
|
+
});
|
|
1259
|
+
throw error;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
async sendTransaction(transaction) {
|
|
1263
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction", {
|
|
1264
|
+
walletId: this.walletId,
|
|
1265
|
+
walletName: this.walletName,
|
|
1266
|
+
from: transaction.from,
|
|
1267
|
+
to: transaction.to,
|
|
1268
|
+
value: transaction.value
|
|
1269
|
+
});
|
|
1270
|
+
try {
|
|
1271
|
+
if (typeof this.provider.sendTransaction === "function") {
|
|
1272
|
+
const result2 = await this.provider.sendTransaction(transaction);
|
|
1273
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction success", {
|
|
1274
|
+
walletId: this.walletId,
|
|
1275
|
+
walletName: this.walletName,
|
|
1276
|
+
txHash: result2
|
|
1277
|
+
});
|
|
1278
|
+
return result2;
|
|
1279
|
+
}
|
|
1280
|
+
const result = await this.request({
|
|
1281
|
+
method: "eth_sendTransaction",
|
|
1282
|
+
params: [transaction]
|
|
1283
|
+
});
|
|
1284
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction success", {
|
|
1285
|
+
walletId: this.walletId,
|
|
1286
|
+
walletName: this.walletName,
|
|
1287
|
+
txHash: result
|
|
1288
|
+
});
|
|
1289
|
+
return result;
|
|
1290
|
+
} catch (error) {
|
|
1291
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum sendTransaction failed", {
|
|
1292
|
+
walletId: this.walletId,
|
|
1293
|
+
walletName: this.walletName,
|
|
1294
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1295
|
+
});
|
|
1296
|
+
throw error;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
async switchChain(chainId) {
|
|
1300
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum switchChain", {
|
|
1301
|
+
walletId: this.walletId,
|
|
1302
|
+
walletName: this.walletName,
|
|
1303
|
+
chainId
|
|
1304
|
+
});
|
|
1305
|
+
try {
|
|
1306
|
+
const hexChainId = typeof chainId === "string" ? chainId.toLowerCase().startsWith("0x") ? chainId : `0x${parseInt(chainId, 10).toString(16)}` : `0x${chainId.toString(16)}`;
|
|
1307
|
+
if (typeof this.provider.switchChain === "function") {
|
|
1308
|
+
await this.provider.switchChain(hexChainId);
|
|
1309
|
+
} else {
|
|
1310
|
+
await this.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }] });
|
|
1311
|
+
}
|
|
1312
|
+
this._chainId = hexChainId;
|
|
1313
|
+
this.eventEmitter.emit("chainChanged", this._chainId);
|
|
1314
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum switchChain success", {
|
|
1315
|
+
walletId: this.walletId,
|
|
1316
|
+
walletName: this.walletName,
|
|
1317
|
+
chainId: hexChainId
|
|
1318
|
+
});
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum switchChain failed", {
|
|
1321
|
+
walletId: this.walletId,
|
|
1322
|
+
walletName: this.walletName,
|
|
1323
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1324
|
+
});
|
|
1325
|
+
throw error;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
async getChainId() {
|
|
1329
|
+
if (typeof this.provider.getChainId === "function") {
|
|
1330
|
+
const chainId2 = await this.provider.getChainId();
|
|
1331
|
+
this._chainId = `0x${chainId2.toString(16)}`;
|
|
1332
|
+
return chainId2;
|
|
1333
|
+
}
|
|
1334
|
+
const chainId = await this.request({ method: "eth_chainId" });
|
|
1335
|
+
const parsed = parseInt(chainId, 16);
|
|
1336
|
+
this._chainId = chainId;
|
|
1337
|
+
return parsed;
|
|
1338
|
+
}
|
|
1339
|
+
async getAccounts() {
|
|
1340
|
+
if (typeof this.provider.getAccounts === "function") {
|
|
1341
|
+
const accounts2 = await this.provider.getAccounts();
|
|
1342
|
+
this._accounts = accounts2;
|
|
1343
|
+
return accounts2;
|
|
1344
|
+
}
|
|
1345
|
+
const accounts = await this.request({ method: "eth_accounts" });
|
|
1346
|
+
this._accounts = accounts;
|
|
1347
|
+
return accounts;
|
|
1348
|
+
}
|
|
1349
|
+
isConnected() {
|
|
1350
|
+
return this._connected;
|
|
1351
|
+
}
|
|
1352
|
+
setupEventListeners() {
|
|
1353
|
+
if (typeof this.provider.on === "function") {
|
|
1354
|
+
this.provider.on("connect", (info) => {
|
|
1355
|
+
this._connected = true;
|
|
1356
|
+
this._chainId = info.chainId;
|
|
1357
|
+
this.eventEmitter.emit("connect", info);
|
|
1358
|
+
});
|
|
1359
|
+
this.provider.on("disconnect", (error) => {
|
|
1360
|
+
this._connected = false;
|
|
1361
|
+
this._accounts = [];
|
|
1362
|
+
this.eventEmitter.emit("disconnect", error);
|
|
1363
|
+
this.eventEmitter.emit("accountsChanged", []);
|
|
1364
|
+
});
|
|
1365
|
+
this.provider.on("accountsChanged", (accounts) => {
|
|
1366
|
+
this._accounts = accounts;
|
|
1367
|
+
this.eventEmitter.emit("accountsChanged", accounts);
|
|
1368
|
+
});
|
|
1369
|
+
this.provider.on("chainChanged", (chainId) => {
|
|
1370
|
+
this._chainId = chainId;
|
|
1371
|
+
this.eventEmitter.emit("chainChanged", chainId);
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
on(event, listener) {
|
|
1376
|
+
this.eventEmitter.on(event, listener);
|
|
1377
|
+
}
|
|
1378
|
+
off(event, listener) {
|
|
1379
|
+
this.eventEmitter.off(event, listener);
|
|
1380
|
+
}
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// src/providers/injected/chains/SolanaChain.ts
|
|
1384
|
+
import { EventEmitter as EventEmitter3 } from "eventemitter3";
|
|
1385
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
1386
|
+
var PhantomSolanaChain = class {
|
|
1387
|
+
constructor(phantom) {
|
|
1388
|
+
this._publicKey = null;
|
|
1389
|
+
this.eventEmitter = new EventEmitter3();
|
|
1390
|
+
this.phantom = phantom;
|
|
1391
|
+
this.setupEventListeners();
|
|
1392
|
+
}
|
|
1393
|
+
// Wallet adapter compliant properties
|
|
1394
|
+
get connected() {
|
|
1395
|
+
return this._publicKey !== null;
|
|
1396
|
+
}
|
|
1397
|
+
get publicKey() {
|
|
1398
|
+
return this._publicKey;
|
|
1399
|
+
}
|
|
1400
|
+
// Connection methods
|
|
1401
|
+
async connect(options) {
|
|
1402
|
+
const result = await this.phantom.solana.connect(options);
|
|
1403
|
+
if (!result) {
|
|
1404
|
+
throw new Error("Failed to connect to Solana wallet");
|
|
1405
|
+
}
|
|
1406
|
+
const publicKey = typeof result === "string" ? result : "";
|
|
1407
|
+
this._publicKey = publicKey;
|
|
1408
|
+
return { publicKey };
|
|
1409
|
+
}
|
|
1410
|
+
async disconnect() {
|
|
1411
|
+
await this.phantom.solana.disconnect();
|
|
1412
|
+
this._publicKey = null;
|
|
1413
|
+
}
|
|
1414
|
+
// Standard wallet adapter methods
|
|
1415
|
+
async signMessage(message) {
|
|
1416
|
+
const messageBytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1417
|
+
const result = await this.phantom.solana.signMessage(messageBytes);
|
|
1418
|
+
return {
|
|
1419
|
+
signature: result.signature instanceof Uint8Array ? result.signature : new Uint8Array(Buffer3.from(result.signature, "base64")),
|
|
1420
|
+
publicKey: this._publicKey || ""
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
async signTransaction(transaction) {
|
|
1424
|
+
if (!this.connected) {
|
|
1425
|
+
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
1426
|
+
}
|
|
1427
|
+
try {
|
|
1428
|
+
const result = await this.phantom.solana.signTransaction(transaction);
|
|
1429
|
+
return result;
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
return Promise.reject(error);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
async signAndSendTransaction(transaction) {
|
|
1435
|
+
const result = await this.phantom.solana.signAndSendTransaction(transaction);
|
|
1436
|
+
return { signature: result.signature };
|
|
1437
|
+
}
|
|
1438
|
+
async signAllTransactions(transactions) {
|
|
1439
|
+
if (!this.connected) {
|
|
1440
|
+
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
1441
|
+
}
|
|
1442
|
+
try {
|
|
1443
|
+
const result = await this.phantom.solana.signAllTransactions(transactions);
|
|
1444
|
+
return result;
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
return Promise.reject(error);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
async signAndSendAllTransactions(transactions) {
|
|
1450
|
+
if (!this.connected) {
|
|
1451
|
+
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
1452
|
+
}
|
|
1453
|
+
try {
|
|
1454
|
+
const result = await this.phantom.solana.signAndSendAllTransactions(transactions);
|
|
1455
|
+
return { signatures: result.signatures };
|
|
1456
|
+
} catch (error) {
|
|
1457
|
+
return Promise.reject(error);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
switchNetwork(_network) {
|
|
1461
|
+
return Promise.resolve();
|
|
1462
|
+
}
|
|
1463
|
+
// Legacy methods
|
|
1464
|
+
getPublicKey() {
|
|
1465
|
+
return Promise.resolve(this._publicKey);
|
|
1466
|
+
}
|
|
1467
|
+
isConnected() {
|
|
1468
|
+
return this.connected;
|
|
1469
|
+
}
|
|
1470
|
+
setupEventListeners() {
|
|
1471
|
+
this.phantom.solana.addEventListener("connect", (publicKey) => {
|
|
1472
|
+
this._publicKey = publicKey;
|
|
1473
|
+
this.eventEmitter.emit("connect", publicKey);
|
|
1474
|
+
});
|
|
1475
|
+
this.phantom.solana.addEventListener("disconnect", () => {
|
|
1476
|
+
this._publicKey = null;
|
|
1477
|
+
this.eventEmitter.emit("disconnect");
|
|
1478
|
+
});
|
|
1479
|
+
this.phantom.solana.addEventListener("accountChanged", (publicKey) => {
|
|
1480
|
+
this._publicKey = publicKey;
|
|
1481
|
+
this.eventEmitter.emit("accountChanged", publicKey);
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
// Event methods for interface compliance
|
|
1485
|
+
on(event, listener) {
|
|
1486
|
+
this.eventEmitter.on(event, listener);
|
|
1487
|
+
}
|
|
1488
|
+
off(event, listener) {
|
|
1489
|
+
this.eventEmitter.off(event, listener);
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
|
|
1493
|
+
// src/providers/injected/chains/EthereumChain.ts
|
|
1494
|
+
import { EventEmitter as EventEmitter4 } from "eventemitter3";
|
|
1495
|
+
var PhantomEthereumChain = class {
|
|
1496
|
+
constructor(phantom) {
|
|
1497
|
+
this._chainId = "0x1";
|
|
1498
|
+
this._accounts = [];
|
|
1499
|
+
this.eventEmitter = new EventEmitter4();
|
|
1500
|
+
this.phantom = phantom;
|
|
1501
|
+
this.setupEventListeners();
|
|
1502
|
+
}
|
|
1503
|
+
// EIP-1193 compliant properties
|
|
1504
|
+
get connected() {
|
|
1505
|
+
return this._accounts.length > 0;
|
|
1506
|
+
}
|
|
1507
|
+
get chainId() {
|
|
1508
|
+
return this._chainId;
|
|
1509
|
+
}
|
|
1510
|
+
get accounts() {
|
|
1511
|
+
return this._accounts;
|
|
1512
|
+
}
|
|
1513
|
+
// EIP-1193 core method with eth_signTransaction support
|
|
1514
|
+
async request(args) {
|
|
1515
|
+
if (args.method === "eth_signTransaction") {
|
|
1516
|
+
const [transaction] = args.params;
|
|
1517
|
+
const result = await this.signTransaction(transaction);
|
|
1518
|
+
return result;
|
|
1519
|
+
}
|
|
1520
|
+
const phantomProvider = await this.phantom.ethereum.getProvider();
|
|
1521
|
+
return await phantomProvider.request(args);
|
|
1522
|
+
}
|
|
1523
|
+
// Connection methods
|
|
1524
|
+
async connect() {
|
|
1525
|
+
const accounts = await this.phantom.ethereum.getAccounts();
|
|
1526
|
+
this._accounts = accounts;
|
|
1527
|
+
return accounts;
|
|
1528
|
+
}
|
|
1529
|
+
async disconnect() {
|
|
1530
|
+
await this.phantom.ethereum.disconnect();
|
|
1531
|
+
this._accounts = [];
|
|
1532
|
+
}
|
|
1533
|
+
// Standard compliant methods (return raw values, not wrapped objects)
|
|
1534
|
+
async signPersonalMessage(message, address) {
|
|
1535
|
+
return await this.phantom.ethereum.signPersonalMessage(message, address);
|
|
274
1536
|
}
|
|
275
1537
|
async signTypedData(typedData, address) {
|
|
276
1538
|
return await this.phantom.ethereum.signTypedData(typedData, address);
|
|
@@ -282,8 +1544,9 @@ var InjectedEthereumChain = class {
|
|
|
282
1544
|
return await this.phantom.ethereum.sendTransaction(transaction);
|
|
283
1545
|
}
|
|
284
1546
|
async switchChain(chainId) {
|
|
285
|
-
|
|
286
|
-
this.
|
|
1547
|
+
const hexChainId = typeof chainId === "string" ? chainId.toLowerCase().startsWith("0x") ? chainId : `0x${parseInt(chainId, 10).toString(16)}` : `0x${chainId.toString(16)}`;
|
|
1548
|
+
await this.phantom.ethereum.switchChain(hexChainId);
|
|
1549
|
+
this._chainId = hexChainId;
|
|
287
1550
|
this.eventEmitter.emit("chainChanged", this._chainId);
|
|
288
1551
|
}
|
|
289
1552
|
async getChainId() {
|
|
@@ -294,16 +1557,16 @@ var InjectedEthereumChain = class {
|
|
|
294
1557
|
return await this.phantom.ethereum.getAccounts();
|
|
295
1558
|
}
|
|
296
1559
|
isConnected() {
|
|
297
|
-
return this.
|
|
1560
|
+
return this.connected;
|
|
298
1561
|
}
|
|
299
1562
|
setupEventListeners() {
|
|
300
1563
|
this.phantom.ethereum.addEventListener("connect", (accounts) => {
|
|
301
|
-
this.
|
|
1564
|
+
this._accounts = accounts;
|
|
302
1565
|
this.eventEmitter.emit("connect", { chainId: this._chainId });
|
|
303
1566
|
this.eventEmitter.emit("accountsChanged", accounts);
|
|
304
1567
|
});
|
|
305
1568
|
this.phantom.ethereum.addEventListener("disconnect", () => {
|
|
306
|
-
this.
|
|
1569
|
+
this._accounts = [];
|
|
307
1570
|
this.eventEmitter.emit("disconnect", { code: 4900, message: "Provider disconnected" });
|
|
308
1571
|
this.eventEmitter.emit("accountsChanged", []);
|
|
309
1572
|
});
|
|
@@ -315,254 +1578,532 @@ var InjectedEthereumChain = class {
|
|
|
315
1578
|
this._chainId = chainId;
|
|
316
1579
|
this.eventEmitter.emit("chainChanged", chainId);
|
|
317
1580
|
});
|
|
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);
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
this.callbacks.on("disconnect", () => {
|
|
325
|
-
this.updateConnectionState(false, []);
|
|
326
|
-
});
|
|
327
1581
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
1582
|
+
// Event methods for interface compliance
|
|
1583
|
+
on(event, listener) {
|
|
1584
|
+
this.eventEmitter.on(event, listener);
|
|
1585
|
+
}
|
|
1586
|
+
off(event, listener) {
|
|
1587
|
+
this.eventEmitter.off(event, listener);
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
// src/wallets/registry.ts
|
|
1592
|
+
function isPhantomWallet(wallet) {
|
|
1593
|
+
return wallet !== void 0 && wallet.id === "phantom" && "isPhantom" in wallet && wallet.isPhantom === true;
|
|
1594
|
+
}
|
|
1595
|
+
var InjectedWalletRegistry = class {
|
|
1596
|
+
constructor() {
|
|
1597
|
+
this.wallets = /* @__PURE__ */ new Map();
|
|
1598
|
+
this.discoveryPromise = null;
|
|
1599
|
+
}
|
|
1600
|
+
register(info) {
|
|
1601
|
+
const wrappedProviders = {};
|
|
1602
|
+
if (info.providers?.solana) {
|
|
1603
|
+
const isWalletStandard = info.providers.solana && typeof info.providers.solana === "object" && "features" in info.providers.solana && typeof info.providers.solana.features === "object";
|
|
1604
|
+
if (isWalletStandard) {
|
|
1605
|
+
wrappedProviders.solana = new WalletStandardSolanaAdapter(info.providers.solana, info.id, info.name);
|
|
1606
|
+
debug.log(DebugCategory.BROWSER_SDK, "Wrapped Wallet Standard Solana wallet with adapter", {
|
|
1607
|
+
walletId: info.id,
|
|
1608
|
+
walletName: info.name
|
|
1609
|
+
});
|
|
1610
|
+
} else {
|
|
1611
|
+
wrappedProviders.solana = new InjectedWalletSolanaChain(info.providers.solana, info.id, info.name);
|
|
1612
|
+
debug.log(DebugCategory.BROWSER_SDK, "Wrapped Solana provider with InjectedWalletSolanaChain", {
|
|
1613
|
+
walletId: info.id,
|
|
1614
|
+
walletName: info.name
|
|
1615
|
+
});
|
|
333
1616
|
}
|
|
334
1617
|
}
|
|
1618
|
+
if (info.providers?.ethereum) {
|
|
1619
|
+
wrappedProviders.ethereum = new InjectedWalletEthereumChain(info.providers.ethereum, info.id, info.name);
|
|
1620
|
+
debug.log(DebugCategory.BROWSER_SDK, "Wrapped Ethereum provider with InjectedWalletEthereumChain", {
|
|
1621
|
+
walletId: info.id,
|
|
1622
|
+
walletName: info.name
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
const wrappedInfo = {
|
|
1626
|
+
...info,
|
|
1627
|
+
providers: Object.keys(wrappedProviders).length > 0 ? wrappedProviders : info.providers
|
|
1628
|
+
};
|
|
1629
|
+
this.wallets.set(info.id, wrappedInfo);
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Register Phantom wallet with its instance
|
|
1633
|
+
* This creates wrapped providers and stores the Phantom instance for auto-confirm access
|
|
1634
|
+
*/
|
|
1635
|
+
registerPhantom(phantomInstance, addressTypes) {
|
|
1636
|
+
const wrappedProviders = {};
|
|
1637
|
+
if (addressTypes.includes(AddressType.solana) && phantomInstance.solana) {
|
|
1638
|
+
wrappedProviders.solana = new PhantomSolanaChain(phantomInstance);
|
|
1639
|
+
debug.log(DebugCategory.BROWSER_SDK, "Created PhantomSolanaChain wrapper", {
|
|
1640
|
+
walletId: "phantom"
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
if (addressTypes.includes(AddressType.ethereum) && phantomInstance.ethereum) {
|
|
1644
|
+
wrappedProviders.ethereum = new PhantomEthereumChain(phantomInstance);
|
|
1645
|
+
debug.log(DebugCategory.BROWSER_SDK, "Created PhantomEthereumChain wrapper", {
|
|
1646
|
+
walletId: "phantom"
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
const phantomWallet = {
|
|
1650
|
+
id: "phantom",
|
|
1651
|
+
name: "Phantom",
|
|
1652
|
+
icon: "",
|
|
1653
|
+
// Icon will be rendered from icons package in UI components
|
|
1654
|
+
addressTypes,
|
|
1655
|
+
providers: wrappedProviders,
|
|
1656
|
+
isPhantom: true,
|
|
1657
|
+
phantomInstance,
|
|
1658
|
+
discovery: "phantom"
|
|
1659
|
+
};
|
|
1660
|
+
this.wallets.set("phantom", phantomWallet);
|
|
1661
|
+
debug.log(DebugCategory.BROWSER_SDK, "Registered Phantom wallet with chain wrappers", {
|
|
1662
|
+
addressTypes,
|
|
1663
|
+
hasSolana: !!wrappedProviders.solana,
|
|
1664
|
+
hasEthereum: !!wrappedProviders.ethereum
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
unregister(id) {
|
|
1668
|
+
this.wallets.delete(id);
|
|
1669
|
+
}
|
|
1670
|
+
has(id) {
|
|
1671
|
+
return this.wallets.has(id);
|
|
335
1672
|
}
|
|
336
|
-
|
|
337
|
-
this.
|
|
338
|
-
this._accounts = accounts;
|
|
1673
|
+
getById(id) {
|
|
1674
|
+
return this.wallets.get(id);
|
|
339
1675
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
this.eventEmitter.on(event, listener);
|
|
1676
|
+
getAll() {
|
|
1677
|
+
return Array.from(this.wallets.values());
|
|
343
1678
|
}
|
|
344
|
-
|
|
345
|
-
|
|
1679
|
+
getByAddressTypes(addressTypes) {
|
|
1680
|
+
if (addressTypes.length === 0) {
|
|
1681
|
+
return this.getAll();
|
|
1682
|
+
}
|
|
1683
|
+
const allowed = new Set(addressTypes);
|
|
1684
|
+
return this.getAll().filter((wallet) => wallet.addressTypes.some((t) => allowed.has(t)));
|
|
1685
|
+
}
|
|
1686
|
+
discover(addressTypes) {
|
|
1687
|
+
if (this.discoveryPromise) {
|
|
1688
|
+
return this.discoveryPromise;
|
|
1689
|
+
}
|
|
1690
|
+
debug.log(DebugCategory.BROWSER_SDK, "Starting wallet discovery", { addressTypes });
|
|
1691
|
+
this.discoveryPromise = discoverWallets(addressTypes).then((discoveredWallets) => {
|
|
1692
|
+
const relevantWallets = addressTypes ? discoveredWallets.filter((wallet) => wallet.addressTypes.some((type) => addressTypes.includes(type))) : discoveredWallets;
|
|
1693
|
+
for (const wallet of relevantWallets) {
|
|
1694
|
+
if (wallet.id === "phantom" && isPhantomWallet(wallet)) {
|
|
1695
|
+
this.registerPhantom(wallet.phantomInstance, wallet.addressTypes);
|
|
1696
|
+
} else {
|
|
1697
|
+
this.register(wallet);
|
|
1698
|
+
}
|
|
1699
|
+
debug.log(DebugCategory.BROWSER_SDK, "Registered discovered wallet", {
|
|
1700
|
+
id: wallet.id,
|
|
1701
|
+
name: wallet.name,
|
|
1702
|
+
addressTypes: wallet.addressTypes
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
debug.info(DebugCategory.BROWSER_SDK, "Wallet discovery completed", {
|
|
1706
|
+
totalDiscovered: discoveredWallets.length,
|
|
1707
|
+
relevantWallets: relevantWallets.length
|
|
1708
|
+
});
|
|
1709
|
+
}).catch((error) => {
|
|
1710
|
+
debug.warn(DebugCategory.BROWSER_SDK, "Wallet discovery failed", { error });
|
|
1711
|
+
this.discoveryPromise = null;
|
|
1712
|
+
throw error;
|
|
1713
|
+
});
|
|
1714
|
+
return this.discoveryPromise;
|
|
346
1715
|
}
|
|
347
1716
|
};
|
|
1717
|
+
var walletRegistry = null;
|
|
1718
|
+
function getWalletRegistry() {
|
|
1719
|
+
if (!walletRegistry) {
|
|
1720
|
+
walletRegistry = new InjectedWalletRegistry();
|
|
1721
|
+
}
|
|
1722
|
+
return walletRegistry;
|
|
1723
|
+
}
|
|
348
1724
|
|
|
349
1725
|
// src/providers/injected/index.ts
|
|
1726
|
+
var WAS_CONNECTED_KEY = "phantom-injected-was-connected";
|
|
1727
|
+
var WAS_CONNECTED_VALUE = "true";
|
|
1728
|
+
var LAST_WALLET_ID_KEY = "phantom-injected-last-wallet-id";
|
|
350
1729
|
var InjectedProvider = class {
|
|
1730
|
+
// Track which wallets have event listeners set up
|
|
351
1731
|
constructor(config) {
|
|
352
|
-
this.
|
|
353
|
-
this.
|
|
1732
|
+
this.selectedWalletId = null;
|
|
1733
|
+
this.walletStates = /* @__PURE__ */ new Map();
|
|
354
1734
|
// Event management
|
|
355
1735
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
356
1736
|
this.browserInjectedCleanupFunctions = [];
|
|
357
1737
|
this.eventsInitialized = false;
|
|
1738
|
+
this.externalWalletEventListenersSetup = /* @__PURE__ */ new Set();
|
|
358
1739
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Initializing InjectedProvider", { config });
|
|
359
1740
|
this.addressTypes = config.addressTypes;
|
|
1741
|
+
this.walletRegistry = getWalletRegistry();
|
|
360
1742
|
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
|
|
1743
|
+
this.walletRegistry.discover(this.addressTypes).catch((error) => {
|
|
1744
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Wallet discovery failed during initialization", { error });
|
|
374
1745
|
});
|
|
375
|
-
this.phantom = createPhantom({ plugins });
|
|
376
|
-
const callbacks = this.createCallbacks();
|
|
377
|
-
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
378
|
-
this._solanaChain = new InjectedSolanaChain(this.phantom, callbacks);
|
|
379
|
-
}
|
|
380
|
-
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
381
|
-
this._ethereumChain = new InjectedEthereumChain(this.phantom, callbacks);
|
|
382
|
-
}
|
|
383
1746
|
debug.info(DebugCategory.INJECTED_PROVIDER, "InjectedProvider initialized");
|
|
384
1747
|
}
|
|
385
1748
|
/**
|
|
386
|
-
*
|
|
1749
|
+
* Wait for wallet discovery to complete if the wallet is not yet in the registry
|
|
1750
|
+
* This is needed for auto-connect when the last wallet was an external wallet
|
|
387
1751
|
*/
|
|
1752
|
+
async waitForWalletDiscovery(walletId) {
|
|
1753
|
+
if (this.walletRegistry.has(walletId)) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Wallet not found in registry, waiting for discovery", {
|
|
1757
|
+
walletId
|
|
1758
|
+
});
|
|
1759
|
+
try {
|
|
1760
|
+
await this.walletRegistry.discover(this.addressTypes);
|
|
1761
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Wallet discovery completed", { walletId });
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Wallet discovery failed", {
|
|
1764
|
+
walletId,
|
|
1765
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
388
1769
|
get solana() {
|
|
389
|
-
if (!this.addressTypes.includes(
|
|
1770
|
+
if (!this.addressTypes.includes(AddressType2.solana)) {
|
|
390
1771
|
throw new Error("Solana not enabled for this provider");
|
|
391
1772
|
}
|
|
392
|
-
|
|
393
|
-
|
|
1773
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
1774
|
+
const walletInfo = this.walletRegistry.getById(walletId);
|
|
1775
|
+
if (!walletInfo) {
|
|
1776
|
+
const registry = this.walletRegistry;
|
|
1777
|
+
if (registry.discoveryPromise) {
|
|
1778
|
+
throw new Error(
|
|
1779
|
+
`Wallet "${walletId}" not found. Wallet discovery is still in progress. Please wait for sdk.discoverWallets() to complete before accessing chain properties.`
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
throw new Error(
|
|
1783
|
+
`Wallet "${walletId}" not found. Please ensure wallet discovery has completed. Make sure you call sdk.discoverWallets() and await it before accessing chain properties.`
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
if (!walletInfo.providers?.solana) {
|
|
1787
|
+
throw new Error(
|
|
1788
|
+
`Selected wallet "${walletInfo.name}" does not support Solana. This wallet only supports: ${walletInfo.addressTypes.join(", ")}. Make sure your SDK config includes Solana in addressTypes.`
|
|
1789
|
+
);
|
|
394
1790
|
}
|
|
395
|
-
return
|
|
1791
|
+
return walletInfo.providers.solana;
|
|
396
1792
|
}
|
|
397
1793
|
/**
|
|
398
1794
|
* Access to Ethereum chain operations
|
|
399
1795
|
*/
|
|
400
1796
|
get ethereum() {
|
|
401
|
-
if (!this.addressTypes.includes(
|
|
1797
|
+
if (!this.addressTypes.includes(AddressType2.ethereum)) {
|
|
402
1798
|
throw new Error("Ethereum not enabled for this provider");
|
|
403
1799
|
}
|
|
404
|
-
|
|
405
|
-
|
|
1800
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
1801
|
+
const walletInfo = this.walletRegistry.getById(walletId);
|
|
1802
|
+
if (!walletInfo) {
|
|
1803
|
+
const registry = this.walletRegistry;
|
|
1804
|
+
if (registry.discoveryPromise) {
|
|
1805
|
+
throw new Error(
|
|
1806
|
+
`Wallet "${walletId}" not found. Wallet discovery is still in progress. Please wait for sdk.discoverWallets() to complete before accessing chain properties.`
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
throw new Error(
|
|
1810
|
+
`Wallet "${walletId}" not found. Please ensure wallet discovery has completed. Make sure you call sdk.discoverWallets() and await it before accessing chain properties.`
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1813
|
+
if (!walletInfo.providers?.ethereum) {
|
|
1814
|
+
throw new Error(
|
|
1815
|
+
`Selected wallet "${walletInfo.name}" does not support Ethereum. This wallet only supports: ${walletInfo.addressTypes.join(", ")}. Make sure your SDK config includes Ethereum in addressTypes.`
|
|
1816
|
+
);
|
|
406
1817
|
}
|
|
407
|
-
return
|
|
1818
|
+
return walletInfo.providers.ethereum;
|
|
408
1819
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
1820
|
+
validateAndSelectWallet(requestedWalletId) {
|
|
1821
|
+
if (!this.walletRegistry.has(requestedWalletId)) {
|
|
1822
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Unknown injected wallet id requested", {
|
|
1823
|
+
walletId: requestedWalletId
|
|
1824
|
+
});
|
|
1825
|
+
throw new Error(`Unknown injected wallet id: ${requestedWalletId}`);
|
|
1826
|
+
}
|
|
1827
|
+
const walletInfo = this.walletRegistry.getById(requestedWalletId);
|
|
1828
|
+
if (!walletInfo || !walletInfo.providers) {
|
|
1829
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Wallet not available for connection", {
|
|
1830
|
+
walletId: requestedWalletId
|
|
1831
|
+
});
|
|
1832
|
+
throw new Error(`Wallet not available for connection: ${requestedWalletId}`);
|
|
1833
|
+
}
|
|
1834
|
+
this.selectedWalletId = requestedWalletId;
|
|
1835
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Selected injected wallet for connection", {
|
|
1836
|
+
walletId: requestedWalletId
|
|
414
1837
|
});
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
1838
|
+
return walletInfo;
|
|
1839
|
+
}
|
|
1840
|
+
async connectToWallet(walletInfo, options) {
|
|
1841
|
+
if (!walletInfo.providers) {
|
|
1842
|
+
const error = new Error(`Wallet adapter not available for wallet: ${this.selectedWalletId}`);
|
|
1843
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Wallet adapter not available", { walletId: this.selectedWalletId });
|
|
1844
|
+
this.emit("connect_error", {
|
|
1845
|
+
error: error.message,
|
|
1846
|
+
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
1847
|
+
});
|
|
1848
|
+
throw error;
|
|
1849
|
+
}
|
|
1850
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Connecting via wallet", {
|
|
1851
|
+
walletId: this.selectedWalletId,
|
|
1852
|
+
walletName: walletInfo.name,
|
|
1853
|
+
options
|
|
418
1854
|
});
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
1855
|
+
if (!options?.skipEventListeners) {
|
|
1856
|
+
this.setupExternalWalletEvents(walletInfo);
|
|
1857
|
+
if (this.selectedWalletId === "phantom" && isPhantomWallet(walletInfo)) {
|
|
1858
|
+
this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
|
|
1859
|
+
this.browserInjectedCleanupFunctions = [];
|
|
1860
|
+
this.setupBrowserInjectedEvents();
|
|
1861
|
+
this.eventsInitialized = true;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
const connectedAddresses = [];
|
|
1865
|
+
if (this.addressTypes.includes(AddressType2.solana) && walletInfo.providers?.solana) {
|
|
1866
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection", {
|
|
1867
|
+
walletId: this.selectedWalletId,
|
|
1868
|
+
walletName: walletInfo.name,
|
|
1869
|
+
onlyIfTrusted: options?.onlyIfTrusted
|
|
1870
|
+
});
|
|
1871
|
+
try {
|
|
1872
|
+
const result = await walletInfo.providers.solana.connect(
|
|
1873
|
+
options?.onlyIfTrusted ? { onlyIfTrusted: true } : void 0
|
|
1874
|
+
);
|
|
1875
|
+
const address = result.publicKey;
|
|
1876
|
+
connectedAddresses.push({
|
|
1877
|
+
addressType: AddressType2.solana,
|
|
1878
|
+
address
|
|
1879
|
+
});
|
|
1880
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", {
|
|
1881
|
+
address,
|
|
1882
|
+
walletId: this.selectedWalletId,
|
|
1883
|
+
walletName: walletInfo.name
|
|
1884
|
+
});
|
|
1885
|
+
} catch (err) {
|
|
1886
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana, stopping", {
|
|
1887
|
+
error: err,
|
|
1888
|
+
walletId: this.selectedWalletId,
|
|
1889
|
+
walletName: walletInfo.name
|
|
1890
|
+
});
|
|
423
1891
|
this.emit("connect_error", {
|
|
424
|
-
error:
|
|
425
|
-
source: "manual-connect"
|
|
1892
|
+
error: err instanceof Error ? err.message : "Failed to connect",
|
|
1893
|
+
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
426
1894
|
});
|
|
427
|
-
throw
|
|
1895
|
+
throw err;
|
|
428
1896
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
} catch (err) {
|
|
443
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
1897
|
+
}
|
|
1898
|
+
if (this.addressTypes.includes(AddressType2.ethereum) && walletInfo.providers?.ethereum) {
|
|
1899
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Ethereum connection", {
|
|
1900
|
+
walletId: this.selectedWalletId,
|
|
1901
|
+
walletName: walletInfo.name,
|
|
1902
|
+
silent: options?.silent
|
|
1903
|
+
});
|
|
1904
|
+
try {
|
|
1905
|
+
let accounts;
|
|
1906
|
+
if (options?.silent) {
|
|
1907
|
+
accounts = await walletInfo.providers.ethereum.request({ method: "eth_accounts" });
|
|
1908
|
+
} else {
|
|
1909
|
+
accounts = await walletInfo.providers.ethereum.connect();
|
|
444
1910
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
} catch (err) {
|
|
459
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
1911
|
+
if (accounts.length > 0) {
|
|
1912
|
+
connectedAddresses.push(
|
|
1913
|
+
...accounts.map((address) => ({
|
|
1914
|
+
addressType: AddressType2.ethereum,
|
|
1915
|
+
address
|
|
1916
|
+
}))
|
|
1917
|
+
);
|
|
1918
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum connected successfully", {
|
|
1919
|
+
addresses: accounts,
|
|
1920
|
+
walletId: this.selectedWalletId,
|
|
1921
|
+
walletName: walletInfo.name
|
|
1922
|
+
});
|
|
460
1923
|
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
1924
|
+
} catch (err) {
|
|
1925
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum, stopping", {
|
|
1926
|
+
error: err,
|
|
1927
|
+
walletId: this.selectedWalletId,
|
|
1928
|
+
walletName: walletInfo.name
|
|
1929
|
+
});
|
|
464
1930
|
this.emit("connect_error", {
|
|
465
|
-
error:
|
|
466
|
-
source: "manual-connect"
|
|
1931
|
+
error: err instanceof Error ? err.message : "Failed to connect",
|
|
1932
|
+
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
467
1933
|
});
|
|
468
|
-
throw
|
|
1934
|
+
throw err;
|
|
469
1935
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
addresses: this.addresses,
|
|
1936
|
+
}
|
|
1937
|
+
return connectedAddresses;
|
|
1938
|
+
}
|
|
1939
|
+
async finalizeConnection(connectedAddresses, authProvider, walletId) {
|
|
1940
|
+
if (connectedAddresses.length === 0) {
|
|
1941
|
+
const error = new Error("Failed to connect to any supported wallet provider");
|
|
1942
|
+
this.emit("connect_error", {
|
|
1943
|
+
error: error.message,
|
|
479
1944
|
source: "manual-connect"
|
|
480
1945
|
});
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
1946
|
+
throw error;
|
|
1947
|
+
}
|
|
1948
|
+
if (this.selectedWalletId) {
|
|
1949
|
+
this.setWalletState(this.selectedWalletId, {
|
|
1950
|
+
connected: true,
|
|
1951
|
+
addresses: connectedAddresses
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Finalized connection with addresses", {
|
|
1955
|
+
addressCount: connectedAddresses.length,
|
|
1956
|
+
addresses: connectedAddresses.map((addr) => ({
|
|
1957
|
+
type: addr.addressType,
|
|
1958
|
+
address: addr.address.substring(0, 10) + "..."
|
|
1959
|
+
}))
|
|
1960
|
+
});
|
|
1961
|
+
const authUserId = await this.getAuthUserId("manual-connect");
|
|
1962
|
+
try {
|
|
1963
|
+
localStorage.setItem(WAS_CONNECTED_KEY, WAS_CONNECTED_VALUE);
|
|
1964
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Set was-connected flag - auto-reconnect enabled");
|
|
1965
|
+
if (this.selectedWalletId) {
|
|
1966
|
+
localStorage.setItem(LAST_WALLET_ID_KEY, this.selectedWalletId);
|
|
1967
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Stored last injected wallet id", {
|
|
1968
|
+
walletId: this.selectedWalletId
|
|
487
1969
|
});
|
|
488
1970
|
}
|
|
1971
|
+
} catch (error) {
|
|
1972
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to persist injected provider state", { error });
|
|
1973
|
+
}
|
|
1974
|
+
const walletInfo = walletId ? this.walletRegistry.getById(walletId) : void 0;
|
|
1975
|
+
const wallet = walletInfo ? {
|
|
1976
|
+
id: walletInfo.id,
|
|
1977
|
+
name: walletInfo.name,
|
|
1978
|
+
icon: walletInfo.icon,
|
|
1979
|
+
addressTypes: walletInfo.addressTypes,
|
|
1980
|
+
rdns: walletInfo.rdns,
|
|
1981
|
+
discovery: walletInfo.discovery
|
|
1982
|
+
} : void 0;
|
|
1983
|
+
const result = {
|
|
1984
|
+
addresses: connectedAddresses,
|
|
1985
|
+
status: "completed",
|
|
1986
|
+
authUserId,
|
|
1987
|
+
authProvider,
|
|
1988
|
+
walletId,
|
|
1989
|
+
wallet
|
|
1990
|
+
};
|
|
1991
|
+
this.emit("connect", {
|
|
1992
|
+
addresses: connectedAddresses,
|
|
1993
|
+
source: "manual-connect",
|
|
1994
|
+
authUserId
|
|
1995
|
+
});
|
|
1996
|
+
return result;
|
|
1997
|
+
}
|
|
1998
|
+
async connect(authOptions) {
|
|
1999
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider connect", {
|
|
2000
|
+
addressTypes: this.addressTypes,
|
|
2001
|
+
provider: authOptions.provider
|
|
2002
|
+
});
|
|
2003
|
+
if (authOptions.provider !== "injected") {
|
|
2004
|
+
throw new Error(`Invalid provider for injected connection: ${authOptions.provider}. Must be "injected"`);
|
|
2005
|
+
}
|
|
2006
|
+
this.emit("connect_start", {
|
|
2007
|
+
source: "manual-connect",
|
|
2008
|
+
providerType: "injected"
|
|
2009
|
+
});
|
|
2010
|
+
try {
|
|
2011
|
+
const requestedWalletId = authOptions.walletId || "phantom";
|
|
2012
|
+
const walletInfo = this.validateAndSelectWallet(requestedWalletId);
|
|
2013
|
+
const connectedAddresses = await this.connectToWallet(walletInfo);
|
|
2014
|
+
return await this.finalizeConnection(connectedAddresses, "injected", this.selectedWalletId || void 0);
|
|
2015
|
+
} catch (error) {
|
|
2016
|
+
this.emit("connect_error", {
|
|
2017
|
+
error: error instanceof Error ? error.message : "Failed to connect",
|
|
2018
|
+
source: "manual-connect"
|
|
2019
|
+
});
|
|
489
2020
|
throw error;
|
|
490
2021
|
}
|
|
491
2022
|
}
|
|
492
2023
|
async disconnect() {
|
|
493
2024
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider disconnect");
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
2025
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2026
|
+
if (walletInfo?.providers) {
|
|
2027
|
+
if (this.addressTypes.includes(AddressType2.solana) && walletInfo.providers.solana) {
|
|
2028
|
+
try {
|
|
2029
|
+
await walletInfo.providers.solana.disconnect();
|
|
2030
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnected successfully");
|
|
2031
|
+
} catch (err) {
|
|
2032
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Solana", { error: err });
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
if (this.addressTypes.includes(AddressType2.ethereum) && walletInfo.providers.ethereum) {
|
|
2036
|
+
try {
|
|
2037
|
+
await walletInfo.providers.ethereum.disconnect();
|
|
2038
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected successfully");
|
|
2039
|
+
} catch (err) {
|
|
2040
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Ethereum", { error: err });
|
|
2041
|
+
}
|
|
500
2042
|
}
|
|
501
|
-
}
|
|
502
|
-
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
503
|
-
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected (no-op)");
|
|
504
2043
|
}
|
|
505
2044
|
this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
|
|
506
2045
|
this.browserInjectedCleanupFunctions = [];
|
|
507
|
-
this.
|
|
508
|
-
|
|
2046
|
+
if (this.selectedWalletId) {
|
|
2047
|
+
this.externalWalletEventListenersSetup.delete(this.selectedWalletId);
|
|
2048
|
+
this.setWalletState(this.selectedWalletId, {
|
|
2049
|
+
connected: false,
|
|
2050
|
+
addresses: []
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
try {
|
|
2054
|
+
localStorage.removeItem(WAS_CONNECTED_KEY);
|
|
2055
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Cleared was connected flag to prevent auto-reconnect");
|
|
2056
|
+
} catch (error) {
|
|
2057
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to clear was-connected flag", { error });
|
|
2058
|
+
}
|
|
509
2059
|
this.emit("disconnect", {
|
|
510
2060
|
source: "manual-disconnect"
|
|
511
2061
|
});
|
|
512
2062
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Injected provider disconnected successfully");
|
|
513
2063
|
}
|
|
514
2064
|
/**
|
|
515
|
-
* Attempt auto-connection
|
|
516
|
-
*
|
|
2065
|
+
* Attempt auto-connection if user was previously connected
|
|
2066
|
+
* Only reconnects if the user connected before and didn't explicitly disconnect
|
|
517
2067
|
* Should be called after setting up event listeners
|
|
518
2068
|
*/
|
|
519
2069
|
async autoConnect() {
|
|
520
|
-
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting auto-connect
|
|
2070
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting auto-connect");
|
|
2071
|
+
let lastWalletId = null;
|
|
2072
|
+
try {
|
|
2073
|
+
const wasConnected = localStorage.getItem(WAS_CONNECTED_KEY);
|
|
2074
|
+
if (wasConnected !== WAS_CONNECTED_VALUE) {
|
|
2075
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Skipping auto-connect: user was not previously connected");
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
lastWalletId = localStorage.getItem(LAST_WALLET_ID_KEY);
|
|
2079
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "User was previously connected, attempting auto-connect", {
|
|
2080
|
+
lastWalletId: lastWalletId || "phantom"
|
|
2081
|
+
});
|
|
2082
|
+
} catch (error) {
|
|
2083
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to check was-connected flag", { error });
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
521
2086
|
this.emit("connect_start", {
|
|
522
2087
|
source: "auto-connect",
|
|
523
2088
|
providerType: "injected"
|
|
524
2089
|
});
|
|
525
2090
|
try {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
2091
|
+
const walletId = lastWalletId || "phantom";
|
|
2092
|
+
await this.waitForWalletDiscovery(walletId);
|
|
2093
|
+
const walletInfo = this.validateAndSelectWallet(walletId);
|
|
2094
|
+
let connectedAddresses = [];
|
|
2095
|
+
try {
|
|
2096
|
+
connectedAddresses = await this.connectToWallet(walletInfo, {
|
|
2097
|
+
onlyIfTrusted: true,
|
|
2098
|
+
silent: true,
|
|
2099
|
+
skipEventListeners: true
|
|
2100
|
+
// Set up listeners only if connection succeeds
|
|
2101
|
+
});
|
|
2102
|
+
} catch (err) {
|
|
2103
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed (expected if not trusted)", {
|
|
2104
|
+
error: err,
|
|
2105
|
+
walletId: this.selectedWalletId
|
|
531
2106
|
});
|
|
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
2107
|
}
|
|
567
2108
|
if (connectedAddresses.length === 0) {
|
|
568
2109
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed: no trusted connections available");
|
|
@@ -572,15 +2113,33 @@ var InjectedProvider = class {
|
|
|
572
2113
|
});
|
|
573
2114
|
return;
|
|
574
2115
|
}
|
|
575
|
-
this.
|
|
576
|
-
this.
|
|
2116
|
+
this.setupExternalWalletEvents(walletInfo);
|
|
2117
|
+
if (this.selectedWalletId === "phantom" && isPhantomWallet(walletInfo)) {
|
|
2118
|
+
this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
|
|
2119
|
+
this.browserInjectedCleanupFunctions = [];
|
|
2120
|
+
this.setupBrowserInjectedEvents();
|
|
2121
|
+
this.eventsInitialized = true;
|
|
2122
|
+
}
|
|
2123
|
+
if (this.selectedWalletId) {
|
|
2124
|
+
this.setWalletState(this.selectedWalletId, {
|
|
2125
|
+
connected: true,
|
|
2126
|
+
addresses: connectedAddresses
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
const authUserId = await this.getAuthUserId("auto-connect");
|
|
577
2130
|
this.emit("connect", {
|
|
578
|
-
addresses:
|
|
579
|
-
source: "auto-connect"
|
|
2131
|
+
addresses: connectedAddresses,
|
|
2132
|
+
source: "auto-connect",
|
|
2133
|
+
authUserId
|
|
580
2134
|
});
|
|
581
2135
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Auto-connect successful", {
|
|
582
2136
|
addressCount: connectedAddresses.length,
|
|
583
|
-
addresses: connectedAddresses.map((addr) => ({
|
|
2137
|
+
addresses: connectedAddresses.map((addr) => ({
|
|
2138
|
+
type: addr.addressType,
|
|
2139
|
+
address: addr.address.substring(0, 8) + "..."
|
|
2140
|
+
})),
|
|
2141
|
+
walletId: this.selectedWalletId,
|
|
2142
|
+
authUserId
|
|
584
2143
|
});
|
|
585
2144
|
} catch (error) {
|
|
586
2145
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed with error", {
|
|
@@ -592,28 +2151,103 @@ var InjectedProvider = class {
|
|
|
592
2151
|
});
|
|
593
2152
|
}
|
|
594
2153
|
}
|
|
2154
|
+
getWalletState(walletId) {
|
|
2155
|
+
if (!this.walletStates.has(walletId)) {
|
|
2156
|
+
this.walletStates.set(walletId, { connected: false, addresses: [] });
|
|
2157
|
+
}
|
|
2158
|
+
return this.walletStates.get(walletId);
|
|
2159
|
+
}
|
|
2160
|
+
setWalletState(walletId, state) {
|
|
2161
|
+
this.walletStates.set(walletId, state);
|
|
2162
|
+
}
|
|
595
2163
|
getAddresses() {
|
|
596
|
-
|
|
2164
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2165
|
+
return this.getWalletState(walletId).addresses;
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* Get enabled address types for the current selected wallet
|
|
2169
|
+
* - For Phantom: returns config.addressTypes
|
|
2170
|
+
* - For external wallets: returns the wallet's addressTypes from registry
|
|
2171
|
+
*/
|
|
2172
|
+
getEnabledAddressTypes() {
|
|
2173
|
+
if (!this.selectedWalletId || this.selectedWalletId === "phantom") {
|
|
2174
|
+
return this.addressTypes;
|
|
2175
|
+
}
|
|
2176
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId);
|
|
2177
|
+
if (walletInfo) {
|
|
2178
|
+
return walletInfo.addressTypes;
|
|
2179
|
+
}
|
|
2180
|
+
return this.addressTypes;
|
|
597
2181
|
}
|
|
598
2182
|
isConnected() {
|
|
599
|
-
|
|
2183
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2184
|
+
return this.getWalletState(walletId).connected;
|
|
600
2185
|
}
|
|
601
|
-
// AutoConfirm methods - only available for
|
|
2186
|
+
// AutoConfirm methods - only available for Phantom wallet
|
|
602
2187
|
async enableAutoConfirm(params) {
|
|
2188
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2189
|
+
if (!isPhantomWallet(walletInfo)) {
|
|
2190
|
+
throw new Error("Auto-confirm is only available for Phantom wallet");
|
|
2191
|
+
}
|
|
603
2192
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Enabling autoConfirm", { params });
|
|
604
|
-
return await
|
|
2193
|
+
return await walletInfo.phantomInstance.autoConfirm.autoConfirmEnable(params);
|
|
605
2194
|
}
|
|
606
2195
|
async disableAutoConfirm() {
|
|
2196
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2197
|
+
if (!isPhantomWallet(walletInfo)) {
|
|
2198
|
+
throw new Error("Auto-confirm is only available for Phantom wallet");
|
|
2199
|
+
}
|
|
607
2200
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Disabling autoConfirm");
|
|
608
|
-
await
|
|
2201
|
+
await walletInfo.phantomInstance.autoConfirm.autoConfirmDisable();
|
|
609
2202
|
}
|
|
610
2203
|
async getAutoConfirmStatus() {
|
|
2204
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2205
|
+
if (!isPhantomWallet(walletInfo)) {
|
|
2206
|
+
throw new Error("Auto-confirm is only available for Phantom wallet");
|
|
2207
|
+
}
|
|
611
2208
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Getting autoConfirm status");
|
|
612
|
-
return await
|
|
2209
|
+
return await walletInfo.phantomInstance.autoConfirm.autoConfirmStatus();
|
|
613
2210
|
}
|
|
614
2211
|
async getSupportedAutoConfirmChains() {
|
|
2212
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2213
|
+
if (!isPhantomWallet(walletInfo)) {
|
|
2214
|
+
throw new Error("Auto-confirm is only available for Phantom wallet");
|
|
2215
|
+
}
|
|
615
2216
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Getting supported autoConfirm chains");
|
|
616
|
-
return await
|
|
2217
|
+
return await walletInfo.phantomInstance.autoConfirm.autoConfirmSupportedChains();
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Helper method to get authUserId from window.phantom.app.getUser()
|
|
2221
|
+
* Returns undefined if the method is not available or fails, or if wallet is not Phantom
|
|
2222
|
+
*/
|
|
2223
|
+
async getAuthUserId(context) {
|
|
2224
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2225
|
+
if (!isPhantomWallet(walletInfo)) {
|
|
2226
|
+
return void 0;
|
|
2227
|
+
}
|
|
2228
|
+
try {
|
|
2229
|
+
if (window.phantom?.app?.getUser) {
|
|
2230
|
+
const userInfo = await window.phantom.app.getUser();
|
|
2231
|
+
const authUserId = userInfo?.authUserId;
|
|
2232
|
+
if (authUserId) {
|
|
2233
|
+
debug.log(
|
|
2234
|
+
DebugCategory.INJECTED_PROVIDER,
|
|
2235
|
+
`Retrieved authUserId from window.phantom.app.getUser() during ${context}`,
|
|
2236
|
+
{
|
|
2237
|
+
authUserId
|
|
2238
|
+
}
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
return authUserId;
|
|
2242
|
+
}
|
|
2243
|
+
} catch (error) {
|
|
2244
|
+
debug.log(
|
|
2245
|
+
DebugCategory.INJECTED_PROVIDER,
|
|
2246
|
+
`Failed to get user info during ${context} (method may not be supported)`,
|
|
2247
|
+
{ error }
|
|
2248
|
+
);
|
|
2249
|
+
}
|
|
2250
|
+
return void 0;
|
|
617
2251
|
}
|
|
618
2252
|
// Event management methods - implementing unified event interface
|
|
619
2253
|
on(event, callback) {
|
|
@@ -654,132 +2288,245 @@ var InjectedProvider = class {
|
|
|
654
2288
|
}
|
|
655
2289
|
}
|
|
656
2290
|
setupBrowserInjectedEvents() {
|
|
657
|
-
|
|
658
|
-
if (
|
|
659
|
-
|
|
2291
|
+
const walletInfo = this.walletRegistry.getById(this.selectedWalletId || "phantom");
|
|
2292
|
+
if (!isPhantomWallet(walletInfo)) {
|
|
2293
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Skipping browser-injected-sdk event setup - not Phantom wallet");
|
|
2294
|
+
return;
|
|
660
2295
|
}
|
|
661
|
-
|
|
662
|
-
|
|
2296
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up browser-injected-sdk event listeners");
|
|
2297
|
+
if (this.selectedWalletId === "phantom" && isPhantomWallet(walletInfo)) {
|
|
2298
|
+
if (this.addressTypes.includes(AddressType2.solana)) {
|
|
2299
|
+
this.setupSolanaEvents(walletInfo.phantomInstance);
|
|
2300
|
+
}
|
|
2301
|
+
if (this.addressTypes.includes(AddressType2.ethereum)) {
|
|
2302
|
+
this.setupEthereumEvents(walletInfo.phantomInstance);
|
|
2303
|
+
}
|
|
663
2304
|
}
|
|
664
2305
|
}
|
|
665
|
-
setupSolanaEvents() {
|
|
2306
|
+
setupSolanaEvents(phantom) {
|
|
666
2307
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Solana event listeners");
|
|
667
|
-
const handleSolanaConnect = (publicKey) => {
|
|
2308
|
+
const handleSolanaConnect = async (publicKey) => {
|
|
668
2309
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana connect event received", { publicKey });
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
2310
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2311
|
+
const state = this.getWalletState(walletId);
|
|
2312
|
+
const solanaAddress = { addressType: AddressType2.solana, address: publicKey };
|
|
2313
|
+
const hasSolana = state.addresses.some((addr) => addr.addressType === AddressType2.solana);
|
|
2314
|
+
const newAddresses = hasSolana ? state.addresses.map((addr) => addr.addressType === AddressType2.solana ? solanaAddress : addr) : [...state.addresses, solanaAddress];
|
|
2315
|
+
this.setWalletState(walletId, {
|
|
2316
|
+
connected: true,
|
|
2317
|
+
addresses: newAddresses
|
|
2318
|
+
});
|
|
2319
|
+
const authUserId = await this.getAuthUserId("Solana connect event");
|
|
674
2320
|
this.emit("connect", {
|
|
675
|
-
addresses:
|
|
676
|
-
source: "injected-extension"
|
|
2321
|
+
addresses: newAddresses,
|
|
2322
|
+
source: "injected-extension",
|
|
2323
|
+
authUserId
|
|
677
2324
|
});
|
|
678
2325
|
};
|
|
679
2326
|
const handleSolanaDisconnect = () => {
|
|
680
2327
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnect event received");
|
|
681
|
-
|
|
682
|
-
|
|
2328
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2329
|
+
const state = this.getWalletState(walletId);
|
|
2330
|
+
const filteredAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.solana);
|
|
2331
|
+
this.setWalletState(walletId, {
|
|
2332
|
+
connected: filteredAddresses.length > 0,
|
|
2333
|
+
addresses: filteredAddresses
|
|
2334
|
+
});
|
|
683
2335
|
this.emit("disconnect", {
|
|
684
2336
|
source: "injected-extension"
|
|
685
2337
|
});
|
|
686
2338
|
};
|
|
687
|
-
const handleSolanaAccountChanged = (publicKey) => {
|
|
2339
|
+
const handleSolanaAccountChanged = async (publicKey) => {
|
|
688
2340
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana account changed event received", { publicKey });
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}
|
|
2341
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2342
|
+
const state = this.getWalletState(walletId);
|
|
2343
|
+
const solanaIndex = state.addresses.findIndex((addr) => addr.addressType === AddressType2.solana);
|
|
2344
|
+
const newAddresses = solanaIndex >= 0 ? state.addresses.map(
|
|
2345
|
+
(addr, idx) => idx === solanaIndex ? { addressType: AddressType2.solana, address: publicKey } : addr
|
|
2346
|
+
) : [...state.addresses, { addressType: AddressType2.solana, address: publicKey }];
|
|
2347
|
+
this.setWalletState(walletId, {
|
|
2348
|
+
connected: true,
|
|
2349
|
+
addresses: newAddresses
|
|
2350
|
+
});
|
|
2351
|
+
const authUserId = await this.getAuthUserId("Solana account changed event");
|
|
695
2352
|
this.emit("connect", {
|
|
696
|
-
addresses:
|
|
697
|
-
source: "injected-extension-account-change"
|
|
2353
|
+
addresses: newAddresses,
|
|
2354
|
+
source: "injected-extension-account-change",
|
|
2355
|
+
authUserId
|
|
698
2356
|
});
|
|
699
2357
|
};
|
|
700
|
-
const cleanupConnect =
|
|
701
|
-
const cleanupDisconnect =
|
|
702
|
-
const cleanupAccountChanged =
|
|
2358
|
+
const cleanupConnect = phantom.solana.addEventListener("connect", handleSolanaConnect);
|
|
2359
|
+
const cleanupDisconnect = phantom.solana.addEventListener("disconnect", handleSolanaDisconnect);
|
|
2360
|
+
const cleanupAccountChanged = phantom.solana.addEventListener("accountChanged", handleSolanaAccountChanged);
|
|
703
2361
|
this.browserInjectedCleanupFunctions.push(cleanupConnect, cleanupDisconnect, cleanupAccountChanged);
|
|
704
2362
|
}
|
|
705
|
-
setupEthereumEvents() {
|
|
2363
|
+
setupEthereumEvents(phantom) {
|
|
706
2364
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Ethereum event listeners");
|
|
707
|
-
const handleEthereumConnect = (accounts) => {
|
|
2365
|
+
const handleEthereumConnect = async (accounts) => {
|
|
708
2366
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum connect event received", { accounts });
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
|
|
2367
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2368
|
+
const state = this.getWalletState(walletId);
|
|
2369
|
+
const ethAddresses = accounts.map((address) => ({ addressType: AddressType2.ethereum, address }));
|
|
2370
|
+
const otherAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.ethereum);
|
|
2371
|
+
const newAddresses = [...otherAddresses, ...ethAddresses];
|
|
2372
|
+
this.setWalletState(walletId, {
|
|
2373
|
+
connected: true,
|
|
2374
|
+
addresses: newAddresses
|
|
2375
|
+
});
|
|
2376
|
+
const authUserId = await this.getAuthUserId("Ethereum connect event");
|
|
719
2377
|
this.emit("connect", {
|
|
720
|
-
addresses:
|
|
721
|
-
source: "injected-extension"
|
|
2378
|
+
addresses: newAddresses,
|
|
2379
|
+
source: "injected-extension",
|
|
2380
|
+
authUserId
|
|
722
2381
|
});
|
|
723
2382
|
};
|
|
724
2383
|
const handleEthereumDisconnect = () => {
|
|
725
2384
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnect event received");
|
|
726
|
-
|
|
727
|
-
|
|
2385
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2386
|
+
const state = this.getWalletState(walletId);
|
|
2387
|
+
const filteredAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.ethereum);
|
|
2388
|
+
this.setWalletState(walletId, {
|
|
2389
|
+
connected: filteredAddresses.length > 0,
|
|
2390
|
+
addresses: filteredAddresses
|
|
2391
|
+
});
|
|
728
2392
|
this.emit("disconnect", {
|
|
729
2393
|
source: "injected-extension"
|
|
730
2394
|
});
|
|
731
2395
|
};
|
|
732
|
-
const handleEthereumAccountsChanged = (accounts) => {
|
|
2396
|
+
const handleEthereumAccountsChanged = async (accounts) => {
|
|
733
2397
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum accounts changed event received", { accounts });
|
|
734
|
-
|
|
2398
|
+
const walletId = this.selectedWalletId || "phantom";
|
|
2399
|
+
const state = this.getWalletState(walletId);
|
|
2400
|
+
const otherAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.ethereum);
|
|
735
2401
|
if (accounts && accounts.length > 0) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
);
|
|
2402
|
+
const ethAddresses = accounts.map((address) => ({ addressType: AddressType2.ethereum, address }));
|
|
2403
|
+
const newAddresses = [...otherAddresses, ...ethAddresses];
|
|
2404
|
+
this.setWalletState(walletId, {
|
|
2405
|
+
connected: true,
|
|
2406
|
+
addresses: newAddresses
|
|
2407
|
+
});
|
|
2408
|
+
const authUserId = await this.getAuthUserId("Ethereum accounts changed event");
|
|
742
2409
|
this.emit("connect", {
|
|
743
|
-
addresses:
|
|
744
|
-
source: "injected-extension-account-change"
|
|
2410
|
+
addresses: newAddresses,
|
|
2411
|
+
source: "injected-extension-account-change",
|
|
2412
|
+
authUserId
|
|
745
2413
|
});
|
|
746
2414
|
} else {
|
|
747
|
-
this.
|
|
2415
|
+
this.setWalletState(walletId, {
|
|
2416
|
+
connected: otherAddresses.length > 0,
|
|
2417
|
+
addresses: otherAddresses
|
|
2418
|
+
});
|
|
748
2419
|
this.emit("disconnect", {
|
|
749
2420
|
source: "injected-extension-account-change"
|
|
750
2421
|
});
|
|
751
2422
|
}
|
|
752
2423
|
};
|
|
753
|
-
const cleanupConnect =
|
|
754
|
-
const cleanupDisconnect =
|
|
755
|
-
const cleanupAccountsChanged =
|
|
756
|
-
"accountsChanged",
|
|
757
|
-
handleEthereumAccountsChanged
|
|
758
|
-
);
|
|
2424
|
+
const cleanupConnect = phantom.ethereum.addEventListener("connect", handleEthereumConnect);
|
|
2425
|
+
const cleanupDisconnect = phantom.ethereum.addEventListener("disconnect", handleEthereumDisconnect);
|
|
2426
|
+
const cleanupAccountsChanged = phantom.ethereum.addEventListener("accountsChanged", handleEthereumAccountsChanged);
|
|
759
2427
|
this.browserInjectedCleanupFunctions.push(cleanupConnect, cleanupDisconnect, cleanupAccountsChanged);
|
|
760
2428
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
this.
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
2429
|
+
setupExternalWalletEvents(walletInfo) {
|
|
2430
|
+
if (isPhantomWallet(walletInfo)) {
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
if (!this.selectedWalletId || this.externalWalletEventListenersSetup.has(this.selectedWalletId)) {
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2436
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up external wallet event listeners", {
|
|
2437
|
+
walletId: this.selectedWalletId
|
|
2438
|
+
});
|
|
2439
|
+
if (walletInfo.providers?.ethereum) {
|
|
2440
|
+
const handleExternalEthereumAccountsChanged = async (accounts) => {
|
|
2441
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Ethereum accounts changed event received", {
|
|
2442
|
+
walletId: this.selectedWalletId,
|
|
2443
|
+
accounts
|
|
2444
|
+
});
|
|
2445
|
+
const walletId = this.selectedWalletId;
|
|
2446
|
+
const state = this.getWalletState(walletId);
|
|
2447
|
+
const otherAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.ethereum);
|
|
2448
|
+
if (accounts && accounts.length > 0) {
|
|
2449
|
+
const ethAddresses = accounts.map((address) => ({ addressType: AddressType2.ethereum, address }));
|
|
2450
|
+
const newAddresses = [...otherAddresses, ...ethAddresses];
|
|
2451
|
+
this.setWalletState(walletId, {
|
|
2452
|
+
connected: true,
|
|
2453
|
+
addresses: newAddresses
|
|
2454
|
+
});
|
|
2455
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Updated Ethereum addresses after account change", {
|
|
2456
|
+
walletId,
|
|
2457
|
+
oldCount: 0,
|
|
2458
|
+
// We filtered them out
|
|
2459
|
+
newCount: accounts.length,
|
|
2460
|
+
addresses: newAddresses.filter((addr) => addr.addressType === AddressType2.ethereum)
|
|
2461
|
+
});
|
|
2462
|
+
const authUserId = await this.getAuthUserId("External wallet Ethereum accounts changed event");
|
|
2463
|
+
this.emit("connect", {
|
|
2464
|
+
addresses: newAddresses,
|
|
2465
|
+
source: "external-wallet-account-change",
|
|
2466
|
+
authUserId
|
|
2467
|
+
});
|
|
2468
|
+
} else {
|
|
2469
|
+
this.setWalletState(walletId, {
|
|
2470
|
+
connected: otherAddresses.length > 0,
|
|
2471
|
+
addresses: otherAddresses
|
|
2472
|
+
});
|
|
2473
|
+
this.emit("disconnect", {
|
|
2474
|
+
source: "external-wallet-account-change"
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
if (typeof walletInfo.providers.ethereum.on === "function") {
|
|
2479
|
+
walletInfo.providers.ethereum.on("accountsChanged", handleExternalEthereumAccountsChanged);
|
|
2480
|
+
this.browserInjectedCleanupFunctions.push(() => {
|
|
2481
|
+
if (typeof walletInfo.providers?.ethereum?.off === "function") {
|
|
2482
|
+
walletInfo.providers.ethereum.off("accountsChanged", handleExternalEthereumAccountsChanged);
|
|
2483
|
+
}
|
|
2484
|
+
});
|
|
781
2485
|
}
|
|
782
|
-
}
|
|
2486
|
+
}
|
|
2487
|
+
if (walletInfo.providers?.solana) {
|
|
2488
|
+
const handleExternalSolanaAccountChanged = async (publicKey) => {
|
|
2489
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "External wallet Solana account changed event received", {
|
|
2490
|
+
walletId: this.selectedWalletId,
|
|
2491
|
+
publicKey
|
|
2492
|
+
});
|
|
2493
|
+
const walletId = this.selectedWalletId;
|
|
2494
|
+
const state = this.getWalletState(walletId);
|
|
2495
|
+
const otherAddresses = state.addresses.filter((addr) => addr.addressType !== AddressType2.solana);
|
|
2496
|
+
if (publicKey) {
|
|
2497
|
+
const newAddresses = [...otherAddresses, { addressType: AddressType2.solana, address: publicKey }];
|
|
2498
|
+
this.setWalletState(walletId, {
|
|
2499
|
+
connected: true,
|
|
2500
|
+
addresses: newAddresses
|
|
2501
|
+
});
|
|
2502
|
+
const authUserId = await this.getAuthUserId("External wallet Solana account changed event");
|
|
2503
|
+
this.emit("connect", {
|
|
2504
|
+
addresses: newAddresses,
|
|
2505
|
+
source: "external-wallet-account-change",
|
|
2506
|
+
authUserId
|
|
2507
|
+
});
|
|
2508
|
+
} else {
|
|
2509
|
+
this.setWalletState(walletId, {
|
|
2510
|
+
connected: otherAddresses.length > 0,
|
|
2511
|
+
addresses: otherAddresses
|
|
2512
|
+
});
|
|
2513
|
+
this.emit("disconnect", {
|
|
2514
|
+
source: "external-wallet-account-change"
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
if (typeof walletInfo.providers.solana.on === "function") {
|
|
2519
|
+
walletInfo.providers.solana.on("accountChanged", handleExternalSolanaAccountChanged);
|
|
2520
|
+
this.browserInjectedCleanupFunctions.push(() => {
|
|
2521
|
+
if (typeof walletInfo.providers?.solana?.off === "function") {
|
|
2522
|
+
walletInfo.providers.solana.off("accountChanged", handleExternalSolanaAccountChanged);
|
|
2523
|
+
}
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
if (this.selectedWalletId) {
|
|
2528
|
+
this.externalWalletEventListenersSetup.add(this.selectedWalletId);
|
|
2529
|
+
}
|
|
783
2530
|
}
|
|
784
2531
|
};
|
|
785
2532
|
|
|
@@ -866,6 +2613,47 @@ var BrowserStorage = class {
|
|
|
866
2613
|
};
|
|
867
2614
|
});
|
|
868
2615
|
}
|
|
2616
|
+
async getShouldClearPreviousSession() {
|
|
2617
|
+
debug.log(DebugCategory.STORAGE, "Getting shouldClearPreviousSession flag from IndexedDB");
|
|
2618
|
+
const db = await this.getDB();
|
|
2619
|
+
return new Promise((resolve, reject) => {
|
|
2620
|
+
const transaction = db.transaction([this.storeName], "readonly");
|
|
2621
|
+
const store = transaction.objectStore(this.storeName);
|
|
2622
|
+
const request = store.get("shouldClearPreviousSession");
|
|
2623
|
+
request.onsuccess = () => {
|
|
2624
|
+
const shouldClear = request.result ?? false;
|
|
2625
|
+
debug.log(DebugCategory.STORAGE, "Retrieved shouldClearPreviousSession flag from IndexedDB", {
|
|
2626
|
+
shouldClear
|
|
2627
|
+
});
|
|
2628
|
+
resolve(shouldClear);
|
|
2629
|
+
};
|
|
2630
|
+
request.onerror = () => {
|
|
2631
|
+
debug.error(DebugCategory.STORAGE, "Failed to get shouldClearPreviousSession flag from IndexedDB", {
|
|
2632
|
+
error: request.error
|
|
2633
|
+
});
|
|
2634
|
+
reject(request.error);
|
|
2635
|
+
};
|
|
2636
|
+
});
|
|
2637
|
+
}
|
|
2638
|
+
async setShouldClearPreviousSession(should) {
|
|
2639
|
+
debug.log(DebugCategory.STORAGE, "Setting shouldClearPreviousSession flag in IndexedDB", { should });
|
|
2640
|
+
const db = await this.getDB();
|
|
2641
|
+
return new Promise((resolve, reject) => {
|
|
2642
|
+
const transaction = db.transaction([this.storeName], "readwrite");
|
|
2643
|
+
const store = transaction.objectStore(this.storeName);
|
|
2644
|
+
const request = store.put(should, "shouldClearPreviousSession");
|
|
2645
|
+
request.onsuccess = () => {
|
|
2646
|
+
debug.log(DebugCategory.STORAGE, "Successfully set shouldClearPreviousSession flag in IndexedDB");
|
|
2647
|
+
resolve();
|
|
2648
|
+
};
|
|
2649
|
+
request.onerror = () => {
|
|
2650
|
+
debug.error(DebugCategory.STORAGE, "Failed to set shouldClearPreviousSession flag in IndexedDB", {
|
|
2651
|
+
error: request.error
|
|
2652
|
+
});
|
|
2653
|
+
reject(request.error);
|
|
2654
|
+
};
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
869
2657
|
};
|
|
870
2658
|
|
|
871
2659
|
// src/providers/embedded/adapters/url-params.ts
|
|
@@ -879,6 +2667,147 @@ var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
|
|
|
879
2667
|
|
|
880
2668
|
// src/providers/embedded/adapters/auth.ts
|
|
881
2669
|
import { DEFAULT_AUTH_URL } from "@phantom/constants";
|
|
2670
|
+
|
|
2671
|
+
// src/utils/browser-detection.ts
|
|
2672
|
+
function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
|
|
2673
|
+
let name = "unknown";
|
|
2674
|
+
let version = "unknown";
|
|
2675
|
+
if (!userAgent || typeof userAgent !== "string") {
|
|
2676
|
+
return { name, version, userAgent: "unknown" };
|
|
2677
|
+
}
|
|
2678
|
+
try {
|
|
2679
|
+
if (userAgent.includes("Edg/")) {
|
|
2680
|
+
name = "edge";
|
|
2681
|
+
const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2682
|
+
if (match)
|
|
2683
|
+
version = match[1].split(".")[0];
|
|
2684
|
+
} else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
|
|
2685
|
+
name = "opera";
|
|
2686
|
+
const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2687
|
+
if (match)
|
|
2688
|
+
version = match[1].split(".")[0];
|
|
2689
|
+
} else if (userAgent.includes("SamsungBrowser/")) {
|
|
2690
|
+
name = "samsung";
|
|
2691
|
+
const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2692
|
+
if (match)
|
|
2693
|
+
version = match[1].split(".")[0];
|
|
2694
|
+
} else if (userAgent.includes("DuckDuckGo/")) {
|
|
2695
|
+
name = "duckduckgo";
|
|
2696
|
+
const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2697
|
+
if (match)
|
|
2698
|
+
version = match[1].split(".")[0];
|
|
2699
|
+
} else if (userAgent.includes("Chrome/") && hasBraveAPI) {
|
|
2700
|
+
name = "brave";
|
|
2701
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2702
|
+
if (match)
|
|
2703
|
+
version = match[1].split(".")[0];
|
|
2704
|
+
} else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
|
|
2705
|
+
if (userAgent.includes("Chrome/")) {
|
|
2706
|
+
name = "chrome-mobile";
|
|
2707
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2708
|
+
if (match)
|
|
2709
|
+
version = match[1].split(".")[0];
|
|
2710
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
2711
|
+
name = "firefox-mobile";
|
|
2712
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2713
|
+
if (match)
|
|
2714
|
+
version = match[1].split(".")[0];
|
|
2715
|
+
} else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
|
|
2716
|
+
name = "safari-mobile";
|
|
2717
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2718
|
+
if (match)
|
|
2719
|
+
version = match[1].split(".")[0];
|
|
2720
|
+
} else {
|
|
2721
|
+
name = "mobile";
|
|
2722
|
+
}
|
|
2723
|
+
} else if (userAgent.includes("Chrome/")) {
|
|
2724
|
+
name = "chrome";
|
|
2725
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2726
|
+
if (match)
|
|
2727
|
+
version = match[1].split(".")[0];
|
|
2728
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
2729
|
+
name = "firefox";
|
|
2730
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2731
|
+
if (match)
|
|
2732
|
+
version = match[1].split(".")[0];
|
|
2733
|
+
} else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
|
|
2734
|
+
name = "safari";
|
|
2735
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
2736
|
+
if (match)
|
|
2737
|
+
version = match[1].split(".")[0];
|
|
2738
|
+
}
|
|
2739
|
+
if (name === "unknown") {
|
|
2740
|
+
const patterns = [
|
|
2741
|
+
{ regex: /Chrome\/([0-9]+)/, name: "chrome" },
|
|
2742
|
+
{ regex: /Firefox\/([0-9]+)/, name: "firefox" },
|
|
2743
|
+
{ regex: /Safari\/([0-9]+)/, name: "safari" },
|
|
2744
|
+
{ regex: /Edge\/([0-9]+)/, name: "edge" },
|
|
2745
|
+
{ regex: /Opera\/([0-9]+)/, name: "opera" }
|
|
2746
|
+
];
|
|
2747
|
+
for (const pattern of patterns) {
|
|
2748
|
+
const match = userAgent.match(pattern.regex);
|
|
2749
|
+
if (match) {
|
|
2750
|
+
name = pattern.name;
|
|
2751
|
+
version = match[1];
|
|
2752
|
+
break;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
} catch (error) {
|
|
2757
|
+
}
|
|
2758
|
+
return { name, version, userAgent };
|
|
2759
|
+
}
|
|
2760
|
+
function detectBrowser() {
|
|
2761
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
2762
|
+
return { name: "unknown", version: "unknown", userAgent: "unknown" };
|
|
2763
|
+
}
|
|
2764
|
+
const userAgent = window.navigator.userAgent;
|
|
2765
|
+
const hasBraveAPI = !!navigator.brave;
|
|
2766
|
+
return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
|
|
2767
|
+
}
|
|
2768
|
+
function getPlatformName() {
|
|
2769
|
+
const { name, version } = detectBrowser();
|
|
2770
|
+
return version !== "unknown" ? `${name}-v${version}` : name;
|
|
2771
|
+
}
|
|
2772
|
+
function getBrowserDisplayName() {
|
|
2773
|
+
const { name, version } = detectBrowser();
|
|
2774
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
2775
|
+
return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
|
|
2776
|
+
}
|
|
2777
|
+
function isMobileDevice() {
|
|
2778
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
2779
|
+
return false;
|
|
2780
|
+
}
|
|
2781
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
2782
|
+
const mobilePatterns = [
|
|
2783
|
+
/android/,
|
|
2784
|
+
/iphone|ipad|ipod/,
|
|
2785
|
+
/blackberry/,
|
|
2786
|
+
/windows phone/,
|
|
2787
|
+
/mobile/,
|
|
2788
|
+
/tablet/,
|
|
2789
|
+
/silk/,
|
|
2790
|
+
/kindle/,
|
|
2791
|
+
/opera mini/,
|
|
2792
|
+
/opera mobi/
|
|
2793
|
+
];
|
|
2794
|
+
const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
|
|
2795
|
+
let isSmallScreen = false;
|
|
2796
|
+
try {
|
|
2797
|
+
isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
|
|
2798
|
+
} catch (error) {
|
|
2799
|
+
isSmallScreen = false;
|
|
2800
|
+
}
|
|
2801
|
+
let isTouchDevice = false;
|
|
2802
|
+
try {
|
|
2803
|
+
isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
2804
|
+
} catch (error) {
|
|
2805
|
+
isTouchDevice = false;
|
|
2806
|
+
}
|
|
2807
|
+
return isMobileUA || isSmallScreen && isTouchDevice;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
// src/providers/embedded/adapters/auth.ts
|
|
882
2811
|
var BrowserAuthProvider = class {
|
|
883
2812
|
constructor(urlParamsAccessor) {
|
|
884
2813
|
this.urlParamsAccessor = urlParamsAccessor;
|
|
@@ -897,21 +2826,24 @@ var BrowserAuthProvider = class {
|
|
|
897
2826
|
}
|
|
898
2827
|
const phantomOptions = options;
|
|
899
2828
|
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Starting Phantom Connect authentication", {
|
|
900
|
-
|
|
2829
|
+
publicKey: phantomOptions.publicKey,
|
|
901
2830
|
appId: phantomOptions.appId,
|
|
902
2831
|
provider: phantomOptions.provider,
|
|
903
|
-
authUrl: phantomOptions.authUrl
|
|
904
|
-
hasCustomData: !!phantomOptions.customAuthData
|
|
2832
|
+
authUrl: phantomOptions.authUrl
|
|
905
2833
|
});
|
|
906
2834
|
const baseUrl = phantomOptions.authUrl || DEFAULT_AUTH_URL;
|
|
907
2835
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
908
2836
|
const params = new URLSearchParams({
|
|
909
|
-
|
|
2837
|
+
public_key: phantomOptions.publicKey,
|
|
910
2838
|
app_id: phantomOptions.appId,
|
|
911
2839
|
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
|
|
912
2840
|
session_id: phantomOptions.sessionId,
|
|
913
|
-
|
|
914
|
-
|
|
2841
|
+
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
2842
|
+
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
2843
|
+
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
2844
|
+
sdk_version: "1.0.0",
|
|
2845
|
+
sdk_type: "browser",
|
|
2846
|
+
platform: detectBrowser().name
|
|
915
2847
|
});
|
|
916
2848
|
if (phantomOptions.provider) {
|
|
917
2849
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
@@ -922,12 +2854,8 @@ var BrowserAuthProvider = class {
|
|
|
922
2854
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
|
|
923
2855
|
params.append("provider", "google");
|
|
924
2856
|
}
|
|
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
2857
|
const authContext = {
|
|
930
|
-
|
|
2858
|
+
publicKey: phantomOptions.publicKey,
|
|
931
2859
|
appId: phantomOptions.appId,
|
|
932
2860
|
provider: phantomOptions.provider,
|
|
933
2861
|
sessionId: phantomOptions.sessionId
|
|
@@ -936,14 +2864,14 @@ var BrowserAuthProvider = class {
|
|
|
936
2864
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Stored auth context in session storage", { authContext });
|
|
937
2865
|
const authUrl = `${baseUrl}?${params.toString()}`;
|
|
938
2866
|
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Redirecting to Phantom Connect", { authUrl });
|
|
939
|
-
if (!authUrl.startsWith("https:")) {
|
|
2867
|
+
if (!authUrl.startsWith("https:") && !authUrl.startsWith("http://localhost")) {
|
|
940
2868
|
throw new Error("Invalid auth URL - only HTTPS URLs are allowed for authentication");
|
|
941
2869
|
}
|
|
942
2870
|
window.location.href = authUrl;
|
|
943
2871
|
resolve();
|
|
944
2872
|
});
|
|
945
2873
|
}
|
|
946
|
-
resumeAuthFromRedirect() {
|
|
2874
|
+
resumeAuthFromRedirect(provider) {
|
|
947
2875
|
try {
|
|
948
2876
|
const walletId = this.urlParamsAccessor.getParam("wallet_id");
|
|
949
2877
|
const sessionId = this.urlParamsAccessor.getParam("session_id");
|
|
@@ -994,10 +2922,37 @@ var BrowserAuthProvider = class {
|
|
|
994
2922
|
sessionId,
|
|
995
2923
|
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
|
|
996
2924
|
});
|
|
2925
|
+
const organizationId = this.urlParamsAccessor.getParam("organization_id");
|
|
2926
|
+
const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
|
|
2927
|
+
const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
|
|
2928
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
|
|
2929
|
+
walletId,
|
|
2930
|
+
organizationId,
|
|
2931
|
+
sessionId,
|
|
2932
|
+
accountDerivationIndex,
|
|
2933
|
+
expiresInMs,
|
|
2934
|
+
authUserId
|
|
2935
|
+
});
|
|
2936
|
+
if (!organizationId) {
|
|
2937
|
+
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
|
|
2938
|
+
throw new Error("Missing organization_id in auth response");
|
|
2939
|
+
}
|
|
2940
|
+
if (organizationId.startsWith("temp-")) {
|
|
2941
|
+
debug.warn(
|
|
2942
|
+
DebugCategory.PHANTOM_CONNECT_AUTH,
|
|
2943
|
+
"Received temporary organization_id, server may not be configured properly",
|
|
2944
|
+
{
|
|
2945
|
+
organizationId
|
|
2946
|
+
}
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
997
2949
|
return {
|
|
998
2950
|
walletId,
|
|
999
|
-
|
|
1000
|
-
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) :
|
|
2951
|
+
organizationId,
|
|
2952
|
+
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
2953
|
+
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
2954
|
+
authUserId: authUserId || void 0,
|
|
2955
|
+
provider
|
|
1001
2956
|
};
|
|
1002
2957
|
} catch (error) {
|
|
1003
2958
|
sessionStorage.removeItem("phantom-auth-context");
|
|
@@ -1006,160 +2961,120 @@ var BrowserAuthProvider = class {
|
|
|
1006
2961
|
}
|
|
1007
2962
|
};
|
|
1008
2963
|
|
|
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
|
-
};
|
|
2964
|
+
// src/providers/embedded/adapters/phantom-app.ts
|
|
2965
|
+
import { isPhantomExtensionInstalled as isPhantomExtensionInstalled3 } from "@phantom/browser-injected-sdk";
|
|
1024
2966
|
|
|
1025
|
-
// src/
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
if (!
|
|
1030
|
-
return
|
|
2967
|
+
// src/isPhantomLoginAvailable.ts
|
|
2968
|
+
import { isPhantomExtensionInstalled as isPhantomExtensionInstalled2 } from "@phantom/browser-injected-sdk";
|
|
2969
|
+
async function isPhantomLoginAvailable(timeoutMs = 3e3) {
|
|
2970
|
+
const extensionInstalled = await waitForExtension(timeoutMs);
|
|
2971
|
+
if (!extensionInstalled) {
|
|
2972
|
+
return false;
|
|
1031
2973
|
}
|
|
1032
2974
|
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];
|
|
2975
|
+
if (!window.phantom?.app?.features || typeof window.phantom.app.features !== "function") {
|
|
2976
|
+
return false;
|
|
1092
2977
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
2978
|
+
const response = await window.phantom.app.features();
|
|
2979
|
+
if (!Array.isArray(response.features)) {
|
|
2980
|
+
return false;
|
|
2981
|
+
}
|
|
2982
|
+
return response.features.includes("phantom_login");
|
|
2983
|
+
} catch (error) {
|
|
2984
|
+
console.error("Error checking Phantom extension features", error);
|
|
2985
|
+
return false;
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
async function waitForExtension(timeoutMs) {
|
|
2989
|
+
return new Promise((resolve) => {
|
|
2990
|
+
const startTime = Date.now();
|
|
2991
|
+
const checkInterval = 100;
|
|
2992
|
+
const checkForExtension = () => {
|
|
2993
|
+
try {
|
|
2994
|
+
if (isPhantomExtensionInstalled2()) {
|
|
2995
|
+
resolve(true);
|
|
2996
|
+
return;
|
|
1107
2997
|
}
|
|
2998
|
+
} catch (error) {
|
|
2999
|
+
}
|
|
3000
|
+
const elapsed = Date.now() - startTime;
|
|
3001
|
+
if (elapsed >= timeoutMs) {
|
|
3002
|
+
resolve(false);
|
|
3003
|
+
return;
|
|
1108
3004
|
}
|
|
3005
|
+
setTimeout(checkForExtension, checkInterval);
|
|
3006
|
+
};
|
|
3007
|
+
checkForExtension();
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
// src/providers/embedded/adapters/phantom-app.ts
|
|
3012
|
+
var BrowserPhantomAppProvider = class {
|
|
3013
|
+
/**
|
|
3014
|
+
* Check if the Phantom extension is installed in the browser
|
|
3015
|
+
*/
|
|
3016
|
+
isAvailable() {
|
|
3017
|
+
return isPhantomExtensionInstalled3();
|
|
3018
|
+
}
|
|
3019
|
+
/**
|
|
3020
|
+
* Authenticate using the Phantom browser extension
|
|
3021
|
+
*/
|
|
3022
|
+
async authenticate(options) {
|
|
3023
|
+
if (!this.isAvailable()) {
|
|
3024
|
+
throw new Error(
|
|
3025
|
+
"Phantom extension is not installed. Please install the Phantom browser extension to use this authentication method."
|
|
3026
|
+
);
|
|
3027
|
+
}
|
|
3028
|
+
const loginAvailable = await isPhantomLoginAvailable();
|
|
3029
|
+
if (!loginAvailable) {
|
|
3030
|
+
throw new Error(
|
|
3031
|
+
"Phantom Login is not available. Please update your Phantom extension to use this authentication method."
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
3034
|
+
try {
|
|
3035
|
+
if (!window.phantom?.app?.login) {
|
|
3036
|
+
throw new Error("Phantom extension login method not found");
|
|
3037
|
+
}
|
|
3038
|
+
const result = await window.phantom.app.login({
|
|
3039
|
+
publicKey: options.publicKey,
|
|
3040
|
+
appId: options.appId,
|
|
3041
|
+
sessionId: options.sessionId
|
|
3042
|
+
});
|
|
3043
|
+
if (!result || !result.walletId || !result.organizationId) {
|
|
3044
|
+
throw new Error("Invalid authentication response from Phantom extension");
|
|
3045
|
+
}
|
|
3046
|
+
return {
|
|
3047
|
+
walletId: result.walletId,
|
|
3048
|
+
organizationId: result.organizationId,
|
|
3049
|
+
provider: "phantom",
|
|
3050
|
+
accountDerivationIndex: result.accountDerivationIndex ?? 0,
|
|
3051
|
+
expiresInMs: result.expiresInMs ?? 0,
|
|
3052
|
+
authUserId: result.authUserId
|
|
3053
|
+
};
|
|
3054
|
+
} catch (error) {
|
|
3055
|
+
if (error instanceof Error) {
|
|
3056
|
+
throw error;
|
|
3057
|
+
}
|
|
3058
|
+
throw new Error(`Phantom extension authentication failed: ${String(error)}`);
|
|
1109
3059
|
}
|
|
1110
|
-
} catch (error) {
|
|
1111
3060
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
3061
|
+
};
|
|
3062
|
+
|
|
3063
|
+
// src/providers/embedded/adapters/logger.ts
|
|
3064
|
+
var BrowserLogger = class {
|
|
3065
|
+
info(category, message, data) {
|
|
3066
|
+
debug.info(category, message, data);
|
|
1117
3067
|
}
|
|
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;
|
|
3068
|
+
warn(category, message, data) {
|
|
3069
|
+
debug.warn(category, message, data);
|
|
1134
3070
|
}
|
|
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;
|
|
3071
|
+
error(category, message, data) {
|
|
3072
|
+
debug.error(category, message, data);
|
|
1154
3073
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
1158
|
-
} catch (error) {
|
|
1159
|
-
isTouchDevice = false;
|
|
3074
|
+
log(category, message, data) {
|
|
3075
|
+
debug.log(category, message, data);
|
|
1160
3076
|
}
|
|
1161
|
-
|
|
1162
|
-
}
|
|
3077
|
+
};
|
|
1163
3078
|
|
|
1164
3079
|
// src/providers/embedded/index.ts
|
|
1165
3080
|
import { ANALYTICS_HEADERS } from "@phantom/constants";
|
|
@@ -1177,6 +3092,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
1177
3092
|
const platform = {
|
|
1178
3093
|
storage: new BrowserStorage(),
|
|
1179
3094
|
authProvider: new BrowserAuthProvider(urlParamsAccessor),
|
|
3095
|
+
phantomAppProvider: new BrowserPhantomAppProvider(),
|
|
1180
3096
|
urlParamsAccessor,
|
|
1181
3097
|
stamper,
|
|
1182
3098
|
name: platformName,
|
|
@@ -1189,26 +3105,61 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
1189
3105
|
// Full user agent for more detailed info
|
|
1190
3106
|
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
1191
3107
|
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
1192
|
-
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0
|
|
3108
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0"
|
|
1193
3109
|
// Replaced at build time
|
|
1194
3110
|
}
|
|
1195
3111
|
};
|
|
1196
3112
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Detected platform", { platformName });
|
|
1197
3113
|
const logger = new BrowserLogger();
|
|
1198
3114
|
super(config, platform, logger);
|
|
3115
|
+
this.addressTypes = config.addressTypes;
|
|
1199
3116
|
debug.info(DebugCategory.EMBEDDED_PROVIDER, "Browser EmbeddedProvider initialized");
|
|
1200
3117
|
}
|
|
3118
|
+
getEnabledAddressTypes() {
|
|
3119
|
+
return this.addressTypes;
|
|
3120
|
+
}
|
|
1201
3121
|
};
|
|
1202
3122
|
|
|
1203
3123
|
// src/ProviderManager.ts
|
|
3124
|
+
import {
|
|
3125
|
+
EMBEDDED_PROVIDER_AUTH_TYPES
|
|
3126
|
+
} from "@phantom/embedded-provider-core";
|
|
1204
3127
|
import { DEFAULT_WALLET_API_URL, DEFAULT_EMBEDDED_WALLET_TYPE, DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2 } from "@phantom/constants";
|
|
3128
|
+
|
|
3129
|
+
// src/utils/auth-callback.ts
|
|
3130
|
+
function isAuthFailureCallback(searchParams) {
|
|
3131
|
+
if (typeof window === "undefined" && !searchParams)
|
|
3132
|
+
return false;
|
|
3133
|
+
const params = searchParams || new URLSearchParams(window.location.search);
|
|
3134
|
+
const responseType = params.get("response_type");
|
|
3135
|
+
const sessionId = params.get("session_id");
|
|
3136
|
+
return responseType === "failure" && !!sessionId;
|
|
3137
|
+
}
|
|
3138
|
+
function isAuthCallbackUrl(searchParams) {
|
|
3139
|
+
if (typeof window === "undefined" && !searchParams)
|
|
3140
|
+
return false;
|
|
3141
|
+
const params = searchParams || new URLSearchParams(window.location.search);
|
|
3142
|
+
const sessionId = params.get("session_id");
|
|
3143
|
+
return !!(sessionId && (params.has("response_type") || params.has("wallet_id")));
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
// src/utils/deeplink.ts
|
|
3147
|
+
function getDeeplinkToPhantom(ref) {
|
|
3148
|
+
if (!window.location.href.startsWith("http:") && !window.location.href.startsWith("https:")) {
|
|
3149
|
+
throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported for deeplinks");
|
|
3150
|
+
}
|
|
3151
|
+
const currentUrl = encodeURIComponent(window.location.href);
|
|
3152
|
+
const refParam = ref ? `?ref=${encodeURIComponent(ref)}` : "";
|
|
3153
|
+
return `https://phantom.app/ul/browse/${currentUrl}${refParam}`;
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// src/ProviderManager.ts
|
|
1205
3157
|
var ProviderManager = class {
|
|
1206
3158
|
// Track which providers have forwarding set up
|
|
1207
3159
|
constructor(config) {
|
|
1208
3160
|
this.providers = /* @__PURE__ */ new Map();
|
|
1209
3161
|
this.currentProvider = null;
|
|
1210
3162
|
this.currentProviderKey = null;
|
|
1211
|
-
this.walletId = null;
|
|
1212
3163
|
// Event management for forwarding provider events
|
|
1213
3164
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
1214
3165
|
this.providerForwardingSetup = /* @__PURE__ */ new WeakSet();
|
|
@@ -1244,7 +3195,6 @@ var ProviderManager = class {
|
|
|
1244
3195
|
}
|
|
1245
3196
|
this.currentProvider = this.providers.get(key);
|
|
1246
3197
|
this.currentProviderKey = key;
|
|
1247
|
-
this.walletId = null;
|
|
1248
3198
|
this.ensureProviderEventForwarding();
|
|
1249
3199
|
return this.currentProvider;
|
|
1250
3200
|
}
|
|
@@ -1267,29 +3217,78 @@ var ProviderManager = class {
|
|
|
1267
3217
|
embeddedWalletType
|
|
1268
3218
|
};
|
|
1269
3219
|
}
|
|
3220
|
+
/**
|
|
3221
|
+
* Check if a provider is allowed by the config
|
|
3222
|
+
*/
|
|
3223
|
+
isProviderAllowed(provider) {
|
|
3224
|
+
return this.config.providers.includes(provider);
|
|
3225
|
+
}
|
|
1270
3226
|
/**
|
|
1271
3227
|
* Connect using the current provider
|
|
3228
|
+
* Automatically switches provider based on authOptions.provider
|
|
1272
3229
|
*/
|
|
1273
3230
|
async connect(authOptions) {
|
|
1274
3231
|
debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
|
|
1275
3232
|
currentProviderKey: this.currentProviderKey,
|
|
1276
|
-
authOptions:
|
|
3233
|
+
authOptions: { provider: authOptions.provider }
|
|
1277
3234
|
});
|
|
3235
|
+
if (!this.isProviderAllowed(authOptions.provider)) {
|
|
3236
|
+
const error = `Provider "${authOptions.provider}" is not in the allowed providers list: ${JSON.stringify(this.config.providers)}`;
|
|
3237
|
+
debug.error(DebugCategory.PROVIDER_MANAGER, error);
|
|
3238
|
+
throw new Error(error);
|
|
3239
|
+
}
|
|
3240
|
+
const requestedProvider = authOptions.provider;
|
|
3241
|
+
let targetProviderType = null;
|
|
3242
|
+
if (requestedProvider === "injected") {
|
|
3243
|
+
targetProviderType = "injected";
|
|
3244
|
+
} else if (requestedProvider === "deeplink") {
|
|
3245
|
+
try {
|
|
3246
|
+
const deeplinkUrl = getDeeplinkToPhantom();
|
|
3247
|
+
if (typeof window !== "undefined") {
|
|
3248
|
+
window.location.href = deeplinkUrl;
|
|
3249
|
+
}
|
|
3250
|
+
return {
|
|
3251
|
+
addresses: [],
|
|
3252
|
+
walletId: void 0,
|
|
3253
|
+
authUserId: void 0
|
|
3254
|
+
};
|
|
3255
|
+
} catch (error) {
|
|
3256
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to open deeplink";
|
|
3257
|
+
debug.error(DebugCategory.PROVIDER_MANAGER, "Deeplink error", { error: errorMessage });
|
|
3258
|
+
throw new Error(`Failed to open deeplink: ${errorMessage}`);
|
|
3259
|
+
}
|
|
3260
|
+
} else if (EMBEDDED_PROVIDER_AUTH_TYPES.includes(requestedProvider)) {
|
|
3261
|
+
targetProviderType = "embedded";
|
|
3262
|
+
}
|
|
3263
|
+
if (targetProviderType) {
|
|
3264
|
+
const currentInfo = this.getCurrentProviderInfo();
|
|
3265
|
+
if (currentInfo?.type !== targetProviderType) {
|
|
3266
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-switching provider based on auth options", {
|
|
3267
|
+
from: currentInfo?.type,
|
|
3268
|
+
to: targetProviderType,
|
|
3269
|
+
requestedProvider
|
|
3270
|
+
});
|
|
3271
|
+
const switchOptions = {};
|
|
3272
|
+
if (targetProviderType === "embedded") {
|
|
3273
|
+
switchOptions.embeddedWalletType = currentInfo?.embeddedWalletType || this.config.embeddedWalletType;
|
|
3274
|
+
}
|
|
3275
|
+
this.switchProvider(targetProviderType, switchOptions);
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
1278
3278
|
if (!this.currentProvider) {
|
|
1279
3279
|
debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
|
|
1280
3280
|
throw new Error("No provider selected");
|
|
1281
3281
|
}
|
|
1282
3282
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Delegating to provider connect method");
|
|
1283
3283
|
const result = await this.currentProvider.connect(authOptions);
|
|
1284
|
-
this.walletId = result.walletId || null;
|
|
1285
3284
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Connection successful, saving preferences", {
|
|
1286
|
-
|
|
1287
|
-
|
|
3285
|
+
addressCount: result.addresses?.length || 0,
|
|
3286
|
+
provider: authOptions.provider
|
|
1288
3287
|
});
|
|
1289
3288
|
this.saveProviderPreference();
|
|
1290
3289
|
debug.info(DebugCategory.PROVIDER_MANAGER, "Connect completed", {
|
|
1291
|
-
|
|
1292
|
-
|
|
3290
|
+
addresses: result.addresses,
|
|
3291
|
+
provider: authOptions.provider
|
|
1293
3292
|
});
|
|
1294
3293
|
return result;
|
|
1295
3294
|
}
|
|
@@ -1300,7 +3299,6 @@ var ProviderManager = class {
|
|
|
1300
3299
|
if (!this.currentProvider)
|
|
1301
3300
|
return;
|
|
1302
3301
|
await this.currentProvider.disconnect();
|
|
1303
|
-
this.walletId = null;
|
|
1304
3302
|
}
|
|
1305
3303
|
/**
|
|
1306
3304
|
* Get addresses from current provider
|
|
@@ -1318,10 +3316,71 @@ var ProviderManager = class {
|
|
|
1318
3316
|
return this.currentProvider?.isConnected() ?? false;
|
|
1319
3317
|
}
|
|
1320
3318
|
/**
|
|
1321
|
-
*
|
|
3319
|
+
* Attempt auto-connect with fallback strategy
|
|
3320
|
+
* Tries embedded provider first if it exists and is allowed, then injected provider if allowed
|
|
3321
|
+
* Returns true if any provider successfully connected
|
|
1322
3322
|
*/
|
|
1323
|
-
|
|
1324
|
-
|
|
3323
|
+
async autoConnect() {
|
|
3324
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Starting auto-connect with fallback strategy");
|
|
3325
|
+
if (isAuthFailureCallback()) {
|
|
3326
|
+
debug.warn(DebugCategory.PROVIDER_MANAGER, "Auth failure detected in URL, skipping autoConnect fallback");
|
|
3327
|
+
return false;
|
|
3328
|
+
}
|
|
3329
|
+
const embeddedWalletType = this.config.embeddedWalletType || "user-wallet";
|
|
3330
|
+
const embeddedKey = this.getProviderKey("embedded", embeddedWalletType);
|
|
3331
|
+
const embeddedAllowed = this.config.providers.some((p) => p !== "injected");
|
|
3332
|
+
if (embeddedAllowed && this.providers.has(embeddedKey)) {
|
|
3333
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Trying auto-connect with existing embedded provider");
|
|
3334
|
+
const embeddedProvider = this.providers.get(embeddedKey);
|
|
3335
|
+
try {
|
|
3336
|
+
const previousProvider = this.currentProvider;
|
|
3337
|
+
const previousKey = this.currentProviderKey;
|
|
3338
|
+
this.currentProvider = embeddedProvider;
|
|
3339
|
+
this.currentProviderKey = embeddedKey;
|
|
3340
|
+
this.ensureProviderEventForwarding();
|
|
3341
|
+
await embeddedProvider.autoConnect();
|
|
3342
|
+
if (embeddedProvider.isConnected()) {
|
|
3343
|
+
debug.info(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect successful");
|
|
3344
|
+
this.saveProviderPreference();
|
|
3345
|
+
return true;
|
|
3346
|
+
} else {
|
|
3347
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded provider did not connect, restoring previous provider");
|
|
3348
|
+
this.currentProvider = previousProvider;
|
|
3349
|
+
this.currentProviderKey = previousKey;
|
|
3350
|
+
}
|
|
3351
|
+
} catch (error) {
|
|
3352
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect failed", {
|
|
3353
|
+
error: error.message
|
|
3354
|
+
});
|
|
3355
|
+
if (isAuthCallbackUrl()) {
|
|
3356
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "In auth callback URL, not attempting injected fallback");
|
|
3357
|
+
return false;
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const injectedAllowed = this.config.providers.includes("injected");
|
|
3362
|
+
const injectedKey = this.getProviderKey("injected");
|
|
3363
|
+
if (injectedAllowed && this.providers.has(injectedKey)) {
|
|
3364
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Trying auto-connect with existing injected provider");
|
|
3365
|
+
const injectedProvider = this.providers.get(injectedKey);
|
|
3366
|
+
try {
|
|
3367
|
+
this.currentProvider = injectedProvider;
|
|
3368
|
+
this.currentProviderKey = injectedKey;
|
|
3369
|
+
this.ensureProviderEventForwarding();
|
|
3370
|
+
await injectedProvider.autoConnect();
|
|
3371
|
+
if (injectedProvider.isConnected()) {
|
|
3372
|
+
debug.info(DebugCategory.PROVIDER_MANAGER, "Injected auto-connect successful");
|
|
3373
|
+
this.saveProviderPreference();
|
|
3374
|
+
return true;
|
|
3375
|
+
}
|
|
3376
|
+
} catch (error) {
|
|
3377
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Injected auto-connect failed", {
|
|
3378
|
+
error: error.message
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-connect failed for all allowed providers");
|
|
3383
|
+
return false;
|
|
1325
3384
|
}
|
|
1326
3385
|
/**
|
|
1327
3386
|
* Add event listener - stores callback and ensures current provider forwards events to ProviderManager
|
|
@@ -1390,7 +3449,8 @@ var ProviderManager = class {
|
|
|
1390
3449
|
"connect",
|
|
1391
3450
|
"connect_error",
|
|
1392
3451
|
"disconnect",
|
|
1393
|
-
"error"
|
|
3452
|
+
"error",
|
|
3453
|
+
"spending_limit_reached"
|
|
1394
3454
|
];
|
|
1395
3455
|
for (const event of eventsToForward) {
|
|
1396
3456
|
const forwardingCallback = (data) => {
|
|
@@ -1404,12 +3464,33 @@ var ProviderManager = class {
|
|
|
1404
3464
|
}
|
|
1405
3465
|
/**
|
|
1406
3466
|
* Set default provider based on initial config
|
|
3467
|
+
* Creates providers based on the allowed providers array
|
|
1407
3468
|
*/
|
|
1408
3469
|
setDefaultProvider() {
|
|
1409
|
-
const defaultType = this.config.providerType || "embedded";
|
|
1410
3470
|
const defaultEmbeddedType = this.config.embeddedWalletType || "user-wallet";
|
|
1411
|
-
this.
|
|
1412
|
-
this.
|
|
3471
|
+
const hasInjected = this.config.providers.includes("injected");
|
|
3472
|
+
const hasEmbedded = this.config.providers.some((p) => p !== "injected" && p !== "deeplink");
|
|
3473
|
+
if (hasInjected) {
|
|
3474
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Creating injected provider (allowed by providers array)");
|
|
3475
|
+
this.createProvider("injected");
|
|
3476
|
+
}
|
|
3477
|
+
if (hasEmbedded) {
|
|
3478
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Creating embedded provider (allowed by providers array)");
|
|
3479
|
+
this.createProvider("embedded", defaultEmbeddedType);
|
|
3480
|
+
}
|
|
3481
|
+
let defaultType;
|
|
3482
|
+
if (hasEmbedded && this.providers.has(`embedded-${defaultEmbeddedType}`)) {
|
|
3483
|
+
defaultType = "embedded";
|
|
3484
|
+
} else if (hasInjected && this.providers.has("injected")) {
|
|
3485
|
+
defaultType = "injected";
|
|
3486
|
+
} else {
|
|
3487
|
+
throw new Error("No valid providers could be created from the providers array");
|
|
3488
|
+
}
|
|
3489
|
+
const switchOptions = {};
|
|
3490
|
+
if (defaultType === "embedded") {
|
|
3491
|
+
switchOptions.embeddedWalletType = defaultEmbeddedType;
|
|
3492
|
+
}
|
|
3493
|
+
this.switchProvider(defaultType, switchOptions);
|
|
1413
3494
|
}
|
|
1414
3495
|
/**
|
|
1415
3496
|
* Create a provider instance
|
|
@@ -1469,66 +3550,48 @@ var ProviderManager = class {
|
|
|
1469
3550
|
console.error("Failed to save provider preference:", error);
|
|
1470
3551
|
}
|
|
1471
3552
|
}
|
|
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
3553
|
};
|
|
1491
3554
|
|
|
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
3555
|
// src/BrowserSDK.ts
|
|
3556
|
+
import { EMBEDDED_PROVIDER_AUTH_TYPES as EMBEDDED_PROVIDER_AUTH_TYPES2 } from "@phantom/embedded-provider-core";
|
|
1518
3557
|
import { DEFAULT_EMBEDDED_WALLET_TYPE as DEFAULT_EMBEDDED_WALLET_TYPE2 } from "@phantom/constants";
|
|
3558
|
+
var BROWSER_SDK_PROVIDER_TYPES = [
|
|
3559
|
+
...EMBEDDED_PROVIDER_AUTH_TYPES2,
|
|
3560
|
+
"injected",
|
|
3561
|
+
"deeplink"
|
|
3562
|
+
];
|
|
1519
3563
|
var BrowserSDK = class {
|
|
1520
3564
|
constructor(config) {
|
|
3565
|
+
this.walletRegistry = getWalletRegistry();
|
|
3566
|
+
this.isLoading = true;
|
|
1521
3567
|
debug.info(DebugCategory.BROWSER_SDK, "Initializing BrowserSDK", {
|
|
1522
|
-
|
|
3568
|
+
providers: config.providers,
|
|
1523
3569
|
embeddedWalletType: config.embeddedWalletType,
|
|
1524
3570
|
addressTypes: config.addressTypes
|
|
1525
3571
|
});
|
|
1526
|
-
if (!
|
|
1527
|
-
debug.error(DebugCategory.BROWSER_SDK, "Invalid
|
|
1528
|
-
throw new Error(
|
|
3572
|
+
if (!Array.isArray(config.providers) || config.providers.length === 0) {
|
|
3573
|
+
debug.error(DebugCategory.BROWSER_SDK, "Invalid providers array", { providers: config.providers });
|
|
3574
|
+
throw new Error("providers must be a non-empty array of AuthProviderType");
|
|
3575
|
+
}
|
|
3576
|
+
const invalidProviders = config.providers.filter((p) => !BROWSER_SDK_PROVIDER_TYPES.includes(p));
|
|
3577
|
+
if (invalidProviders.length > 0) {
|
|
3578
|
+
debug.error(DebugCategory.BROWSER_SDK, "Invalid provider types", {
|
|
3579
|
+
invalidProviders,
|
|
3580
|
+
validProviders: BROWSER_SDK_PROVIDER_TYPES
|
|
3581
|
+
});
|
|
3582
|
+
throw new Error(
|
|
3583
|
+
`Invalid provider type(s): ${invalidProviders.join(", ")}. Valid providers are: ${BROWSER_SDK_PROVIDER_TYPES.join(", ")}`
|
|
3584
|
+
);
|
|
3585
|
+
}
|
|
3586
|
+
const hasEmbeddedProviders = config.providers.some((p) => p !== "injected");
|
|
3587
|
+
if (hasEmbeddedProviders && !config.appId) {
|
|
3588
|
+
debug.error(DebugCategory.BROWSER_SDK, "appId required for embedded providers", {
|
|
3589
|
+
providers: config.providers
|
|
3590
|
+
});
|
|
3591
|
+
throw new Error("appId is required when using embedded providers (google, apple, phantom, etc.)");
|
|
1529
3592
|
}
|
|
1530
3593
|
const embeddedWalletType = config.embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE2;
|
|
1531
|
-
if (
|
|
3594
|
+
if (!["app-wallet", "user-wallet"].includes(embeddedWalletType)) {
|
|
1532
3595
|
debug.error(DebugCategory.BROWSER_SDK, "Invalid embeddedWalletType", {
|
|
1533
3596
|
embeddedWalletType: config.embeddedWalletType
|
|
1534
3597
|
});
|
|
@@ -1536,7 +3599,14 @@ var BrowserSDK = class {
|
|
|
1536
3599
|
`Invalid embeddedWalletType: ${config.embeddedWalletType}. Must be "app-wallet" or "user-wallet".`
|
|
1537
3600
|
);
|
|
1538
3601
|
}
|
|
3602
|
+
this.config = config;
|
|
1539
3603
|
this.providerManager = new ProviderManager(config);
|
|
3604
|
+
void this.discoverWallets();
|
|
3605
|
+
}
|
|
3606
|
+
discoverWallets() {
|
|
3607
|
+
return this.walletRegistry.discover(this.config.addressTypes).finally(() => {
|
|
3608
|
+
this.isLoading = false;
|
|
3609
|
+
});
|
|
1540
3610
|
}
|
|
1541
3611
|
// ===== CHAIN API =====
|
|
1542
3612
|
/**
|
|
@@ -1569,7 +3639,6 @@ var BrowserSDK = class {
|
|
|
1569
3639
|
const result = await this.providerManager.connect(options);
|
|
1570
3640
|
debug.info(DebugCategory.BROWSER_SDK, "Connection successful", {
|
|
1571
3641
|
addressCount: result.addresses.length,
|
|
1572
|
-
walletId: result.walletId,
|
|
1573
3642
|
status: result.status
|
|
1574
3643
|
});
|
|
1575
3644
|
return result;
|
|
@@ -1591,22 +3660,6 @@ var BrowserSDK = class {
|
|
|
1591
3660
|
throw error;
|
|
1592
3661
|
}
|
|
1593
3662
|
}
|
|
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
3663
|
// ===== STATE QUERIES =====
|
|
1611
3664
|
/**
|
|
1612
3665
|
* Check if the SDK is connected to a wallet
|
|
@@ -1627,18 +3680,19 @@ var BrowserSDK = class {
|
|
|
1627
3680
|
return this.providerManager.getCurrentProviderInfo();
|
|
1628
3681
|
}
|
|
1629
3682
|
/**
|
|
1630
|
-
* Get
|
|
3683
|
+
* Get enabled address types for the current provider
|
|
3684
|
+
* - For embedded provider: returns config.addressTypes
|
|
3685
|
+
* - For Phantom injected: returns config.addressTypes
|
|
3686
|
+
* - For discovered wallets: returns the wallet's addressTypes from registry
|
|
1631
3687
|
*/
|
|
1632
|
-
|
|
1633
|
-
|
|
3688
|
+
getEnabledAddressTypes() {
|
|
3689
|
+
const currentProvider = this.providerManager.getCurrentProvider();
|
|
3690
|
+
if (!currentProvider) {
|
|
3691
|
+
return [];
|
|
3692
|
+
}
|
|
3693
|
+
return currentProvider.getEnabledAddressTypes();
|
|
1634
3694
|
}
|
|
1635
3695
|
// ===== UTILITY METHODS =====
|
|
1636
|
-
/**
|
|
1637
|
-
* Check if Phantom extension is installed
|
|
1638
|
-
*/
|
|
1639
|
-
static async isPhantomInstalled(timeoutMs) {
|
|
1640
|
-
return waitForPhantomExtension(timeoutMs);
|
|
1641
|
-
}
|
|
1642
3696
|
/**
|
|
1643
3697
|
* Add event listener for provider events (connect, connect_start, connect_error, disconnect, error)
|
|
1644
3698
|
* Works with both embedded and injected providers
|
|
@@ -1658,53 +3712,34 @@ var BrowserSDK = class {
|
|
|
1658
3712
|
/**
|
|
1659
3713
|
* Attempt auto-connection using existing session
|
|
1660
3714
|
* Should be called after setting up event listeners
|
|
1661
|
-
*
|
|
3715
|
+
* Tries embedded provider first, then injected provider as fallback
|
|
1662
3716
|
*/
|
|
1663
3717
|
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", {
|
|
3718
|
+
debug.log(DebugCategory.BROWSER_SDK, "Attempting auto-connect with fallback strategy");
|
|
3719
|
+
const result = await this.providerManager.autoConnect();
|
|
3720
|
+
if (result) {
|
|
3721
|
+
debug.info(DebugCategory.BROWSER_SDK, "Auto-connect successful", {
|
|
1670
3722
|
providerType: this.getCurrentProviderInfo()?.type
|
|
1671
3723
|
});
|
|
3724
|
+
} else {
|
|
3725
|
+
debug.log(DebugCategory.BROWSER_SDK, "Auto-connect failed for all providers");
|
|
1672
3726
|
}
|
|
1673
3727
|
}
|
|
1674
|
-
/**
|
|
1675
|
-
* Debug configuration methods
|
|
1676
|
-
* These allow dynamic debug configuration without SDK reinstantiation
|
|
1677
|
-
*/
|
|
1678
|
-
/**
|
|
1679
|
-
* Enable debug logging
|
|
1680
|
-
*/
|
|
1681
3728
|
enableDebug() {
|
|
1682
3729
|
debug.enable();
|
|
1683
3730
|
debug.info(DebugCategory.BROWSER_SDK, "Debug logging enabled");
|
|
1684
3731
|
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Disable debug logging
|
|
1687
|
-
*/
|
|
1688
3732
|
disableDebug() {
|
|
1689
3733
|
debug.disable();
|
|
1690
3734
|
}
|
|
1691
|
-
/**
|
|
1692
|
-
* Set debug level
|
|
1693
|
-
*/
|
|
1694
3735
|
setDebugLevel(level) {
|
|
1695
3736
|
debug.setLevel(level);
|
|
1696
3737
|
debug.info(DebugCategory.BROWSER_SDK, "Debug level updated", { level });
|
|
1697
3738
|
}
|
|
1698
|
-
/**
|
|
1699
|
-
* Set debug callback function
|
|
1700
|
-
*/
|
|
1701
3739
|
setDebugCallback(callback) {
|
|
1702
3740
|
debug.setCallback(callback);
|
|
1703
3741
|
debug.info(DebugCategory.BROWSER_SDK, "Debug callback updated");
|
|
1704
3742
|
}
|
|
1705
|
-
/**
|
|
1706
|
-
* Configure debug settings all at once
|
|
1707
|
-
*/
|
|
1708
3743
|
configureDebug(config) {
|
|
1709
3744
|
if (config.enabled !== void 0) {
|
|
1710
3745
|
if (config.enabled) {
|
|
@@ -1810,23 +3845,54 @@ var BrowserSDK = class {
|
|
|
1810
3845
|
throw error;
|
|
1811
3846
|
}
|
|
1812
3847
|
}
|
|
3848
|
+
getDiscoveredWallets() {
|
|
3849
|
+
debug.log(DebugCategory.BROWSER_SDK, "Getting discovered wallets");
|
|
3850
|
+
try {
|
|
3851
|
+
const allWallets = this.walletRegistry.getByAddressTypes(this.config.addressTypes);
|
|
3852
|
+
debug.log(DebugCategory.BROWSER_SDK, "Retrieved discovered wallets", {
|
|
3853
|
+
count: allWallets.length,
|
|
3854
|
+
walletIds: allWallets.map((w) => w.id)
|
|
3855
|
+
});
|
|
3856
|
+
return allWallets;
|
|
3857
|
+
} catch (error) {
|
|
3858
|
+
debug.error(DebugCategory.BROWSER_SDK, "Failed to get discovered wallets", {
|
|
3859
|
+
error: error.message
|
|
3860
|
+
});
|
|
3861
|
+
return [];
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
1813
3864
|
};
|
|
1814
3865
|
|
|
1815
|
-
// src/
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
3866
|
+
// src/waitForPhantomExtension.ts
|
|
3867
|
+
import { isPhantomExtensionInstalled as isPhantomExtensionInstalled4 } from "@phantom/browser-injected-sdk";
|
|
3868
|
+
async function waitForPhantomExtension(timeoutMs = 3e3) {
|
|
3869
|
+
return new Promise((resolve) => {
|
|
3870
|
+
const startTime = Date.now();
|
|
3871
|
+
const checkInterval = 100;
|
|
3872
|
+
const checkForExtension = () => {
|
|
3873
|
+
try {
|
|
3874
|
+
if (isPhantomExtensionInstalled4()) {
|
|
3875
|
+
resolve(true);
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3878
|
+
} catch (error) {
|
|
3879
|
+
}
|
|
3880
|
+
const elapsed = Date.now() - startTime;
|
|
3881
|
+
if (elapsed >= timeoutMs) {
|
|
3882
|
+
resolve(false);
|
|
3883
|
+
return;
|
|
3884
|
+
}
|
|
3885
|
+
setTimeout(checkForExtension, checkInterval);
|
|
3886
|
+
};
|
|
3887
|
+
checkForExtension();
|
|
3888
|
+
});
|
|
1823
3889
|
}
|
|
1824
3890
|
|
|
1825
3891
|
// src/index.ts
|
|
1826
3892
|
import { NetworkId } from "@phantom/constants";
|
|
1827
|
-
import { AddressType as
|
|
3893
|
+
import { AddressType as AddressType3 } from "@phantom/client";
|
|
1828
3894
|
export {
|
|
1829
|
-
|
|
3895
|
+
AddressType3 as AddressType,
|
|
1830
3896
|
BrowserSDK,
|
|
1831
3897
|
DebugCategory,
|
|
1832
3898
|
DebugLevel,
|
|
@@ -1837,6 +3903,7 @@ export {
|
|
|
1837
3903
|
getDeeplinkToPhantom,
|
|
1838
3904
|
getPlatformName,
|
|
1839
3905
|
isMobileDevice,
|
|
3906
|
+
isPhantomLoginAvailable,
|
|
1840
3907
|
parseBrowserFromUserAgent,
|
|
1841
3908
|
waitForPhantomExtension
|
|
1842
3909
|
};
|