Skip to main content
The Phantom Connect Browser SDK provides a framework-agnostic JavaScript/TypeScript interface for connecting to existing Phantom user wallets in web apps.

Quick start

Generate a new Solana project using the Phantom Embedded JS Starter template.
npx -y create-solana-dapp@latest -t solana-foundation/templates/community/phantom-embedded-js
Run the command above in your terminal to get started. View template on Solana Templates →

Features

  • Non-custodial: Full user control of private keys for both injected and embedded wallets.
  • Dual provider support: Works with Phantom browser extension or creates embedded wallets.
  • Chain-specific APIs: Dedicated interfaces for Solana and Ethereum operations.
  • Native transactions: Work with blockchain-native objects, not base64url strings.
  • Multi-chain: Solana and Ethereum support with dedicated methods.
  • TypeScript: Full type safety for all transaction formats.
  • Unified API: Same interface for both injected and embedded providers.
  • Multiple auth methods: Google, Apple, and browser extension.

Security

The Phantom Connect Browser SDK connects to existing Phantom user wallets, ensuring:
  • Users control their own wallets and private keys.
  • Users maintain full control of their assets.
  • Integration with Phantom’s secure wallet infrastructure.
  • No private key handling in your app.

Prerequisites

  • Register your app: Sign up or log in to the Phantom Portal and register your app.
  • Obtain your App ID:
    • In Phantom Portal, expand your app in the left navigation, then select Set Up.
    • Your App ID appears at the top of the page.
  • Allowlist your domains and redirect URLs: Add your app’s domains and redirect URLs in the Phantom Portal to enable wallet connections.

Authentication providers

The SDK supports multiple authentication providers that you configure via the providers array:

Available providers

ProviderDescriptionRequires appId
"injected"Phantom browser extensionNo
"google"Google OAuthYes
"apple"Apple IDYes
"deeplink"Deeplink to Phantom mobile app (mobile devices only)Yes

Configuration examples

Injected provider only (browser extension)
const sdk = new BrowserSDK({
  providers: ["injected"], // Only allow browser extension
  addressTypes: [AddressType.solana, AddressType.ethereum],
});
Multiple authentication methods
const sdk = new BrowserSDK({
  providers: ["google", "apple", "injected", "deeplink"], // Allow all methods
  addressTypes: [AddressType.solana, AddressType.ethereum],
  appId: "your-app-id", // Required for embedded providers
  authOptions: {
    authUrl: "https://connect.phantom.app/login", // optional
    redirectUrl: "https://yourapp.com/callback", // optional, defaults to current page
  },
  autoConnect: true, // optional, auto-connect to existing session
});
Mobile deeplink support The "deeplink" provider enables a button that opens the Phantom mobile app on mobile devices. This option only appears when the Phantom browser extension is not installed. When clicked, it redirects users to the Phantom mobile app to complete authentication.
const sdk = new BrowserSDK({
  providers: ["google", "apple", "deeplink"], // Include deeplink for mobile support
  addressTypes: [AddressType.solana, AddressType.ethereum],
  appId: "your-app-id",
});
Notes about redirectUrl:
  • Must be an existing page/route in your app.
  • Must be allowlisted in your Phantom Portal app configuration.
  • This is where users will be redirected after completing OAuth authentication.

Installation

npm install @phantom/browser-sdk

Quick start

Injected provider (browser extension)

import { BrowserSDK, AddressType } from "@phantom/browser-sdk";

// Connect to Phantom browser extension
const sdk = new BrowserSDK({
  providers: ["injected"], // Only allow browser extension
  addressTypes: [AddressType.solana, AddressType.ethereum],
});

const { addresses } = await sdk.connect({ provider: "injected" });
console.log("Connected addresses:", addresses);

// Chain-specific operations
const message = "Hello from Phantom!";
const solanaSignature = await sdk.solana.signMessage(message);

// Encode the message as hex for EVM
const encoded = "0x" + Buffer.from(message, "utf8").toString("hex");
const ethSignature = await sdk.ethereum.signPersonalMessage(encoded, addresses[1].address);

Embedded provider (multiple auth methods)

import { BrowserSDK, AddressType } from "@phantom/browser-sdk";

// Create embedded non-custodial wallet with multiple auth providers
const sdk = new BrowserSDK({
  providers: ["google", "apple"], // Allow Google and Apple OAuth
  addressTypes: [AddressType.solana, AddressType.ethereum],
  appId: "your-app-id", // Get your app ID from phantom.com/portal
});

const { addresses } = await sdk.connect({ provider: "google" });
console.log("Addresses:", addresses);

// Use chain-specific APIs
const solanaResult = await sdk.solana.signAndSendTransaction(mySolanaTransaction);
const ethResult = await sdk.ethereum.sendTransaction(myEthTransaction);

Chain-specific APIs

The SDK provides separate interfaces for each blockchain with optimized methods:

Solana chain (sdk.solana)

// Message signing
const signature = await sdk.solana.signMessage("Hello Solana!");

// Transaction signing (without sending)
const signedTx = await sdk.solana.signTransaction(transaction);

// Sign and send transaction
const result = await sdk.solana.signAndSendTransaction(transaction);

// Network switching
await sdk.solana.switchNetwork('devnet');

// Utilities
const publicKey = await sdk.solana.getPublicKey();
const isConnected = sdk.solana.isConnected();

Ethereum chain (sdk.ethereum)

EVM support for Phantom Connect embedded wallets will go live later in 2026.
// EIP-1193 requests
const accounts = await sdk.ethereum.request({ method: 'eth_accounts' });
const chainId = await sdk.ethereum.request({ method: 'eth_chainId' });

// Message signing
const signature = await sdk.ethereum.signPersonalMessage(message, address);

// EIP-712 typed data signing
const typedDataSignature = await sdk.ethereum.signTypedData(typedData, address);

// Transaction sending
const result = await sdk.ethereum.sendTransaction({
  to: "0x...",
  value: "1000000000000000000", // 1 ETH in wei
  gas: "21000",
});

// Network switching
await sdk.ethereum.switchChain(1); // Ethereum mainnet
await sdk.ethereum.switchChain(137); // Polygon

// Utilities
const chainId = await sdk.ethereum.getChainId();
const accounts = await sdk.ethereum.getAccounts();
const isConnected = sdk.ethereum.isConnected();
Supported EVM Networks:
NetworkChain IDUsage
Ethereum Mainnet1ethereum.switchChain(1)
Ethereum Sepolia11155111ethereum.switchChain(11155111)
Polygon Mainnet137ethereum.switchChain(137)
Polygon Amoy80002ethereum.switchChain(80002)
Base Mainnet8453ethereum.switchChain(8453)
Base Sepolia84532ethereum.switchChain(84532)
Arbitrum One42161ethereum.switchChain(42161)
Arbitrum Sepolia421614ethereum.switchChain(421614)
Monad Mainnet143ethereum.switchChain(143)
Monad Testnet10143ethereum.switchChain(10143)

Auto-Confirm (injected provider only)

The SDK provides Auto-Confirm functionality that allows automatic transaction confirmation for specified chains. This feature is only available when using the injected provider (Phantom browser extension).
import { NetworkId } from "@phantom/browser-sdk";

// Enable auto-confirm for specific chains
const result = await sdk.enableAutoConfirm({
  chains: [NetworkId.SOLANA_MAINNET, NetworkId.ETHEREUM_MAINNET]
});

// Enable auto-confirm for all supported chains  
const result = await sdk.enableAutoConfirm();

// Disable auto-confirm
await sdk.disableAutoConfirm();

// Get current status
const status = await sdk.getAutoConfirmStatus();

// Get supported chains for auto-confirm
const supportedChains = await sdk.getSupportedAutoConfirmChains();
Auto-confirm methods are only available for injected providers (Phantom browser extension). Calling these methods on embedded providers will throw an error.

Extension detection

The SDK provides functions to check if the Phantom extension is installed:
import { waitForPhantomExtension } from "@phantom/browser-sdk";

// Check if Phantom extension is available (with optional timeout in ms)
const isAvailable = await waitForPhantomExtension(5000);

if (isAvailable) {
  console.log("Phantom extension is available!");
} else {
  console.log("Phantom extension not found");
}

Wallet discovery

The SDK can discover multiple injected wallets using Wallet Standard for Solana and EIP-6963 for Ethereum. This allows users to choose from any installed wallet that supports the configured address types.

Discover wallets

Asynchronously discover all available injected wallets:
// Discover wallets asynchronously
const wallets = await sdk.discoverWallets();

console.log("Discovered wallets:", wallets);
// Example output:
// [
//   {
//     id: "backpack",
//     name: "Backpack",
//     icon: "https://backpack.app/icon.png",
//     addressTypes: [AddressType.solana],
//     chains: ["solana:mainnet", "solana:devnet"]
//   },
//   {
//     id: "metamask-io",
//     name: "MetaMask",
//     icon: "https://metamask.io/icon.png",
//     addressTypes: [AddressType.ethereum],
//     chains: ["eip155:1", "eip155:5", "eip155:11155111"]
//   }
// ]

Get discovered wallets

Get wallets from the internal registry (synchronous):
// Get already discovered wallets
const wallets = sdk.getDiscoveredWallets();

Event handlers

import { BrowserSDK, AddressType } from "@phantom/browser-sdk";

const sdk = new BrowserSDK({
  providers: ["google", "apple"],
  appId: "your-app-id",
  addressTypes: [AddressType.solana],
});

// Fired when connection starts
sdk.on("connect_start", (data) => {
  console.log("Connection starting:", data.source); // "auto-connect" | "manual-connect"
});

// Fired when connection succeeds
sdk.on("connect", (data) => {
  console.log("Connected successfully!");
  console.log("Provider type:", data.provider);
  console.log("Addresses:", data.addresses);
  console.log("Status:", data.status);
});

// Fired when connection fails
sdk.on("connect_error", (data) => {
  console.error("Connection failed:", data.error);
  console.log("Source:", data.source);
});

// Fired when disconnected
sdk.on("disconnect", (data) => {
  console.log("Disconnected from wallet");
});

// Remove listeners when done
sdk.off("connect", handleConnect);

Available events

EventWhen firedKey data
connect_startConnection initiatedsource, authOptions
connectConnection successfulprovider, addresses, status, source
connect_errorConnection failederror, source
disconnectDisconnectedsource
errorGeneral SDK errorsError details

Using events with autoConnect

const sdk = new BrowserSDK({
  providers: ["google", "apple"],
  appId: "your-app-id",
  addressTypes: [AddressType.solana],
  autoConnect: true,
});

// Set up event listeners BEFORE autoConnect
sdk.on("connect", (data) => {
  console.log("Auto-connected successfully!");
  updateUIWithAddresses(data.addresses);
});

sdk.on("connect_error", (data) => {
  console.log("Auto-connect failed:", data.error);
  showConnectButton();
});

// Auto-connect will trigger events
await sdk.autoConnect();

Debug configuration

The SDK provides dynamic debug configuration that can be changed at runtime:
import { DebugLevel } from "@phantom/browser-sdk";

// Enable debug logging
sdk.enableDebug();

// Disable debug logging
sdk.disableDebug();

// Set debug level
sdk.setDebugLevel(DebugLevel.INFO);

// Set debug callback function
sdk.setDebugCallback((message) => {
  console.log(`[${message.category}] ${message.message}`, message.data);
});

// Configure all debug settings at once
sdk.configureDebug({
  enabled: true,
  level: DebugLevel.DEBUG,
  callback: (message) => {
    console.log(`[${message.level}] ${message.category}: ${message.message}`);
  },
});

Debug levels

LevelValueDescription
DebugLevel.ERROR0Only error messages
DebugLevel.WARN1Warning and error messages
DebugLevel.INFO2Info, warning, and error messages
DebugLevel.DEBUG3All debug messages (most verbose)

Available AddressType values

AddressTypeSupported chains
AddressType.solanaSolana Mainnet, Devnet, Testnet
AddressType.ethereumEthereum, Polygon, Arbitrum, and more

What you can do

Starter kits and examples

Framework-agnostic JavaScript examples and templates:

Additional resources