@phantom/browser-sdk 0.2.3 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,7 +37,7 @@ import { BrowserSDK, AddressType, NetworkId } from "@phantom/browser-sdk";
37
37
  const sdk = new BrowserSDK({
38
38
  providerType: "embedded",
39
39
  addressTypes: [AddressType.solana, AddressType.ethereum],
40
- apiBaseUrl: "https://api.phantom.com",
40
+ apiBaseUrl: "https://api.phantom.app/v1/wallets",
41
41
  organizationId: "your-org-id",
42
42
  });
43
43
 
@@ -80,13 +80,19 @@ Creates a non-custodial wallet embedded in your application. Requires API config
80
80
  const sdk = new BrowserSDK({
81
81
  providerType: "embedded",
82
82
  addressTypes: [AddressType.solana, AddressType.ethereum],
83
- apiBaseUrl: "https://api.phantom.com",
83
+ apiBaseUrl: "https://api.phantom.app/v1/wallets",
84
84
  organizationId: "your-org-id",
85
85
  embeddedWalletType: "app-wallet", // or 'user-wallet'
86
86
  authOptions: {
87
87
  authUrl: "https://auth.phantom.app", // optional, defaults to "https://connect.phantom.app"
88
88
  redirectUrl: "https://yourapp.com/callback", // optional, defaults to current page
89
89
  },
90
+ appName: "My DApp", // optional, for branding
91
+ appLogo: "https://myapp.com/logo.png", // optional, for branding
92
+ debug: {
93
+ enabled: true, // optional, enable debug logging
94
+ level: "info", // optional, debug level
95
+ },
90
96
  });
91
97
  ```
92
98
 
@@ -169,9 +175,11 @@ new BrowserSDK(config: BrowserSDKConfig)
169
175
  ```typescript
170
176
  interface BrowserSDKConfig {
171
177
  providerType: "injected" | "embedded";
178
+ appName?: string; // Optional app name for branding
179
+ appLogo?: string; // Optional app logo URL for branding
180
+ addressTypes?: AddressType[]; // Networks to enable (e.g., [AddressType.solana])
172
181
 
173
182
  // Required for embedded provider only
174
- addressTypes?: AddressType[]; // Networks to enable
175
183
  apiBaseUrl?: string; // Phantom API base URL
176
184
  organizationId?: string; // Your organization ID
177
185
  authOptions?: {
@@ -180,6 +188,13 @@ interface BrowserSDKConfig {
180
188
  };
181
189
  embeddedWalletType?: "app-wallet" | "user-wallet"; // Wallet type
182
190
  solanaProvider?: "web3js" | "kit"; // Solana library choice (default: 'web3js')
191
+
192
+ // Debug options
193
+ debug?: {
194
+ enabled?: boolean; // Enable debug logging
195
+ level?: "info" | "warn" | "error"; // Debug level
196
+ callback?: (level: string, message: string, data?: any) => void; // Custom debug callback
197
+ };
183
198
  }
184
199
  ```
185
200
 
@@ -237,7 +252,6 @@ const result = await sdk.connect({
237
252
  **Authentication Flow Types:**
238
253
 
239
254
  1. **Phantom Connect (Redirect-based)**: Used when `provider` is undefined, `"google"`, or `"apple"`
240
-
241
255
  - Redirects to `https://connect.phantom.app` (or custom `authOptions.authUrl` from config)
242
256
  - Handles OAuth flow with selected provider
243
257
  - Returns to your app with authentication result using `authOptions.redirectUrl` or current page
@@ -317,17 +331,35 @@ npm install @solana/web3.js
317
331
  ```
318
332
 
319
333
  ```typescript
320
- import { Transaction, SystemProgram, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
334
+ import {
335
+ VersionedTransaction,
336
+ TransactionMessage,
337
+ SystemProgram,
338
+ PublicKey,
339
+ LAMPORTS_PER_SOL,
340
+ Connection,
341
+ } from "@solana/web3.js";
321
342
  import { BrowserSDK, NetworkId } from "@phantom/browser-sdk";
322
343
 
323
- // Create native Solana transaction
324
- const transaction = new Transaction().add(
325
- SystemProgram.transfer({
326
- fromPubkey: new PublicKey(fromAddress),
327
- toPubkey: new PublicKey(toAddress),
328
- lamports: 0.001 * LAMPORTS_PER_SOL,
329
- }),
330
- );
344
+ // Get recent blockhash
345
+ const connection = new Connection("https://api.mainnet-beta.solana.com");
346
+ const { blockhash } = await connection.getLatestBlockhash();
347
+
348
+ // Create transfer instruction
349
+ const transferInstruction = SystemProgram.transfer({
350
+ fromPubkey: new PublicKey(fromAddress),
351
+ toPubkey: new PublicKey(toAddress),
352
+ lamports: 0.001 * LAMPORTS_PER_SOL,
353
+ });
354
+
355
+ // Create VersionedTransaction
356
+ const messageV0 = new TransactionMessage({
357
+ payerKey: new PublicKey(fromAddress),
358
+ recentBlockhash: blockhash,
359
+ instructions: [transferInstruction],
360
+ }).compileToV0Message();
361
+
362
+ const transaction = new VersionedTransaction(messageV0);
331
363
 
332
364
  // Send native transaction object - no encoding needed!
333
365
  const result = await sdk.signAndSendTransaction({
@@ -553,19 +585,31 @@ import { BrowserSDK, AddressType } from "@phantom/browser-sdk";
553
585
 
554
586
  const sdk = new BrowserSDK({
555
587
  addressTypes: [AddressType.solana, AddressType.ethereum, AddressType.sui],
556
- apiBaseUrl: "https://api.phantom.com",
588
+ apiBaseUrl: "https://api.phantom.app/v1/wallets",
557
589
  organizationId: "your-org-id",
558
590
  });
559
591
 
560
592
  class MultiChainWallet {
561
593
  async sendSolana(amount: number, recipient: string) {
562
- const transaction = new Transaction().add(
563
- SystemProgram.transfer({
564
- fromPubkey: new PublicKey(this.solanaAddress),
565
- toPubkey: new PublicKey(recipient),
566
- lamports: amount * LAMPORTS_PER_SOL,
567
- }),
568
- );
594
+ // Get recent blockhash
595
+ const connection = new Connection("https://api.mainnet-beta.solana.com");
596
+ const { blockhash } = await connection.getLatestBlockhash();
597
+
598
+ // Create transfer instruction
599
+ const transferInstruction = SystemProgram.transfer({
600
+ fromPubkey: new PublicKey(this.solanaAddress),
601
+ toPubkey: new PublicKey(recipient),
602
+ lamports: amount * LAMPORTS_PER_SOL,
603
+ });
604
+
605
+ // Create VersionedTransaction
606
+ const messageV0 = new TransactionMessage({
607
+ payerKey: new PublicKey(this.solanaAddress),
608
+ recentBlockhash: blockhash,
609
+ instructions: [transferInstruction],
610
+ }).compileToV0Message();
611
+
612
+ const transaction = new VersionedTransaction(messageV0);
569
613
 
570
614
  return await sdk.signAndSendTransaction({
571
615
  networkId: NetworkId.SOLANA_MAINNET,
@@ -633,12 +677,12 @@ try {
633
677
 
634
678
  2. **Install dependencies based on enabled networks**:
635
679
 
636
- | AddressType | Required Dependencies | Bundle Size |
637
- | --------------------------- | ---------------------------------- | ----------- |
638
- | `AddressType.solana` | `@solana/web3.js` OR `@solana/kit` | ~250KB |
639
- | `AddressType.ethereum` | `viem` | ~300KB |
640
- | `AddressType.bitcoinSegwit` | `bitcoinjs-lib` | ~200KB |
641
- | `AddressType.sui` | `@mysten/sui.js` | ~250KB |
680
+ | AddressType | Required Dependencies |
681
+ | --------------------------- | ---------------------------------- |
682
+ | `AddressType.solana` | `@solana/web3.js` OR `@solana/kit` |
683
+ | `AddressType.ethereum` | `viem` |
684
+ | `AddressType.bitcoinSegwit` | `bitcoinjs-lib` |
685
+ | `AddressType.sui` | `@mysten/sui.js` |
642
686
 
643
687
  **Example package.json for Solana + Ethereum (using @solana/web3.js)**:
644
688
 
@@ -713,62 +757,3 @@ try {
713
757
  }
714
758
  }
715
759
  ```
716
-
717
- 3. **Monitor bundle size**:
718
- ```bash
719
- # Analyze your bundle
720
- npx webpack-bundle-analyzer dist/main.js
721
- ```
722
-
723
- ## Server Setup for Embedded Wallets
724
-
725
- For embedded wallets, you need to set up a backend endpoint. Add the `serverUrl` parameter to your SDK configuration:
726
-
727
- ```typescript
728
- const sdk = new BrowserSDK({
729
- providerType: "embedded",
730
- addressTypes: [AddressType.solana],
731
- apiBaseUrl: "https://api.phantom.com",
732
- organizationId: "your-org-id",
733
- serverUrl: "http://localhost:3000/api",
734
- });
735
- ```
736
-
737
- ### Required Backend Endpoint
738
-
739
- Your backend needs an endpoint that uses the server-sdk:
740
-
741
- ```javascript
742
- // server.js
743
- const express = require("express");
744
- const { ServerSDK } = require("@phantom/server-sdk");
745
-
746
- const app = express();
747
- app.use(express.json());
748
-
749
- const serverSDK = new ServerSDK({
750
- organizationId: process.env.ORGANIZATION_ID,
751
- apiPrivateKey: process.env.PRIVATE_KEY,
752
- apiBaseUrl: process.env.API_URL,
753
- });
754
-
755
- app.post("/api/organizations", async (req, res) => {
756
- try {
757
- const { userId } = req.body;
758
-
759
- if (!userId) {
760
- return res.status(400).json({ error: "userId is required" });
761
- }
762
-
763
- const organization = await serverSDK.getOrCreateChildOrganizationByTag({
764
- tag: userId,
765
- });
766
-
767
- res.json({ organizationId: organization.id });
768
- } catch (error) {
769
- res.status(500).json({ error: "Failed to process request" });
770
- }
771
- });
772
-
773
- app.listen(3000);
774
- ```
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { AddressType } from '@phantom/client';
2
- export { AddressType, NetworkId } from '@phantom/client';
3
- import { AuthOptions, ConnectResult, SignMessageParams, SignAndSendTransactionParams, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
4
- export { AuthOptions, ConnectResult, SignAndSendTransactionParams, SignMessageParams, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
2
+ export { AddressType } from '@phantom/client';
3
+ import { AuthOptions, ConnectResult, SignMessageParams, SignMessageResult, SignAndSendTransactionParams, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
4
+ export { AuthOptions, ConnectResult, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
5
+ export { NetworkId } from '@phantom/constants';
5
6
 
6
7
  declare enum DebugLevel {
7
8
  ERROR = 0,
@@ -50,6 +51,7 @@ declare const DebugCategory: {
50
51
  interface BrowserSDKConfig {
51
52
  providerType: "injected" | "embedded" | (string & Record<never, never>);
52
53
  appName?: string;
54
+ appLogo?: string;
53
55
  addressTypes?: AddressType[];
54
56
  apiBaseUrl?: string;
55
57
  organizationId?: string;
@@ -59,25 +61,17 @@ interface BrowserSDKConfig {
59
61
  };
60
62
  embeddedWalletType?: "app-wallet" | "user-wallet" | (string & Record<never, never>);
61
63
  solanaProvider?: "web3js" | "kit";
62
- serverUrl?: string;
63
64
  debug?: {
64
65
  enabled?: boolean;
65
66
  level?: DebugLevel;
66
67
  callback?: DebugCallback;
67
68
  };
68
69
  }
69
- interface CreateUserOrganizationParams {
70
- userId: string;
71
- [key: string]: any;
72
- }
73
- interface CreateUserOrganizationResult {
74
- organizationId: string;
75
- }
76
70
 
77
71
  interface Provider {
78
72
  connect(authOptions?: AuthOptions): Promise<ConnectResult>;
79
73
  disconnect(): Promise<void>;
80
- signMessage(params: SignMessageParams): Promise<string>;
74
+ signMessage(params: SignMessageParams): Promise<SignMessageResult>;
81
75
  signAndSendTransaction(params: SignAndSendTransactionParams): Promise<SignedTransaction>;
82
76
  getAddresses(): WalletAddress[];
83
77
  isConnected(): boolean;
@@ -93,7 +87,6 @@ interface SwitchProviderOptions {
93
87
 
94
88
  declare class BrowserSDK {
95
89
  private providerManager;
96
- private config;
97
90
  constructor(config: BrowserSDKConfig);
98
91
  /**
99
92
  * Connect to the wallet with optional provider switching
@@ -125,7 +118,7 @@ declare class BrowserSDK {
125
118
  * @param networkId - Network identifier
126
119
  * @returns Signature string
127
120
  */
128
- signMessage(params: SignMessageParams): Promise<string>;
121
+ signMessage(params: SignMessageParams): Promise<SignMessageResult>;
129
122
  /**
130
123
  * Sign and send a transaction
131
124
  * @param params - Transaction parameters with native transaction object
@@ -144,12 +137,6 @@ declare class BrowserSDK {
144
137
  * Get the current wallet ID (for embedded wallets)
145
138
  */
146
139
  getWalletId(): string | null;
147
- /**
148
- * Create a user organization via your backend API
149
- * @param params - Parameters including userId and any additional options
150
- * @returns Organization creation result with organizationId
151
- */
152
- createUserOrganization(params: CreateUserOrganizationParams): Promise<CreateUserOrganizationResult>;
153
140
  }
154
141
 
155
142
  /**
@@ -158,4 +145,31 @@ declare class BrowserSDK {
158
145
  declare const DEFAULT_AUTH_URL = "https://connect.phantom.app";
159
146
  declare const DEFAULT_WALLET_API_URL = "https://api.phantom.app/v1/wallets";
160
147
 
161
- export { BrowserSDK, BrowserSDKConfig, CreateUserOrganizationParams, CreateUserOrganizationResult, DEFAULT_AUTH_URL, DEFAULT_WALLET_API_URL, DebugCallback, DebugCategory, DebugLevel, DebugMessage, Provider, debug };
148
+ /**
149
+ * Browser detection utility to identify browser name and version
150
+ */
151
+ interface BrowserInfo {
152
+ name: string;
153
+ version: string;
154
+ }
155
+ /**
156
+ * Parse browser information from a user agent string
157
+ * This is the core parsing logic that can be unit tested independently
158
+ */
159
+ declare function parseBrowserFromUserAgent(userAgent: string, hasBraveAPI?: boolean): BrowserInfo;
160
+ /**
161
+ * Detect the current browser and version from the user agent string
162
+ */
163
+ declare function detectBrowser(): BrowserInfo;
164
+ /**
165
+ * Get a formatted platform name for use in authenticator names
166
+ * Format: "browsername-v123" (e.g., "chrome-v120", "firefox-v119")
167
+ */
168
+ declare function getPlatformName(): string;
169
+ /**
170
+ * Get detailed browser information as a string
171
+ * Format: "Chrome 120.0" or "Firefox 119.0"
172
+ */
173
+ declare function getBrowserDisplayName(): string;
174
+
175
+ export { BrowserInfo, BrowserSDK, BrowserSDKConfig, DEFAULT_AUTH_URL, DEFAULT_WALLET_API_URL, DebugCallback, DebugCategory, DebugLevel, DebugMessage, Provider, debug, detectBrowser, getBrowserDisplayName, getPlatformName, parseBrowserFromUserAgent };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -26,8 +36,12 @@ __export(src_exports, {
26
36
  DEFAULT_WALLET_API_URL: () => DEFAULT_WALLET_API_URL,
27
37
  DebugCategory: () => DebugCategory,
28
38
  DebugLevel: () => DebugLevel,
29
- NetworkId: () => import_client2.NetworkId,
30
- debug: () => debug
39
+ NetworkId: () => import_constants3.NetworkId,
40
+ debug: () => debug,
41
+ detectBrowser: () => detectBrowser,
42
+ getBrowserDisplayName: () => getBrowserDisplayName,
43
+ getPlatformName: () => getPlatformName,
44
+ parseBrowserFromUserAgent: () => parseBrowserFromUserAgent
31
45
  });
32
46
  module.exports = __toCommonJS(src_exports);
33
47
 
@@ -112,6 +126,9 @@ var DebugCategory = {
112
126
  };
113
127
 
114
128
  // src/providers/injected/index.ts
129
+ var import_base64url = require("@phantom/base64url");
130
+ var import_constants = require("@phantom/constants");
131
+ var import_bs58 = __toESM(require("bs58"));
115
132
  var InjectedProvider = class {
116
133
  constructor(config) {
117
134
  this.connected = false;
@@ -210,18 +227,24 @@ var InjectedProvider = class {
210
227
  throw new Error("Wallet not connected");
211
228
  }
212
229
  const networkPrefix = params.networkId.split(":")[0].toLowerCase();
230
+ let signatureResult;
213
231
  if (networkPrefix === "solana") {
214
232
  const { signature } = await this.phantom.solana.signMessage(new TextEncoder().encode(params.message));
215
- return Array.from(signature).map((b) => b.toString(16).padStart(2, "0")).join("");
233
+ signatureResult = import_bs58.default.encode(signature);
216
234
  } else if (networkPrefix === "ethereum" || networkPrefix === "polygon" || networkPrefix === "eip155") {
217
235
  const address = this.addresses.find((addr) => addr.addressType === import_client.AddressType.ethereum)?.address;
218
236
  if (!address) {
219
237
  throw new Error("No address available");
220
238
  }
221
239
  const signature = await this.phantom.ethereum.signPersonalMessage(params.message, address);
222
- return signature;
240
+ signatureResult = signature;
241
+ } else {
242
+ throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
223
243
  }
224
- throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
244
+ return {
245
+ signature: signatureResult,
246
+ rawSignature: (0, import_base64url.base64urlEncode)(signatureResult)
247
+ };
225
248
  }
226
249
  async signAndSendTransaction(params) {
227
250
  if (!this.connected) {
@@ -232,7 +255,9 @@ var InjectedProvider = class {
232
255
  const transaction = params.transaction;
233
256
  const result = await this.phantom.solana.signAndSendTransaction(transaction);
234
257
  return {
235
- rawTransaction: result.signature
258
+ hash: result.signature,
259
+ rawTransaction: (0, import_base64url.base64urlEncode)(result.signature),
260
+ blockExplorer: (0, import_constants.getExplorerUrl)(params.networkId, "transaction", result.signature)
236
261
  };
237
262
  } else if (networkPrefix === "ethereum" || networkPrefix === "polygon" || networkPrefix === "eip155") {
238
263
  const toHex = (value) => {
@@ -255,7 +280,9 @@ var InjectedProvider = class {
255
280
  };
256
281
  const txHash = await this.phantom.ethereum.sendTransaction(txRequest);
257
282
  return {
258
- rawTransaction: txHash
283
+ hash: txHash,
284
+ rawTransaction: (0, import_base64url.base64urlEncode)(txHash),
285
+ blockExplorer: (0, import_constants.getExplorerUrl)(params.networkId, "transaction", txHash)
259
286
  };
260
287
  }
261
288
  throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
@@ -270,6 +297,7 @@ var InjectedProvider = class {
270
297
 
271
298
  // src/providers/embedded/index.ts
272
299
  var import_embedded_provider_core = require("@phantom/embedded-provider-core");
300
+ var import_indexed_db_stamper = require("@phantom/indexed-db-stamper");
273
301
 
274
302
  // src/providers/embedded/adapters/storage.ts
275
303
  var BrowserStorage = class {
@@ -390,7 +418,11 @@ var BrowserAuthProvider = class {
390
418
  parent_organization_id: phantomOptions.parentOrganizationId,
391
419
  redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? window.location.href : ""),
392
420
  session_id: phantomOptions.sessionId,
393
- clear_previous_session: true.toString()
421
+ clear_previous_session: true.toString(),
422
+ app_name: phantomOptions.appName || "",
423
+ // Optional app name
424
+ app_logo: phantomOptions.appLogo || ""
425
+ // Optional app logo URL
394
426
  });
395
427
  if (phantomOptions.provider) {
396
428
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
@@ -495,16 +527,133 @@ var BrowserLogger = class {
495
527
  }
496
528
  };
497
529
 
530
+ // src/utils/browser-detection.ts
531
+ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
532
+ let name = "unknown";
533
+ let version = "unknown";
534
+ if (!userAgent || typeof userAgent !== "string") {
535
+ return { name, version };
536
+ }
537
+ try {
538
+ if (userAgent.includes("Edg/")) {
539
+ name = "edge";
540
+ const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
541
+ if (match)
542
+ version = match[1].split(".")[0];
543
+ } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
544
+ name = "opera";
545
+ const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
546
+ if (match)
547
+ version = match[1].split(".")[0];
548
+ } else if (userAgent.includes("SamsungBrowser/")) {
549
+ name = "samsung";
550
+ const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
551
+ if (match)
552
+ version = match[1].split(".")[0];
553
+ } else if (userAgent.includes("DuckDuckGo/")) {
554
+ name = "duckduckgo";
555
+ const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
556
+ if (match)
557
+ version = match[1].split(".")[0];
558
+ } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
559
+ name = "brave";
560
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
561
+ if (match)
562
+ version = match[1].split(".")[0];
563
+ } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
564
+ if (userAgent.includes("Chrome/")) {
565
+ name = "chrome-mobile";
566
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
567
+ if (match)
568
+ version = match[1].split(".")[0];
569
+ } else if (userAgent.includes("Firefox/")) {
570
+ name = "firefox-mobile";
571
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
572
+ if (match)
573
+ version = match[1].split(".")[0];
574
+ } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
575
+ name = "safari-mobile";
576
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
577
+ if (match)
578
+ version = match[1].split(".")[0];
579
+ } else {
580
+ name = "mobile";
581
+ }
582
+ } else if (userAgent.includes("Chrome/")) {
583
+ name = "chrome";
584
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
585
+ if (match)
586
+ version = match[1].split(".")[0];
587
+ } else if (userAgent.includes("Firefox/")) {
588
+ name = "firefox";
589
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
590
+ if (match)
591
+ version = match[1].split(".")[0];
592
+ } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
593
+ name = "safari";
594
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
595
+ if (match)
596
+ version = match[1].split(".")[0];
597
+ }
598
+ if (name === "unknown") {
599
+ const patterns = [
600
+ { regex: /Chrome\/([0-9]+)/, name: "chrome" },
601
+ { regex: /Firefox\/([0-9]+)/, name: "firefox" },
602
+ { regex: /Safari\/([0-9]+)/, name: "safari" },
603
+ { regex: /Edge\/([0-9]+)/, name: "edge" },
604
+ { regex: /Opera\/([0-9]+)/, name: "opera" }
605
+ ];
606
+ for (const pattern of patterns) {
607
+ const match = userAgent.match(pattern.regex);
608
+ if (match) {
609
+ name = pattern.name;
610
+ version = match[1];
611
+ break;
612
+ }
613
+ }
614
+ }
615
+ } catch (error) {
616
+ }
617
+ return { name, version };
618
+ }
619
+ function detectBrowser() {
620
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
621
+ return { name: "unknown", version: "unknown" };
622
+ }
623
+ const userAgent = window.navigator.userAgent;
624
+ const hasBraveAPI = !!navigator.brave;
625
+ return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
626
+ }
627
+ function getPlatformName() {
628
+ const { name, version } = detectBrowser();
629
+ return version !== "unknown" ? `${name}-v${version}` : name;
630
+ }
631
+ function getBrowserDisplayName() {
632
+ const { name, version } = detectBrowser();
633
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
634
+ return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
635
+ }
636
+
498
637
  // src/providers/embedded/index.ts
499
638
  var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvider {
500
639
  constructor(config) {
501
640
  debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
502
641
  const urlParamsAccessor = new BrowserURLParamsAccessor();
642
+ const stamper = new import_indexed_db_stamper.IndexedDbStamper({
643
+ dbName: `phantom-embedded-sdk-${config.organizationId}`,
644
+ storeName: "crypto-keys",
645
+ keyName: "signing-key"
646
+ });
647
+ const platformName = getPlatformName();
503
648
  const platform = {
504
649
  storage: new BrowserStorage(),
505
650
  authProvider: new BrowserAuthProvider(urlParamsAccessor),
506
- urlParamsAccessor
651
+ urlParamsAccessor,
652
+ stamper,
653
+ name: platformName
654
+ // Use detected browser name and version for identification
507
655
  };
656
+ debug.log(DebugCategory.EMBEDDED_PROVIDER, "Detected platform", { platformName });
508
657
  const logger = new BrowserLogger();
509
658
  super(config, platform, logger);
510
659
  debug.info(DebugCategory.EMBEDDED_PROVIDER, "Browser EmbeddedProvider initialized");
@@ -748,7 +897,6 @@ var BrowserSDK = class {
748
897
  );
749
898
  }
750
899
  config.embeddedWalletType = embeddedWalletType;
751
- this.config = config;
752
900
  debug.log(DebugCategory.BROWSER_SDK, "Creating ProviderManager", { config });
753
901
  this.providerManager = new ProviderManager(config);
754
902
  debug.info(DebugCategory.BROWSER_SDK, "BrowserSDK initialized successfully");
@@ -841,7 +989,17 @@ var BrowserSDK = class {
841
989
  * @returns Signature string
842
990
  */
843
991
  async signMessage(params) {
844
- return this.providerManager.signMessage(params);
992
+ debug.info(DebugCategory.BROWSER_SDK, "Signing message", {
993
+ message: params.message,
994
+ networkId: params.networkId
995
+ });
996
+ const result = await this.providerManager.signMessage(params);
997
+ debug.info(DebugCategory.BROWSER_SDK, "Message signed successfully", {
998
+ message: params.message,
999
+ networkId: params.networkId,
1000
+ result
1001
+ });
1002
+ return result;
845
1003
  }
846
1004
  /**
847
1005
  * Sign and send a transaction
@@ -849,7 +1007,15 @@ var BrowserSDK = class {
849
1007
  * @returns Transaction result
850
1008
  */
851
1009
  async signAndSendTransaction(params) {
852
- return this.providerManager.signAndSendTransaction(params);
1010
+ debug.info(DebugCategory.BROWSER_SDK, "Signing and sending transaction", {
1011
+ networkId: params.networkId
1012
+ });
1013
+ const result = await this.providerManager.signAndSendTransaction(params);
1014
+ debug.info(DebugCategory.BROWSER_SDK, "Transaction signed and sent successfully", {
1015
+ networkId: params.networkId,
1016
+ result
1017
+ });
1018
+ return result;
853
1019
  }
854
1020
  /**
855
1021
  * Get wallet addresses
@@ -869,33 +1035,8 @@ var BrowserSDK = class {
869
1035
  getWalletId() {
870
1036
  return this.providerManager.getWalletId();
871
1037
  }
872
- /**
873
- * Create a user organization via your backend API
874
- * @param params - Parameters including userId and any additional options
875
- * @returns Organization creation result with organizationId
876
- */
877
- async createUserOrganization(params) {
878
- if (!this.config.serverUrl) {
879
- throw new Error("serverUrl is required in config to create user organizations");
880
- }
881
- try {
882
- const response = await fetch(`${this.config.serverUrl}/organizations`, {
883
- method: "POST",
884
- headers: {
885
- "Content-Type": "application/json"
886
- },
887
- body: JSON.stringify(params)
888
- });
889
- if (!response.ok) {
890
- throw new Error(`Failed to create organization: ${response.status} ${response.statusText}`);
891
- }
892
- const result = await response.json();
893
- return result;
894
- } catch (error) {
895
- throw new Error(`Error creating user organization: ${error instanceof Error ? error.message : "Unknown error"}`);
896
- }
897
- }
898
1038
  };
899
1039
 
900
1040
  // src/index.ts
1041
+ var import_constants3 = require("@phantom/constants");
901
1042
  var import_client2 = require("@phantom/client");
package/dist/index.mjs CHANGED
@@ -79,6 +79,9 @@ var DebugCategory = {
79
79
  };
80
80
 
81
81
  // src/providers/injected/index.ts
82
+ import { base64urlEncode } from "@phantom/base64url";
83
+ import { getExplorerUrl } from "@phantom/constants";
84
+ import bs58 from "bs58";
82
85
  var InjectedProvider = class {
83
86
  constructor(config) {
84
87
  this.connected = false;
@@ -177,18 +180,24 @@ var InjectedProvider = class {
177
180
  throw new Error("Wallet not connected");
178
181
  }
179
182
  const networkPrefix = params.networkId.split(":")[0].toLowerCase();
183
+ let signatureResult;
180
184
  if (networkPrefix === "solana") {
181
185
  const { signature } = await this.phantom.solana.signMessage(new TextEncoder().encode(params.message));
182
- return Array.from(signature).map((b) => b.toString(16).padStart(2, "0")).join("");
186
+ signatureResult = bs58.encode(signature);
183
187
  } else if (networkPrefix === "ethereum" || networkPrefix === "polygon" || networkPrefix === "eip155") {
184
188
  const address = this.addresses.find((addr) => addr.addressType === AddressType.ethereum)?.address;
185
189
  if (!address) {
186
190
  throw new Error("No address available");
187
191
  }
188
192
  const signature = await this.phantom.ethereum.signPersonalMessage(params.message, address);
189
- return signature;
193
+ signatureResult = signature;
194
+ } else {
195
+ throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
190
196
  }
191
- throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
197
+ return {
198
+ signature: signatureResult,
199
+ rawSignature: base64urlEncode(signatureResult)
200
+ };
192
201
  }
193
202
  async signAndSendTransaction(params) {
194
203
  if (!this.connected) {
@@ -199,7 +208,9 @@ var InjectedProvider = class {
199
208
  const transaction = params.transaction;
200
209
  const result = await this.phantom.solana.signAndSendTransaction(transaction);
201
210
  return {
202
- rawTransaction: result.signature
211
+ hash: result.signature,
212
+ rawTransaction: base64urlEncode(result.signature),
213
+ blockExplorer: getExplorerUrl(params.networkId, "transaction", result.signature)
203
214
  };
204
215
  } else if (networkPrefix === "ethereum" || networkPrefix === "polygon" || networkPrefix === "eip155") {
205
216
  const toHex = (value) => {
@@ -222,7 +233,9 @@ var InjectedProvider = class {
222
233
  };
223
234
  const txHash = await this.phantom.ethereum.sendTransaction(txRequest);
224
235
  return {
225
- rawTransaction: txHash
236
+ hash: txHash,
237
+ rawTransaction: base64urlEncode(txHash),
238
+ blockExplorer: getExplorerUrl(params.networkId, "transaction", txHash)
226
239
  };
227
240
  }
228
241
  throw new Error(`Network ${params.networkId} is not supported for injected wallets`);
@@ -237,6 +250,7 @@ var InjectedProvider = class {
237
250
 
238
251
  // src/providers/embedded/index.ts
239
252
  import { EmbeddedProvider as CoreEmbeddedProvider } from "@phantom/embedded-provider-core";
253
+ import { IndexedDbStamper } from "@phantom/indexed-db-stamper";
240
254
 
241
255
  // src/providers/embedded/adapters/storage.ts
242
256
  var BrowserStorage = class {
@@ -357,7 +371,11 @@ var BrowserAuthProvider = class {
357
371
  parent_organization_id: phantomOptions.parentOrganizationId,
358
372
  redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? window.location.href : ""),
359
373
  session_id: phantomOptions.sessionId,
360
- clear_previous_session: true.toString()
374
+ clear_previous_session: true.toString(),
375
+ app_name: phantomOptions.appName || "",
376
+ // Optional app name
377
+ app_logo: phantomOptions.appLogo || ""
378
+ // Optional app logo URL
361
379
  });
362
380
  if (phantomOptions.provider) {
363
381
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
@@ -462,16 +480,133 @@ var BrowserLogger = class {
462
480
  }
463
481
  };
464
482
 
483
+ // src/utils/browser-detection.ts
484
+ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
485
+ let name = "unknown";
486
+ let version = "unknown";
487
+ if (!userAgent || typeof userAgent !== "string") {
488
+ return { name, version };
489
+ }
490
+ try {
491
+ if (userAgent.includes("Edg/")) {
492
+ name = "edge";
493
+ const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
494
+ if (match)
495
+ version = match[1].split(".")[0];
496
+ } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
497
+ name = "opera";
498
+ const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
499
+ if (match)
500
+ version = match[1].split(".")[0];
501
+ } else if (userAgent.includes("SamsungBrowser/")) {
502
+ name = "samsung";
503
+ const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
504
+ if (match)
505
+ version = match[1].split(".")[0];
506
+ } else if (userAgent.includes("DuckDuckGo/")) {
507
+ name = "duckduckgo";
508
+ const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
509
+ if (match)
510
+ version = match[1].split(".")[0];
511
+ } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
512
+ name = "brave";
513
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
514
+ if (match)
515
+ version = match[1].split(".")[0];
516
+ } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
517
+ if (userAgent.includes("Chrome/")) {
518
+ name = "chrome-mobile";
519
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
520
+ if (match)
521
+ version = match[1].split(".")[0];
522
+ } else if (userAgent.includes("Firefox/")) {
523
+ name = "firefox-mobile";
524
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
525
+ if (match)
526
+ version = match[1].split(".")[0];
527
+ } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
528
+ name = "safari-mobile";
529
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
530
+ if (match)
531
+ version = match[1].split(".")[0];
532
+ } else {
533
+ name = "mobile";
534
+ }
535
+ } else if (userAgent.includes("Chrome/")) {
536
+ name = "chrome";
537
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
538
+ if (match)
539
+ version = match[1].split(".")[0];
540
+ } else if (userAgent.includes("Firefox/")) {
541
+ name = "firefox";
542
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
543
+ if (match)
544
+ version = match[1].split(".")[0];
545
+ } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
546
+ name = "safari";
547
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
548
+ if (match)
549
+ version = match[1].split(".")[0];
550
+ }
551
+ if (name === "unknown") {
552
+ const patterns = [
553
+ { regex: /Chrome\/([0-9]+)/, name: "chrome" },
554
+ { regex: /Firefox\/([0-9]+)/, name: "firefox" },
555
+ { regex: /Safari\/([0-9]+)/, name: "safari" },
556
+ { regex: /Edge\/([0-9]+)/, name: "edge" },
557
+ { regex: /Opera\/([0-9]+)/, name: "opera" }
558
+ ];
559
+ for (const pattern of patterns) {
560
+ const match = userAgent.match(pattern.regex);
561
+ if (match) {
562
+ name = pattern.name;
563
+ version = match[1];
564
+ break;
565
+ }
566
+ }
567
+ }
568
+ } catch (error) {
569
+ }
570
+ return { name, version };
571
+ }
572
+ function detectBrowser() {
573
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
574
+ return { name: "unknown", version: "unknown" };
575
+ }
576
+ const userAgent = window.navigator.userAgent;
577
+ const hasBraveAPI = !!navigator.brave;
578
+ return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
579
+ }
580
+ function getPlatformName() {
581
+ const { name, version } = detectBrowser();
582
+ return version !== "unknown" ? `${name}-v${version}` : name;
583
+ }
584
+ function getBrowserDisplayName() {
585
+ const { name, version } = detectBrowser();
586
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
587
+ return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
588
+ }
589
+
465
590
  // src/providers/embedded/index.ts
466
591
  var EmbeddedProvider = class extends CoreEmbeddedProvider {
467
592
  constructor(config) {
468
593
  debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
469
594
  const urlParamsAccessor = new BrowserURLParamsAccessor();
595
+ const stamper = new IndexedDbStamper({
596
+ dbName: `phantom-embedded-sdk-${config.organizationId}`,
597
+ storeName: "crypto-keys",
598
+ keyName: "signing-key"
599
+ });
600
+ const platformName = getPlatformName();
470
601
  const platform = {
471
602
  storage: new BrowserStorage(),
472
603
  authProvider: new BrowserAuthProvider(urlParamsAccessor),
473
- urlParamsAccessor
604
+ urlParamsAccessor,
605
+ stamper,
606
+ name: platformName
607
+ // Use detected browser name and version for identification
474
608
  };
609
+ debug.log(DebugCategory.EMBEDDED_PROVIDER, "Detected platform", { platformName });
475
610
  const logger = new BrowserLogger();
476
611
  super(config, platform, logger);
477
612
  debug.info(DebugCategory.EMBEDDED_PROVIDER, "Browser EmbeddedProvider initialized");
@@ -715,7 +850,6 @@ var BrowserSDK = class {
715
850
  );
716
851
  }
717
852
  config.embeddedWalletType = embeddedWalletType;
718
- this.config = config;
719
853
  debug.log(DebugCategory.BROWSER_SDK, "Creating ProviderManager", { config });
720
854
  this.providerManager = new ProviderManager(config);
721
855
  debug.info(DebugCategory.BROWSER_SDK, "BrowserSDK initialized successfully");
@@ -808,7 +942,17 @@ var BrowserSDK = class {
808
942
  * @returns Signature string
809
943
  */
810
944
  async signMessage(params) {
811
- return this.providerManager.signMessage(params);
945
+ debug.info(DebugCategory.BROWSER_SDK, "Signing message", {
946
+ message: params.message,
947
+ networkId: params.networkId
948
+ });
949
+ const result = await this.providerManager.signMessage(params);
950
+ debug.info(DebugCategory.BROWSER_SDK, "Message signed successfully", {
951
+ message: params.message,
952
+ networkId: params.networkId,
953
+ result
954
+ });
955
+ return result;
812
956
  }
813
957
  /**
814
958
  * Sign and send a transaction
@@ -816,7 +960,15 @@ var BrowserSDK = class {
816
960
  * @returns Transaction result
817
961
  */
818
962
  async signAndSendTransaction(params) {
819
- return this.providerManager.signAndSendTransaction(params);
963
+ debug.info(DebugCategory.BROWSER_SDK, "Signing and sending transaction", {
964
+ networkId: params.networkId
965
+ });
966
+ const result = await this.providerManager.signAndSendTransaction(params);
967
+ debug.info(DebugCategory.BROWSER_SDK, "Transaction signed and sent successfully", {
968
+ networkId: params.networkId,
969
+ result
970
+ });
971
+ return result;
820
972
  }
821
973
  /**
822
974
  * Get wallet addresses
@@ -836,36 +988,11 @@ var BrowserSDK = class {
836
988
  getWalletId() {
837
989
  return this.providerManager.getWalletId();
838
990
  }
839
- /**
840
- * Create a user organization via your backend API
841
- * @param params - Parameters including userId and any additional options
842
- * @returns Organization creation result with organizationId
843
- */
844
- async createUserOrganization(params) {
845
- if (!this.config.serverUrl) {
846
- throw new Error("serverUrl is required in config to create user organizations");
847
- }
848
- try {
849
- const response = await fetch(`${this.config.serverUrl}/organizations`, {
850
- method: "POST",
851
- headers: {
852
- "Content-Type": "application/json"
853
- },
854
- body: JSON.stringify(params)
855
- });
856
- if (!response.ok) {
857
- throw new Error(`Failed to create organization: ${response.status} ${response.statusText}`);
858
- }
859
- const result = await response.json();
860
- return result;
861
- } catch (error) {
862
- throw new Error(`Error creating user organization: ${error instanceof Error ? error.message : "Unknown error"}`);
863
- }
864
- }
865
991
  };
866
992
 
867
993
  // src/index.ts
868
- import { NetworkId, AddressType as AddressType2 } from "@phantom/client";
994
+ import { NetworkId } from "@phantom/constants";
995
+ import { AddressType as AddressType2 } from "@phantom/client";
869
996
  export {
870
997
  AddressType2 as AddressType,
871
998
  BrowserSDK,
@@ -874,5 +1001,9 @@ export {
874
1001
  DebugCategory,
875
1002
  DebugLevel,
876
1003
  NetworkId,
877
- debug
1004
+ debug,
1005
+ detectBrowser,
1006
+ getBrowserDisplayName,
1007
+ getPlatformName,
1008
+ parseBrowserFromUserAgent
878
1009
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantom/browser-sdk",
3
- "version": "0.2.3",
3
+ "version": "0.3.2",
4
4
  "description": "Browser SDK for Phantom Wallet with unified interface",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -24,15 +24,17 @@
24
24
  "test": "jest",
25
25
  "test:watch": "jest --watch",
26
26
  "lint": "tsc --noEmit && eslint --cache . --ext .ts,.tsx",
27
+ "check-types": "tsc --noEmit",
27
28
  "prettier": "prettier --write \"src/**/*.{ts,tsx}\""
28
29
  },
29
30
  "dependencies": {
30
- "@phantom/api-key-stamper": "^0.1.1",
31
31
  "@phantom/base64url": "^0.1.0",
32
32
  "@phantom/browser-injected-sdk": "^0.0.9",
33
- "@phantom/client": "^0.1.3",
34
- "@phantom/embedded-provider-core": "^0.1.1",
35
- "@phantom/parsers": "^0.0.4",
33
+ "@phantom/client": "^0.1.6",
34
+ "@phantom/constants": "^0.0.2",
35
+ "@phantom/embedded-provider-core": "^0.1.3",
36
+ "@phantom/indexed-db-stamper": "^0.1.2",
37
+ "@phantom/parsers": "^0.0.6",
36
38
  "axios": "^1.10.0",
37
39
  "bs58": "^6.0.0",
38
40
  "buffer": "^6.0.3",