@phantom/browser-sdk 0.2.2 → 0.3.1
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 +109 -21
- package/dist/index.d.ts +97 -26
- package/dist/index.js +519 -124
- package/dist/index.mjs +508 -123
- package/package.json +7 -4
package/dist/index.mjs
CHANGED
|
@@ -3,26 +3,121 @@ import { AddressType } from "@phantom/client";
|
|
|
3
3
|
import { createPhantom, createExtensionPlugin } from "@phantom/browser-injected-sdk";
|
|
4
4
|
import { createSolanaPlugin } from "@phantom/browser-injected-sdk/solana";
|
|
5
5
|
import { createEthereumPlugin } from "@phantom/browser-injected-sdk/ethereum";
|
|
6
|
+
|
|
7
|
+
// src/debug.ts
|
|
8
|
+
var DebugLevel = /* @__PURE__ */ ((DebugLevel2) => {
|
|
9
|
+
DebugLevel2[DebugLevel2["ERROR"] = 0] = "ERROR";
|
|
10
|
+
DebugLevel2[DebugLevel2["WARN"] = 1] = "WARN";
|
|
11
|
+
DebugLevel2[DebugLevel2["INFO"] = 2] = "INFO";
|
|
12
|
+
DebugLevel2[DebugLevel2["DEBUG"] = 3] = "DEBUG";
|
|
13
|
+
return DebugLevel2;
|
|
14
|
+
})(DebugLevel || {});
|
|
15
|
+
var Debug = class {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.level = 0 /* ERROR */;
|
|
18
|
+
this.enabled = false;
|
|
19
|
+
}
|
|
20
|
+
static getInstance() {
|
|
21
|
+
if (!Debug.instance) {
|
|
22
|
+
Debug.instance = new Debug();
|
|
23
|
+
}
|
|
24
|
+
return Debug.instance;
|
|
25
|
+
}
|
|
26
|
+
setCallback(callback) {
|
|
27
|
+
this.callback = callback;
|
|
28
|
+
}
|
|
29
|
+
setLevel(level) {
|
|
30
|
+
this.level = level;
|
|
31
|
+
}
|
|
32
|
+
enable() {
|
|
33
|
+
this.enabled = true;
|
|
34
|
+
}
|
|
35
|
+
disable() {
|
|
36
|
+
this.enabled = false;
|
|
37
|
+
}
|
|
38
|
+
writeLog(level, category, message, data) {
|
|
39
|
+
if (!this.enabled || level > this.level) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const debugMessage = {
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
level,
|
|
45
|
+
category,
|
|
46
|
+
message,
|
|
47
|
+
data
|
|
48
|
+
};
|
|
49
|
+
if (this.callback) {
|
|
50
|
+
this.callback(debugMessage);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
error(category, message, data) {
|
|
54
|
+
this.writeLog(0 /* ERROR */, category, message, data);
|
|
55
|
+
}
|
|
56
|
+
warn(category, message, data) {
|
|
57
|
+
this.writeLog(1 /* WARN */, category, message, data);
|
|
58
|
+
}
|
|
59
|
+
info(category, message, data) {
|
|
60
|
+
this.writeLog(2 /* INFO */, category, message, data);
|
|
61
|
+
}
|
|
62
|
+
debug(category, message, data) {
|
|
63
|
+
this.writeLog(3 /* DEBUG */, category, message, data);
|
|
64
|
+
}
|
|
65
|
+
log(category, message, data) {
|
|
66
|
+
this.writeLog(3 /* DEBUG */, category, message, data);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var debug = Debug.getInstance();
|
|
70
|
+
var DebugCategory = {
|
|
71
|
+
BROWSER_SDK: "BrowserSDK",
|
|
72
|
+
PROVIDER_MANAGER: "ProviderManager",
|
|
73
|
+
EMBEDDED_PROVIDER: "EmbeddedProvider",
|
|
74
|
+
INJECTED_PROVIDER: "InjectedProvider",
|
|
75
|
+
PHANTOM_CONNECT_AUTH: "PhantomConnectAuth",
|
|
76
|
+
JWT_AUTH: "JWTAuth",
|
|
77
|
+
STORAGE: "Storage",
|
|
78
|
+
SESSION: "Session"
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/providers/injected/index.ts
|
|
82
|
+
import { base64urlEncode } from "@phantom/base64url";
|
|
83
|
+
import { getExplorerUrl } from "@phantom/constants";
|
|
84
|
+
import bs58 from "bs58";
|
|
6
85
|
var InjectedProvider = class {
|
|
7
86
|
constructor(config) {
|
|
8
87
|
this.connected = false;
|
|
9
88
|
this.addresses = [];
|
|
89
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Initializing InjectedProvider", { config });
|
|
10
90
|
this.addressTypes = config.addressTypes || [AddressType.solana, AddressType.ethereum];
|
|
91
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Address types configured", { addressTypes: this.addressTypes });
|
|
11
92
|
const plugins = [createExtensionPlugin()];
|
|
12
93
|
if (this.addressTypes.includes(AddressType.solana)) {
|
|
13
94
|
plugins.push(createSolanaPlugin());
|
|
95
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana plugin added");
|
|
14
96
|
}
|
|
15
97
|
if (this.addressTypes.includes(AddressType.ethereum)) {
|
|
16
98
|
plugins.push(createEthereumPlugin());
|
|
99
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum plugin added");
|
|
17
100
|
}
|
|
101
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Creating Phantom instance with plugins", {
|
|
102
|
+
pluginCount: plugins.length
|
|
103
|
+
});
|
|
18
104
|
this.phantom = createPhantom({ plugins });
|
|
105
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "InjectedProvider initialized");
|
|
19
106
|
}
|
|
20
|
-
async connect() {
|
|
107
|
+
async connect(authOptions) {
|
|
108
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider connect", {
|
|
109
|
+
addressTypes: this.addressTypes,
|
|
110
|
+
authOptionsIgnored: !!authOptions
|
|
111
|
+
// Note: authOptions are ignored for injected provider
|
|
112
|
+
});
|
|
21
113
|
if (!this.phantom.extension.isInstalled()) {
|
|
114
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Phantom wallet extension not found");
|
|
22
115
|
throw new Error("Phantom wallet not found");
|
|
23
116
|
}
|
|
117
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Phantom extension detected");
|
|
24
118
|
const connectedAddresses = [];
|
|
25
119
|
if (this.addressTypes.includes(AddressType.solana)) {
|
|
120
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection");
|
|
26
121
|
try {
|
|
27
122
|
const publicKey = await this.phantom.solana.connect();
|
|
28
123
|
if (publicKey) {
|
|
@@ -30,9 +125,10 @@ var InjectedProvider = class {
|
|
|
30
125
|
addressType: AddressType.solana,
|
|
31
126
|
address: publicKey
|
|
32
127
|
});
|
|
128
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
|
|
33
129
|
}
|
|
34
130
|
} catch (err) {
|
|
35
|
-
|
|
131
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
36
132
|
}
|
|
37
133
|
}
|
|
38
134
|
if (this.addressTypes.includes(AddressType.ethereum)) {
|
|
@@ -47,7 +143,7 @@ var InjectedProvider = class {
|
|
|
47
143
|
);
|
|
48
144
|
}
|
|
49
145
|
} catch (err) {
|
|
50
|
-
|
|
146
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
51
147
|
}
|
|
52
148
|
}
|
|
53
149
|
if (connectedAddresses.length === 0) {
|
|
@@ -56,7 +152,8 @@ var InjectedProvider = class {
|
|
|
56
152
|
this.addresses = connectedAddresses;
|
|
57
153
|
this.connected = true;
|
|
58
154
|
return {
|
|
59
|
-
addresses: this.addresses
|
|
155
|
+
addresses: this.addresses,
|
|
156
|
+
status: "completed"
|
|
60
157
|
// walletId is not applicable for injected providers
|
|
61
158
|
};
|
|
62
159
|
}
|
|
@@ -83,18 +180,24 @@ var InjectedProvider = class {
|
|
|
83
180
|
throw new Error("Wallet not connected");
|
|
84
181
|
}
|
|
85
182
|
const networkPrefix = params.networkId.split(":")[0].toLowerCase();
|
|
183
|
+
let signatureResult;
|
|
86
184
|
if (networkPrefix === "solana") {
|
|
87
185
|
const { signature } = await this.phantom.solana.signMessage(new TextEncoder().encode(params.message));
|
|
88
|
-
|
|
186
|
+
signatureResult = bs58.encode(signature);
|
|
89
187
|
} else if (networkPrefix === "ethereum" || networkPrefix === "polygon" || networkPrefix === "eip155") {
|
|
90
188
|
const address = this.addresses.find((addr) => addr.addressType === AddressType.ethereum)?.address;
|
|
91
189
|
if (!address) {
|
|
92
190
|
throw new Error("No address available");
|
|
93
191
|
}
|
|
94
192
|
const signature = await this.phantom.ethereum.signPersonalMessage(params.message, address);
|
|
95
|
-
|
|
193
|
+
signatureResult = signature;
|
|
194
|
+
} else {
|
|
195
|
+
throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
|
|
96
196
|
}
|
|
97
|
-
|
|
197
|
+
return {
|
|
198
|
+
signature: signatureResult,
|
|
199
|
+
rawSignature: base64urlEncode(signatureResult)
|
|
200
|
+
};
|
|
98
201
|
}
|
|
99
202
|
async signAndSendTransaction(params) {
|
|
100
203
|
if (!this.connected) {
|
|
@@ -105,7 +208,9 @@ var InjectedProvider = class {
|
|
|
105
208
|
const transaction = params.transaction;
|
|
106
209
|
const result = await this.phantom.solana.signAndSendTransaction(transaction);
|
|
107
210
|
return {
|
|
108
|
-
|
|
211
|
+
hash: result.signature,
|
|
212
|
+
rawTransaction: base64urlEncode(result.signature),
|
|
213
|
+
blockExplorer: getExplorerUrl(params.networkId, "transaction", result.signature)
|
|
109
214
|
};
|
|
110
215
|
} else if (networkPrefix === "ethereum" || networkPrefix === "polygon" || networkPrefix === "eip155") {
|
|
111
216
|
const toHex = (value) => {
|
|
@@ -128,7 +233,9 @@ var InjectedProvider = class {
|
|
|
128
233
|
};
|
|
129
234
|
const txHash = await this.phantom.ethereum.sendTransaction(txRequest);
|
|
130
235
|
return {
|
|
131
|
-
|
|
236
|
+
hash: txHash,
|
|
237
|
+
rawTransaction: base64urlEncode(txHash),
|
|
238
|
+
blockExplorer: getExplorerUrl(params.networkId, "transaction", txHash)
|
|
132
239
|
};
|
|
133
240
|
}
|
|
134
241
|
throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
|
|
@@ -142,11 +249,11 @@ var InjectedProvider = class {
|
|
|
142
249
|
};
|
|
143
250
|
|
|
144
251
|
// src/providers/embedded/index.ts
|
|
145
|
-
import {
|
|
146
|
-
import {
|
|
252
|
+
import { EmbeddedProvider as CoreEmbeddedProvider } from "@phantom/embedded-provider-core";
|
|
253
|
+
import { IndexedDbStamper } from "@phantom/indexed-db-stamper";
|
|
147
254
|
|
|
148
|
-
// src/providers/embedded/storage.ts
|
|
149
|
-
var
|
|
255
|
+
// src/providers/embedded/adapters/storage.ts
|
|
256
|
+
var BrowserStorage = class {
|
|
150
257
|
constructor() {
|
|
151
258
|
this.dbName = "phantom-browser-sdk";
|
|
152
259
|
this.storeName = "sessions";
|
|
@@ -166,149 +273,339 @@ var IndexedDBStorage = class {
|
|
|
166
273
|
});
|
|
167
274
|
}
|
|
168
275
|
async getSession() {
|
|
276
|
+
debug.log(DebugCategory.STORAGE, "Getting session from IndexedDB");
|
|
169
277
|
const db = await this.getDB();
|
|
170
278
|
return new Promise((resolve, reject) => {
|
|
171
279
|
const transaction = db.transaction([this.storeName], "readonly");
|
|
172
280
|
const store = transaction.objectStore(this.storeName);
|
|
173
281
|
const request = store.get("currentSession");
|
|
174
|
-
request.onsuccess = () =>
|
|
175
|
-
|
|
282
|
+
request.onsuccess = () => {
|
|
283
|
+
const session = request.result || null;
|
|
284
|
+
debug.log(DebugCategory.STORAGE, "Retrieved session from IndexedDB", {
|
|
285
|
+
hasSession: !!session,
|
|
286
|
+
sessionId: session?.sessionId
|
|
287
|
+
});
|
|
288
|
+
resolve(session);
|
|
289
|
+
};
|
|
290
|
+
request.onerror = () => {
|
|
291
|
+
debug.error(DebugCategory.STORAGE, "Failed to get session from IndexedDB", { error: request.error });
|
|
292
|
+
reject(request.error);
|
|
293
|
+
};
|
|
176
294
|
});
|
|
177
295
|
}
|
|
178
296
|
async saveSession(session) {
|
|
297
|
+
debug.log(DebugCategory.STORAGE, "Saving session to IndexedDB", {
|
|
298
|
+
sessionId: session.sessionId,
|
|
299
|
+
walletId: session.walletId,
|
|
300
|
+
status: session.status
|
|
301
|
+
});
|
|
179
302
|
const db = await this.getDB();
|
|
180
303
|
return new Promise((resolve, reject) => {
|
|
181
304
|
const transaction = db.transaction([this.storeName], "readwrite");
|
|
182
305
|
const store = transaction.objectStore(this.storeName);
|
|
183
306
|
const request = store.put(session, "currentSession");
|
|
184
|
-
request.onsuccess = () =>
|
|
185
|
-
|
|
307
|
+
request.onsuccess = () => {
|
|
308
|
+
debug.log(DebugCategory.STORAGE, "Successfully saved session to IndexedDB");
|
|
309
|
+
resolve();
|
|
310
|
+
};
|
|
311
|
+
request.onerror = () => {
|
|
312
|
+
debug.error(DebugCategory.STORAGE, "Failed to save session to IndexedDB", { error: request.error });
|
|
313
|
+
reject(request.error);
|
|
314
|
+
};
|
|
186
315
|
});
|
|
187
316
|
}
|
|
188
317
|
async clearSession() {
|
|
318
|
+
debug.log(DebugCategory.STORAGE, "Clearing session from IndexedDB");
|
|
189
319
|
const db = await this.getDB();
|
|
190
320
|
return new Promise((resolve, reject) => {
|
|
191
321
|
const transaction = db.transaction([this.storeName], "readwrite");
|
|
192
322
|
const store = transaction.objectStore(this.storeName);
|
|
193
323
|
const request = store.delete("currentSession");
|
|
194
|
-
request.onsuccess = () =>
|
|
195
|
-
|
|
324
|
+
request.onsuccess = () => {
|
|
325
|
+
debug.log(DebugCategory.STORAGE, "Successfully cleared session from IndexedDB");
|
|
326
|
+
resolve();
|
|
327
|
+
};
|
|
328
|
+
request.onerror = () => {
|
|
329
|
+
debug.error(DebugCategory.STORAGE, "Failed to clear session from IndexedDB", { error: request.error });
|
|
330
|
+
reject(request.error);
|
|
331
|
+
};
|
|
196
332
|
});
|
|
197
333
|
}
|
|
198
334
|
};
|
|
199
335
|
|
|
200
|
-
// src/providers/embedded/
|
|
201
|
-
var
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return
|
|
205
|
-
walletId: `wallet-${Date.now()}`
|
|
206
|
-
};
|
|
336
|
+
// src/providers/embedded/adapters/url-params.ts
|
|
337
|
+
var BrowserURLParamsAccessor = class {
|
|
338
|
+
getParam(key) {
|
|
339
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
340
|
+
return urlParams.get(key);
|
|
207
341
|
}
|
|
208
342
|
};
|
|
343
|
+
var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
|
|
209
344
|
|
|
210
|
-
// src/
|
|
211
|
-
|
|
212
|
-
var
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
345
|
+
// src/constants.ts
|
|
346
|
+
var DEFAULT_AUTH_URL = "https://connect.phantom.app";
|
|
347
|
+
var DEFAULT_WALLET_API_URL = "https://api.phantom.app/v1/wallets";
|
|
348
|
+
|
|
349
|
+
// src/providers/embedded/adapters/auth.ts
|
|
350
|
+
var BrowserAuthProvider = class {
|
|
351
|
+
constructor(urlParamsAccessor) {
|
|
352
|
+
this.urlParamsAccessor = urlParamsAccessor;
|
|
353
|
+
}
|
|
354
|
+
authenticate(options) {
|
|
355
|
+
return new Promise((resolve) => {
|
|
356
|
+
if ("jwtToken" in options) {
|
|
357
|
+
throw new Error("JWT authentication should be handled by the core JWTAuth class");
|
|
358
|
+
}
|
|
359
|
+
const phantomOptions = options;
|
|
360
|
+
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Starting Phantom Connect authentication", {
|
|
361
|
+
organizationId: phantomOptions.organizationId,
|
|
362
|
+
parentOrganizationId: phantomOptions.parentOrganizationId,
|
|
363
|
+
provider: phantomOptions.provider,
|
|
364
|
+
authUrl: phantomOptions.authUrl,
|
|
365
|
+
hasCustomData: !!phantomOptions.customAuthData
|
|
227
366
|
});
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const authResult = await auth.authenticate({
|
|
241
|
-
iframeUrl: this.config.authUrl || "https://auth-flow.phantom.app",
|
|
242
|
-
organizationId,
|
|
243
|
-
parentOrganizationId: this.config.organizationId,
|
|
244
|
-
embeddedWalletType: this.config.embeddedWalletType
|
|
367
|
+
const baseUrl = phantomOptions.authUrl || DEFAULT_AUTH_URL;
|
|
368
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
369
|
+
const params = new URLSearchParams({
|
|
370
|
+
organization_id: phantomOptions.organizationId,
|
|
371
|
+
parent_organization_id: phantomOptions.parentOrganizationId,
|
|
372
|
+
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? window.location.href : ""),
|
|
373
|
+
session_id: phantomOptions.sessionId,
|
|
374
|
+
clear_previous_session: true.toString()
|
|
375
|
+
});
|
|
376
|
+
if (phantomOptions.provider) {
|
|
377
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
378
|
+
provider: phantomOptions.provider
|
|
245
379
|
});
|
|
246
|
-
|
|
380
|
+
params.append("provider", phantomOptions.provider);
|
|
247
381
|
} else {
|
|
248
|
-
|
|
249
|
-
|
|
382
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
|
|
383
|
+
params.append("provider", "google");
|
|
384
|
+
}
|
|
385
|
+
if (phantomOptions.customAuthData) {
|
|
386
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Adding custom auth data");
|
|
387
|
+
params.append("authData", JSON.stringify(phantomOptions.customAuthData));
|
|
388
|
+
}
|
|
389
|
+
const authContext = {
|
|
390
|
+
organizationId: phantomOptions.organizationId,
|
|
391
|
+
parentOrganizationId: phantomOptions.parentOrganizationId,
|
|
392
|
+
provider: phantomOptions.provider,
|
|
393
|
+
sessionId: phantomOptions.sessionId
|
|
394
|
+
};
|
|
395
|
+
sessionStorage.setItem("phantom-auth-context", JSON.stringify(authContext));
|
|
396
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Stored auth context in session storage", { authContext });
|
|
397
|
+
const authUrl = `${baseUrl}?${params.toString()}`;
|
|
398
|
+
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Redirecting to Phantom Connect", { authUrl });
|
|
399
|
+
window.location.href = authUrl;
|
|
400
|
+
resolve();
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
resumeAuthFromRedirect() {
|
|
404
|
+
try {
|
|
405
|
+
const walletId = this.urlParamsAccessor.getParam("wallet_id");
|
|
406
|
+
const sessionId = this.urlParamsAccessor.getParam("session_id");
|
|
407
|
+
const error = this.urlParamsAccessor.getParam("error");
|
|
408
|
+
const errorDescription = this.urlParamsAccessor.getParam("error_description");
|
|
409
|
+
if (error) {
|
|
410
|
+
const errorMsg = errorDescription || error;
|
|
411
|
+
switch (error) {
|
|
412
|
+
case "access_denied":
|
|
413
|
+
throw new Error(`Authentication cancelled: ${errorMsg}`);
|
|
414
|
+
case "invalid_request":
|
|
415
|
+
throw new Error(`Invalid authentication request: ${errorMsg}`);
|
|
416
|
+
case "server_error":
|
|
417
|
+
throw new Error(`Authentication server error: ${errorMsg}`);
|
|
418
|
+
case "temporarily_unavailable":
|
|
419
|
+
throw new Error(`Authentication service temporarily unavailable: ${errorMsg}`);
|
|
420
|
+
default:
|
|
421
|
+
throw new Error(`Authentication failed: ${errorMsg}`);
|
|
422
|
+
}
|
|
250
423
|
}
|
|
251
|
-
|
|
424
|
+
if (!walletId || !sessionId) {
|
|
425
|
+
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing auth parameters in URL", {
|
|
426
|
+
hasWalletId: !!walletId,
|
|
427
|
+
hasSessionId: !!sessionId
|
|
428
|
+
});
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
const contextStr = sessionStorage.getItem("phantom-auth-context");
|
|
432
|
+
let context = {};
|
|
433
|
+
if (contextStr) {
|
|
434
|
+
try {
|
|
435
|
+
context = JSON.parse(contextStr);
|
|
436
|
+
} catch (parseError) {
|
|
437
|
+
debug.warn(DebugCategory.PHANTOM_CONNECT_AUTH, "Failed to parse stored auth context", { error: parseError });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (context.sessionId && sessionId !== context.sessionId) {
|
|
441
|
+
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Session ID mismatch", {
|
|
442
|
+
urlSessionId: sessionId,
|
|
443
|
+
storedSessionId: context.sessionId
|
|
444
|
+
});
|
|
445
|
+
throw new Error("Session ID mismatch - possible session corruption or replay attack");
|
|
446
|
+
}
|
|
447
|
+
sessionStorage.removeItem("phantom-auth-context");
|
|
448
|
+
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Successfully resumed auth from redirect", {
|
|
252
449
|
walletId,
|
|
253
|
-
|
|
254
|
-
|
|
450
|
+
sessionId
|
|
451
|
+
});
|
|
452
|
+
return {
|
|
453
|
+
walletId,
|
|
454
|
+
userInfo: context
|
|
255
455
|
};
|
|
256
|
-
|
|
456
|
+
} catch (error) {
|
|
457
|
+
sessionStorage.removeItem("phantom-auth-context");
|
|
458
|
+
throw error;
|
|
257
459
|
}
|
|
258
|
-
const stamper = new ApiKeyStamper({
|
|
259
|
-
apiSecretKey: session.keypair.secretKey
|
|
260
|
-
});
|
|
261
|
-
this.client = new PhantomClient(
|
|
262
|
-
{
|
|
263
|
-
apiBaseUrl: this.config.apiBaseUrl,
|
|
264
|
-
organizationId: session.organizationId
|
|
265
|
-
},
|
|
266
|
-
stamper
|
|
267
|
-
);
|
|
268
|
-
this.walletId = session.walletId;
|
|
269
|
-
const addresses = await this.client.getWalletAddresses(session.walletId);
|
|
270
|
-
this.addresses = addresses.filter((addr) => this.config.addressTypes.some((type) => type === addr.addressType)).map((addr) => ({
|
|
271
|
-
addressType: addr.addressType,
|
|
272
|
-
address: addr.address
|
|
273
|
-
}));
|
|
274
|
-
return {
|
|
275
|
-
walletId: this.walletId,
|
|
276
|
-
addresses: this.addresses
|
|
277
|
-
};
|
|
278
460
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// src/providers/embedded/adapters/logger.ts
|
|
464
|
+
var BrowserLogger = class {
|
|
465
|
+
info(category, message, data) {
|
|
466
|
+
debug.info(category, message, data);
|
|
284
467
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
throw new Error("Not connected");
|
|
288
|
-
}
|
|
289
|
-
const parsedMessage = parseMessage(params.message);
|
|
290
|
-
return await this.client.signMessage({
|
|
291
|
-
walletId: this.walletId,
|
|
292
|
-
message: parsedMessage.base64url,
|
|
293
|
-
networkId: params.networkId
|
|
294
|
-
});
|
|
468
|
+
warn(category, message, data) {
|
|
469
|
+
debug.warn(category, message, data);
|
|
295
470
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
471
|
+
error(category, message, data) {
|
|
472
|
+
debug.error(category, message, data);
|
|
473
|
+
}
|
|
474
|
+
log(category, message, data) {
|
|
475
|
+
debug.log(category, message, data);
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// src/utils/browser-detection.ts
|
|
480
|
+
function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
|
|
481
|
+
let name = "unknown";
|
|
482
|
+
let version = "unknown";
|
|
483
|
+
if (!userAgent || typeof userAgent !== "string") {
|
|
484
|
+
return { name, version };
|
|
485
|
+
}
|
|
486
|
+
try {
|
|
487
|
+
if (userAgent.includes("Edg/")) {
|
|
488
|
+
name = "edge";
|
|
489
|
+
const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
|
|
490
|
+
if (match)
|
|
491
|
+
version = match[1].split(".")[0];
|
|
492
|
+
} else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
|
|
493
|
+
name = "opera";
|
|
494
|
+
const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
|
|
495
|
+
if (match)
|
|
496
|
+
version = match[1].split(".")[0];
|
|
497
|
+
} else if (userAgent.includes("SamsungBrowser/")) {
|
|
498
|
+
name = "samsung";
|
|
499
|
+
const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
|
|
500
|
+
if (match)
|
|
501
|
+
version = match[1].split(".")[0];
|
|
502
|
+
} else if (userAgent.includes("DuckDuckGo/")) {
|
|
503
|
+
name = "duckduckgo";
|
|
504
|
+
const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
|
|
505
|
+
if (match)
|
|
506
|
+
version = match[1].split(".")[0];
|
|
507
|
+
} else if (userAgent.includes("Chrome/") && hasBraveAPI) {
|
|
508
|
+
name = "brave";
|
|
509
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
510
|
+
if (match)
|
|
511
|
+
version = match[1].split(".")[0];
|
|
512
|
+
} else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
|
|
513
|
+
if (userAgent.includes("Chrome/")) {
|
|
514
|
+
name = "chrome-mobile";
|
|
515
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
516
|
+
if (match)
|
|
517
|
+
version = match[1].split(".")[0];
|
|
518
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
519
|
+
name = "firefox-mobile";
|
|
520
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
521
|
+
if (match)
|
|
522
|
+
version = match[1].split(".")[0];
|
|
523
|
+
} else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
|
|
524
|
+
name = "safari-mobile";
|
|
525
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
526
|
+
if (match)
|
|
527
|
+
version = match[1].split(".")[0];
|
|
528
|
+
} else {
|
|
529
|
+
name = "mobile";
|
|
530
|
+
}
|
|
531
|
+
} else if (userAgent.includes("Chrome/")) {
|
|
532
|
+
name = "chrome";
|
|
533
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
534
|
+
if (match)
|
|
535
|
+
version = match[1].split(".")[0];
|
|
536
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
537
|
+
name = "firefox";
|
|
538
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
539
|
+
if (match)
|
|
540
|
+
version = match[1].split(".")[0];
|
|
541
|
+
} else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
|
|
542
|
+
name = "safari";
|
|
543
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
544
|
+
if (match)
|
|
545
|
+
version = match[1].split(".")[0];
|
|
299
546
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
547
|
+
if (name === "unknown") {
|
|
548
|
+
const patterns = [
|
|
549
|
+
{ regex: /Chrome\/([0-9]+)/, name: "chrome" },
|
|
550
|
+
{ regex: /Firefox\/([0-9]+)/, name: "firefox" },
|
|
551
|
+
{ regex: /Safari\/([0-9]+)/, name: "safari" },
|
|
552
|
+
{ regex: /Edge\/([0-9]+)/, name: "edge" },
|
|
553
|
+
{ regex: /Opera\/([0-9]+)/, name: "opera" }
|
|
554
|
+
];
|
|
555
|
+
for (const pattern of patterns) {
|
|
556
|
+
const match = userAgent.match(pattern.regex);
|
|
557
|
+
if (match) {
|
|
558
|
+
name = pattern.name;
|
|
559
|
+
version = match[1];
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
} catch (error) {
|
|
306
565
|
}
|
|
307
|
-
|
|
308
|
-
|
|
566
|
+
return { name, version };
|
|
567
|
+
}
|
|
568
|
+
function detectBrowser() {
|
|
569
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
570
|
+
return { name: "unknown", version: "unknown" };
|
|
309
571
|
}
|
|
310
|
-
|
|
311
|
-
|
|
572
|
+
const userAgent = window.navigator.userAgent;
|
|
573
|
+
const hasBraveAPI = !!navigator.brave;
|
|
574
|
+
return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
|
|
575
|
+
}
|
|
576
|
+
function getPlatformName() {
|
|
577
|
+
const { name, version } = detectBrowser();
|
|
578
|
+
return version !== "unknown" ? `${name}-v${version}` : name;
|
|
579
|
+
}
|
|
580
|
+
function getBrowserDisplayName() {
|
|
581
|
+
const { name, version } = detectBrowser();
|
|
582
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
583
|
+
return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/providers/embedded/index.ts
|
|
587
|
+
var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
588
|
+
constructor(config) {
|
|
589
|
+
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
|
|
590
|
+
const urlParamsAccessor = new BrowserURLParamsAccessor();
|
|
591
|
+
const stamper = new IndexedDbStamper({
|
|
592
|
+
dbName: `phantom-embedded-sdk-${config.organizationId}`,
|
|
593
|
+
storeName: "crypto-keys",
|
|
594
|
+
keyName: "signing-key"
|
|
595
|
+
});
|
|
596
|
+
const platformName = getPlatformName();
|
|
597
|
+
const platform = {
|
|
598
|
+
storage: new BrowserStorage(),
|
|
599
|
+
authProvider: new BrowserAuthProvider(urlParamsAccessor),
|
|
600
|
+
urlParamsAccessor,
|
|
601
|
+
stamper,
|
|
602
|
+
name: platformName
|
|
603
|
+
// Use detected browser name and version for identification
|
|
604
|
+
};
|
|
605
|
+
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Detected platform", { platformName });
|
|
606
|
+
const logger = new BrowserLogger();
|
|
607
|
+
super(config, platform, logger);
|
|
608
|
+
debug.info(DebugCategory.EMBEDDED_PROVIDER, "Browser EmbeddedProvider initialized");
|
|
312
609
|
}
|
|
313
610
|
};
|
|
314
611
|
|
|
@@ -319,8 +616,13 @@ var ProviderManager = class {
|
|
|
319
616
|
this.currentProvider = null;
|
|
320
617
|
this.currentProviderKey = null;
|
|
321
618
|
this.walletId = null;
|
|
619
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Initializing ProviderManager", { config });
|
|
322
620
|
this.config = config;
|
|
621
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Setting default provider");
|
|
323
622
|
this.setDefaultProvider();
|
|
623
|
+
debug.info(DebugCategory.PROVIDER_MANAGER, "ProviderManager initialized", {
|
|
624
|
+
currentProviderKey: this.currentProviderKey
|
|
625
|
+
});
|
|
324
626
|
}
|
|
325
627
|
/**
|
|
326
628
|
* Switch to a different provider type
|
|
@@ -361,13 +663,27 @@ var ProviderManager = class {
|
|
|
361
663
|
/**
|
|
362
664
|
* Connect using the current provider
|
|
363
665
|
*/
|
|
364
|
-
async connect() {
|
|
666
|
+
async connect(authOptions) {
|
|
667
|
+
debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
|
|
668
|
+
currentProviderKey: this.currentProviderKey,
|
|
669
|
+
authOptions: authOptions ? { provider: authOptions.provider, hasJwtToken: !!authOptions.jwtToken } : void 0
|
|
670
|
+
});
|
|
365
671
|
if (!this.currentProvider) {
|
|
672
|
+
debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
|
|
366
673
|
throw new Error("No provider selected");
|
|
367
674
|
}
|
|
368
|
-
|
|
675
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Delegating to provider connect method");
|
|
676
|
+
const result = await this.currentProvider.connect(authOptions);
|
|
369
677
|
this.walletId = result.walletId || null;
|
|
678
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Connection successful, saving preferences", {
|
|
679
|
+
walletId: this.walletId,
|
|
680
|
+
addressCount: result.addresses?.length || 0
|
|
681
|
+
});
|
|
370
682
|
this.saveProviderPreference();
|
|
683
|
+
debug.info(DebugCategory.PROVIDER_MANAGER, "Connect completed", {
|
|
684
|
+
walletId: this.walletId,
|
|
685
|
+
addresses: result.addresses
|
|
686
|
+
});
|
|
371
687
|
return result;
|
|
372
688
|
}
|
|
373
689
|
/**
|
|
@@ -447,7 +763,7 @@ var ProviderManager = class {
|
|
|
447
763
|
provider = new EmbeddedProvider({
|
|
448
764
|
apiBaseUrl: this.config.apiBaseUrl,
|
|
449
765
|
organizationId: this.config.organizationId,
|
|
450
|
-
|
|
766
|
+
authOptions: this.config.authOptions,
|
|
451
767
|
embeddedWalletType: embeddedWalletType || "app-wallet",
|
|
452
768
|
addressTypes: this.config.addressTypes || [],
|
|
453
769
|
solanaProvider: this.config.solanaProvider || "web3js"
|
|
@@ -501,37 +817,78 @@ var ProviderManager = class {
|
|
|
501
817
|
import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
|
|
502
818
|
var BrowserSDK = class {
|
|
503
819
|
constructor(config) {
|
|
820
|
+
if (config.debug?.enabled) {
|
|
821
|
+
debug.enable();
|
|
822
|
+
if (config.debug.level !== void 0) {
|
|
823
|
+
debug.setLevel(config.debug.level);
|
|
824
|
+
}
|
|
825
|
+
if (config.debug.callback) {
|
|
826
|
+
debug.setCallback(config.debug.callback);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
debug.info(DebugCategory.BROWSER_SDK, "Initializing BrowserSDK", {
|
|
830
|
+
providerType: config.providerType,
|
|
831
|
+
embeddedWalletType: config.embeddedWalletType,
|
|
832
|
+
addressTypes: config.addressTypes,
|
|
833
|
+
debugEnabled: config.debug?.enabled
|
|
834
|
+
});
|
|
504
835
|
if (!["injected", "embedded"].includes(config.providerType)) {
|
|
836
|
+
debug.error(DebugCategory.BROWSER_SDK, "Invalid providerType", { providerType: config.providerType });
|
|
505
837
|
throw new Error(`Invalid providerType: ${config.providerType}. Must be "injected" or "embedded".`);
|
|
506
838
|
}
|
|
507
839
|
const embeddedWalletType = config.embeddedWalletType || "app-wallet";
|
|
508
840
|
if (config.providerType === "embedded" && !["app-wallet", "user-wallet"].includes(embeddedWalletType)) {
|
|
841
|
+
debug.error(DebugCategory.BROWSER_SDK, "Invalid embeddedWalletType", {
|
|
842
|
+
embeddedWalletType: config.embeddedWalletType
|
|
843
|
+
});
|
|
509
844
|
throw new Error(
|
|
510
845
|
`Invalid embeddedWalletType: ${config.embeddedWalletType}. Must be "app-wallet" or "user-wallet".`
|
|
511
846
|
);
|
|
512
847
|
}
|
|
513
848
|
config.embeddedWalletType = embeddedWalletType;
|
|
514
849
|
this.config = config;
|
|
850
|
+
debug.log(DebugCategory.BROWSER_SDK, "Creating ProviderManager", { config });
|
|
515
851
|
this.providerManager = new ProviderManager(config);
|
|
852
|
+
debug.info(DebugCategory.BROWSER_SDK, "BrowserSDK initialized successfully");
|
|
516
853
|
}
|
|
517
854
|
/**
|
|
518
855
|
* Connect to the wallet with optional provider switching
|
|
519
856
|
*/
|
|
520
857
|
async connect(options) {
|
|
858
|
+
debug.info(DebugCategory.BROWSER_SDK, "Starting connect process", { options });
|
|
521
859
|
if (options?.providerType) {
|
|
860
|
+
debug.log(DebugCategory.BROWSER_SDK, "Provider switch requested", {
|
|
861
|
+
providerType: options.providerType,
|
|
862
|
+
embeddedWalletType: options.embeddedWalletType
|
|
863
|
+
});
|
|
522
864
|
if (!["injected", "embedded"].includes(options.providerType)) {
|
|
865
|
+
debug.error(DebugCategory.BROWSER_SDK, "Invalid providerType in connect options", {
|
|
866
|
+
providerType: options.providerType
|
|
867
|
+
});
|
|
523
868
|
throw new Error(`Invalid providerType: ${options.providerType}. Must be "injected" or "embedded".`);
|
|
524
869
|
}
|
|
525
870
|
if (options.embeddedWalletType && !["app-wallet", "user-wallet"].includes(options.embeddedWalletType)) {
|
|
871
|
+
debug.error(DebugCategory.BROWSER_SDK, "Invalid embeddedWalletType in connect options", {
|
|
872
|
+
embeddedWalletType: options.embeddedWalletType
|
|
873
|
+
});
|
|
526
874
|
throw new Error(
|
|
527
875
|
`Invalid embeddedWalletType: ${options.embeddedWalletType}. Must be "app-wallet" or "user-wallet".`
|
|
528
876
|
);
|
|
529
877
|
}
|
|
878
|
+
debug.log(DebugCategory.BROWSER_SDK, "Switching provider", {
|
|
879
|
+
providerType: options.providerType,
|
|
880
|
+
embeddedWalletType: options.embeddedWalletType
|
|
881
|
+
});
|
|
530
882
|
await this.providerManager.switchProvider(options.providerType, {
|
|
531
883
|
embeddedWalletType: options.embeddedWalletType
|
|
532
884
|
});
|
|
533
885
|
}
|
|
534
|
-
|
|
886
|
+
debug.log(DebugCategory.BROWSER_SDK, "Delegating to ProviderManager.connect", {
|
|
887
|
+
authOptions: options?.authOptions
|
|
888
|
+
});
|
|
889
|
+
const result = await this.providerManager.connect(options?.authOptions);
|
|
890
|
+
debug.info(DebugCategory.BROWSER_SDK, "Connect completed successfully", result);
|
|
891
|
+
return result;
|
|
535
892
|
}
|
|
536
893
|
/**
|
|
537
894
|
* Switch to a different provider type
|
|
@@ -582,7 +939,17 @@ var BrowserSDK = class {
|
|
|
582
939
|
* @returns Signature string
|
|
583
940
|
*/
|
|
584
941
|
async signMessage(params) {
|
|
585
|
-
|
|
942
|
+
debug.info(DebugCategory.BROWSER_SDK, "Signing message", {
|
|
943
|
+
message: params.message,
|
|
944
|
+
networkId: params.networkId
|
|
945
|
+
});
|
|
946
|
+
const result = await this.providerManager.signMessage(params);
|
|
947
|
+
debug.info(DebugCategory.BROWSER_SDK, "Message signed successfully", {
|
|
948
|
+
message: params.message,
|
|
949
|
+
networkId: params.networkId,
|
|
950
|
+
result
|
|
951
|
+
});
|
|
952
|
+
return result;
|
|
586
953
|
}
|
|
587
954
|
/**
|
|
588
955
|
* Sign and send a transaction
|
|
@@ -590,7 +957,15 @@ var BrowserSDK = class {
|
|
|
590
957
|
* @returns Transaction result
|
|
591
958
|
*/
|
|
592
959
|
async signAndSendTransaction(params) {
|
|
593
|
-
|
|
960
|
+
debug.info(DebugCategory.BROWSER_SDK, "Signing and sending transaction", {
|
|
961
|
+
networkId: params.networkId
|
|
962
|
+
});
|
|
963
|
+
const result = await this.providerManager.signAndSendTransaction(params);
|
|
964
|
+
debug.info(DebugCategory.BROWSER_SDK, "Transaction signed and sent successfully", {
|
|
965
|
+
networkId: params.networkId,
|
|
966
|
+
result
|
|
967
|
+
});
|
|
968
|
+
return result;
|
|
594
969
|
}
|
|
595
970
|
/**
|
|
596
971
|
* Get wallet addresses
|
|
@@ -639,9 +1014,19 @@ var BrowserSDK = class {
|
|
|
639
1014
|
};
|
|
640
1015
|
|
|
641
1016
|
// src/index.ts
|
|
642
|
-
import { NetworkId
|
|
1017
|
+
import { NetworkId } from "@phantom/constants";
|
|
1018
|
+
import { AddressType as AddressType2 } from "@phantom/client";
|
|
643
1019
|
export {
|
|
644
1020
|
AddressType2 as AddressType,
|
|
645
1021
|
BrowserSDK,
|
|
646
|
-
|
|
1022
|
+
DEFAULT_AUTH_URL,
|
|
1023
|
+
DEFAULT_WALLET_API_URL,
|
|
1024
|
+
DebugCategory,
|
|
1025
|
+
DebugLevel,
|
|
1026
|
+
NetworkId,
|
|
1027
|
+
debug,
|
|
1028
|
+
detectBrowser,
|
|
1029
|
+
getBrowserDisplayName,
|
|
1030
|
+
getPlatformName,
|
|
1031
|
+
parseBrowserFromUserAgent
|
|
647
1032
|
};
|