# Build with Phantom Source: https://docs.phantom.com/introduction Integrate wallet functionality into your apps with Phantom's powerful SDKs. **Phantom** is a leading crypto wallet enabling users to manage digital assets and access decentralized applications across Solana, Ethereum, Bitcoin, Base, Polygon, Sui, and Monad. Available as a browser extension, iOS app, and Android app, Phantom provides developers with enterprise-grade wallet infrastructure through our comprehensive SDK suite. This documentation is for developers building with Phantom. For integration support, submit a request using this form. For general support, visit our Help Center. ## Key Features ### Phantom Connect Enable smooth and fast authentication with Phantom Connect, offering users the choice between social login providers (Google, Apple) or their existing Phantom browser extension. ### Phantom Connect SDKs Build with our comprehensive SDK suite for web and mobile platforms. Choose from React, React Native, or Browser SDKs to integrate wallet functionality into your app with native multi-chain support. ### Phantom Portal Publish and maintain your app with Phantom Portal, a self-service dashboard for managing branding, supported networks, and contract verification. # Phantom MCP server Source: https://docs.phantom.com/phantom-mcp-server The Phantom MCP server (`@phantom/mcp-server`) gives AI agents a Phantom wallet. Agents can sign transactions, transfer tokens, swap, and interact onchain across Solana, Ethereum, Bitcoin, and Sui. It's a Phantom wallet product for users who interact through AI agents — the same way the mobile wallet and browser extension serve users who interact directly. ## Quick install Add to your AI client config (requires App ID from phantom.com/portal): ```json { "mcpServers": { "phantom": { "command": "npx", "args": ["-y", "@phantom/mcp-server@latest"], "env": { "PHANTOM_APP_ID": "your_app_id_from_portal" } } } } ``` Supported clients: Claude Desktop, Cursor, Claude Code. ## Available tools | Tool | Description | |------|-------------| | `get_connection_status` | Check if the wallet session is active | | `get_wallet_addresses` | Get addresses for Solana, Ethereum, Bitcoin, and Sui | | `get_token_balances` | View token holdings with live USD pricing | | `transfer_tokens` | Transfer SOL, ETH, and SPL/ERC-20 tokens | | `buy_token` | Swap tokens via Phantom routing (Solana, EVM, cross-chain) | | `send_solana_transaction` | Sign and broadcast Solana transactions | | `send_evm_transaction` | Sign and broadcast EVM transactions | | `sign_solana_message` | Sign UTF-8 messages on Solana | | `sign_evm_personal_message` | EIP-191 personal message signing | | `sign_evm_typed_data` | EIP-712 structured data signing | | `phantom_login` | Re-authenticate, switch accounts, or refresh a session | | `pay_api_access` | Pay for daily API access when quota is consumed | | `portfolio_rebalance` | Analyze and rebalance portfolio allocation via token swaps | # Phantom MCP server setup Source: https://docs.phantom.com/phantom-mcp-server/setup Full installation and reference guide for the Phantom MCP server (`@phantom/mcp-server`). ## Prerequisites Requires an App ID from Phantom Portal (phantom.com/portal): 1. Visit phantom.com/portal and sign in 2. Create an app and fill in required details 3. Navigate to Dashboard > View App > Redirect URLs and add `http://localhost:8080/callback` 4. Get your App ID from the Phantom Connect tab ## Installation ```bash npx -y @phantom/mcp-server@latest ``` Or install globally: `npm install -g @phantom/mcp-server@latest` then run `phantom-mcp`. ## Setup by client **Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "phantom": { "command": "npx", "args": ["-y", "@phantom/mcp-server@latest"], "env": { "PHANTOM_APP_ID": "your_app_id_from_portal" } } } } ``` **Cursor** — add to `~/.cursor/mcp.json` (same config as above). **Claude Code**: ```bash claude mcp add phantom -- npx -y @phantom/mcp-server@latest export PHANTOM_APP_ID=your_app_id_from_portal ``` ## Environment variables Required: `PHANTOM_APP_ID` — App ID from Phantom Portal. Optional: `PHANTOM_CLIENT_SECRET`, `PHANTOM_CALLBACK_PORT` (default 8080), `PHANTOM_CALLBACK_PATH` (default /callback), `PHANTOM_MCP_DEBUG` (set to 1), `PHANTOM_AUTH_BASE_URL`, `PHANTOM_CONNECT_BASE_URL`, `PHANTOM_API_BASE_URL`, `PHANTOM_QUOTES_API_URL`. ## Authentication flow On first run: opens browser to connect.phantom.app for Google/Apple login, starts local server on port 8080 for OAuth callback, saves session to `~/.phantom-mcp/session.json`. Sessions persist indefinitely via stamper keys. ## Tools reference ### get_connection_status Checks whether the authenticated wallet session is active. ### get_wallet_addresses Gets all blockchain addresses for the authenticated wallet. - `derivationIndex` (number, optional): default 0 ### get_token_balances Returns token holdings with live USD pricing. - `networks` (string[], optional): filter by network names e.g. `["solana", "ethereum", "base", "polygon", "arbitrum", "bitcoin", "sui", "monad"]`. Omit to return all. - `derivationIndex` (number, optional): default 0 ### send_solana_transaction Signs and broadcasts a Solana transaction. - `transaction` (string, required): Base64-encoded transaction (standard base64 with `A-Za-z0-9+/=`) - `networkId` (string, optional): e.g. `solana:mainnet`. Defaults to `solana:mainnet`. - `walletId` (string, optional): wallet ID to use for signing - `derivationIndex` (number, optional): default 0 ### send_evm_transaction Signs and broadcasts an EVM transaction with automatic gas estimation. - `chainId` (number, required): EVM chain ID e.g. `1` (Ethereum), `8453` (Base), `137` (Polygon) - `to` (string, required): destination address (`0x`-prefixed) - `value` (string, optional): value in wei as hex e.g. `"0x0"` - `data` (string, optional): calldata as hex - `gasLimit` (string, optional): gas limit as hex. Auto-estimated if omitted. - `derivationIndex` (number, optional): default 0 ### transfer_tokens Transfers native tokens or SPL/ERC-20 tokens across Solana and EVM. - `networkId` (string, required), `to` (string, required), `amount` (string, required) - `tokenMint` (string, optional): SPL token mint (Solana only, omit for native SOL) - `tokenAddress` (string, optional): ERC-20 contract address (EVM only, omit for native ETH) ### buy_token Fetches a swap quote from Phantom routing. Optionally executes when `execute: true`. - `amount` (string, required), `sellChainId`, `buyChainId`, `buyTokenMint`, `sellTokenMint`, `execute` (boolean) ### sign_solana_message Signs a UTF-8 message on Solana. - `message` (string, required), `networkId` (string, required) ### sign_evm_personal_message Signs a message using EIP-191 for EVM chains. - `message` (string, required), `chainId` (number, required): e.g. `1`, `8453` ### sign_evm_typed_data Signs EIP-712 structured data. - `typedData` (object, required), `chainId` (number, required): e.g. `1`, `8453` ## Supported networks Solana: `solana:mainnet`, `solana:devnet`, `solana:testnet` EVM: `eip155:1` (Ethereum), `eip155:8453` (Base), `eip155:137` (Polygon), `eip155:42161` (Arbitrum), `eip155:143` (Monad), plus testnets Bitcoin: `bip122:000000000019d6689c085ae165831e93` Sui: `sui:mainnet`, `sui:testnet` ## Session management Sessions stored in `~/.phantom-mcp/session.json`. To reset: `rm ~/.phantom-mcp/session.json` then restart your AI client. # Phantom Connect Source: https://docs.phantom.com/phantom-connect Onboard users with embedded wallets and authenticate them using Google or Apple. ## Overview Phantom Connect is a suite of tools that lets developers onboard users instantly with embedded wallets powered by Phantom. Users sign in with Google or Apple (with more identity providers coming in the future), and your app receives a secure, ready-to-use wallet without requiring extensions, mobile apps, or private-key management. When users authenticate with social login, Phantom creates an embedded wallet inside a secure environment on the user's device. This wallet is authorized to transact with your app and is protected by spending-limit controls, domain binding, and real-time risk evaluation. Your app never handles private keys and never becomes a custodian. Phantom Connect also supports users connecting through the Phantom extension or other injected wallets. In this case, Phantom doesn't create an embedded wallet. Users approve transactions directly in their existing wallet, and your app interacts with it through the same Phantom Connect SDK interface. ## Embedded Wallets Embedded wallets are wallets built directly into your application. No browser extensions, mobile apps, or external wallet software required. Users authenticate with familiar methods like Google or Apple, and your app instantly has access to a fully functional wallet. ## Authentication Methods ### Social Login When a user selects **Sign in with Google** or **Sign in with Apple**, Phantom Connect creates or retrieves an embedded wallet tied to that identity. Flow summary: 1. Users authenticate with Google or Apple. 2. Users enter their 4-digit PIN. 3. Users approve any required permissions or spending limits. 4. Your app receives the connected embedded wallet. ### Extension and Injected Wallets When users connect with the Phantom extension or any other injected wallet: - Phantom Connect doesn't create an embedded wallet. - Users transact and approve actions directly inside their extension. - The extension signs using whatever key-management system that wallet uses. - Your app receives the connected account through the same Phantom Connect SDK interface. ## Session Duration A Phantom Connect session remains active for seven days from the last login. After it expires, users must sign in again with Google or Apple. # Phantom Portal Overview Source: https://docs.phantom.com/phantom-portal/portal Dashboard for managing app configuration, branding, and integration with Phantom. Phantom Portal is a self-service dashboard where developers manage their app's configuration, branding, and presence within Phantom. To sign up, go to phantom.app/portal and sign up with a Google account or Apple ID. ## Key Features - Configure your app: Set up allowed URLs, network support, and authentication settings. - Manage branding: Upload your app icon, cover image, and description. - View access mode: See your app's current access mode (DISABLED, PRIVATE, or PUBLIC). - Verify your domain: Confirm that you own the domain in order to have your app displayed in Phantom. ## Apps That Need Phantom Portal Access You need to use Phantom Portal if you're building one of the following: - Apps with embedded wallet: Using React SDK, Browser SDK, or React Native SDK - Apps with Phantom Connect: Implementing social login or extension-based authentication - Apps that need public listing: Appearing in Phantom's Explore tab, search results, or recommended apps ## Domain Verification Domain verification confirms that you control the domain where your app runs. While domain verification is not required to use Phantom Connect, it is required for your app to appear in Phantom's public discovery surfaces. # Phantom Connect SDK Overview Source: https://docs.phantom.com/wallet-sdks-overview Compare React, React Native, and Browser SDKs to choose the right one for your application. | SDK | Platform | Best For | |-----|----------|----------| | React SDK | Web (React) | React apps with built-in UI components and hooks | | React Native SDK | Mobile (iOS/Android) | React Native and Expo mobile apps | | Browser SDK | Web (Any) | Vanilla JS or non-React frameworks | All SDKs support: - Social login (Google, Apple) - Browser extension connection - Multi-chain support (Solana, Ethereum, Bitcoin, Base, Polygon, Sui, Monad) - Message signing - Transaction signing and sending # React SDK Source: https://docs.phantom.com/sdks/react-sdk React hooks for wallet integration with built-in UI components and native transaction support. ## Installation ```bash npm install @phantom/react-sdk ``` ## Quick Start ```tsx import { PhantomProvider, AddressType } from "@phantom/react-sdk"; function App() { return ( ); } ``` ## Hooks ### useConnect Manages wallet connection functionality. ```tsx const { connect, isConnecting, error } = useConnect(); // Connect with Google await connect({ provider: "google" }); // Connect with Apple await connect({ provider: "apple" }); // Connect with the Phantom browser extension await connect({ provider: "injected" }); ``` ### useAccounts Provides access to connected wallet information. ```tsx const { addresses, // Array of wallet addresses isConnected, // Connection status walletId, // Phantom wallet ID } = useAccounts(); ``` ### useSolana Provides access to Solana-specific operations. ```tsx const { solana, isAvailable } = useSolana(); // Sign a message const signature = await solana.signMessage("Hello Solana!"); // Sign and send a transaction const result = await solana.signAndSendTransaction(transaction); ``` ### useEthereum Provides access to Ethereum-specific operations. ```tsx const { ethereum, isAvailable } = useEthereum(); // Get accounts const accounts = await ethereum.getAccounts(); // Sign a message const signature = await ethereum.signPersonalMessage("Hello!", accounts[0]); // Send a transaction const result = await ethereum.sendTransaction(transactionData); ``` ### useDisconnect Manages wallet disconnection. ```tsx const { disconnect, isDisconnecting } = useDisconnect(); await disconnect(); ``` # React Native SDK Source: https://docs.phantom.com/sdks/react-native-sdk Mobile wallet integration for React Native apps with OAuth authentication. ## Installation ```bash npm install @phantom/react-native-sdk # For Expo projects npx expo install expo-secure-store expo-web-browser expo-auth-session expo-router react-native-svg # Required polyfill npm install react-native-get-random-values ``` ## Required Polyfill You must polyfill random byte generation. Add this import at the very top of your app's entry point (before any other imports): ```tsx // index.js, App.tsx, or _layout.tsx - MUST be the first import import "react-native-get-random-values"; ``` ## Configure App Scheme Add your custom scheme to `app.json`: ```json { "expo": { "scheme": "mywalletapp", "plugins": ["expo-router", "expo-secure-store", "expo-web-browser", "expo-auth-session"] } } ``` ## Quick Start ```tsx import "react-native-get-random-values"; import { PhantomProvider, AddressType, darkTheme } from "@phantom/react-native-sdk"; export default function App() { return ( ); } ``` ## Using the Connection Modal ```tsx import { useModal, useAccounts } from "@phantom/react-native-sdk"; export function WalletScreen() { const modal = useModal(); const { isConnected, addresses } = useAccounts(); if (!isConnected) { return ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; const sdk = new BrowserSDK({ providers: ["google", "apple"], appId: "your-app-id", addressTypes: [AddressType.solana], authOptions: { redirectUrl: "https://yourapp.com/callback", }, }); // Sign in with Google document.getElementById("google-btn").onclick = async () => { const { addresses } = await sdk.connect({ provider: "google" }); console.log("Wallet address:", addresses[0].address); }; // Sign in with Apple document.getElementById("apple-btn").onclick = async () => { const { addresses } = await sdk.connect({ provider: "apple" }); console.log("Wallet address:", addresses[0].address); }; ``` ## React Native ```tsx import { PhantomProvider, useModal, usePhantom, useAccounts, AddressType, darkTheme } from "@phantom/react-native-sdk"; import { View, Text, Button, StyleSheet } from "react-native"; function App() { return ( ); } function LoginPage() { const { open } = useModal(); const { isConnected } = usePhantom(); const addresses = useAccounts(); if (isConnected && addresses) { return ( Welcome! Your wallet: {addresses[0]?.address} ); } return ( Sign in to get started ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType, waitForPhantomExtension } from "@phantom/browser-sdk"; const sdk = new BrowserSDK({ providers: ["injected"], addressTypes: [AddressType.solana], }); // Check if extension is installed const isAvailable = await waitForPhantomExtension(3000); if (!isAvailable) { // Show install prompt window.location.href = "https://phantom.app/download"; } // Connect to extension const { addresses } = await sdk.connect({ provider: "injected" }); console.log("Connected:", addresses[0].address); ``` # Session management Source: https://docs.phantom.com/recipes/auth/session-management Keep users logged in across page reloads and browser sessions. The SDK automatically persists sessions. Here's how to handle the connection state and show the right UI. ## React ```tsx import { PhantomProvider, usePhantom, useModal, useAccounts, useDisconnect, darkTheme } from "@phantom/react-sdk"; import { AddressType } from "@phantom/browser-sdk"; function App() { return ( ); } function AppContent() { const { isConnected } = usePhantom(); const { open } = useModal(); const addresses = useAccounts(); const { disconnect } = useDisconnect(); // User is logged in if (isConnected && addresses) { return (

Welcome back!

Wallet: {addresses[0]?.address}

); } // User needs to log in return (

Welcome

); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); // Listen for connection events sdk.on("connect", (data) => { console.log("Connected:", data.addresses); // Update your UI to show logged-in state }); sdk.on("disconnect", () => { console.log("Disconnected"); // Update your UI to show logged-out state }); // Try to restore existing session on page load await sdk.autoConnect(); ``` ## React Native ```tsx import { PhantomProvider, usePhantom, useModal, useAccounts, useDisconnect, AddressType, darkTheme } from "@phantom/react-native-sdk"; import { View, Text, Button, StyleSheet, ActivityIndicator } from "react-native"; function App() { return ( ); } function AppContent() { const { isConnected } = usePhantom(); const { open } = useModal(); const addresses = useAccounts(); const { disconnect } = useDisconnect(); if (isConnected && addresses) { return ( Welcome back! Wallet: {addresses[0]?.address} ; } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function signIn() { const address = await sdk.solana.getPublicKey(); const message = `Sign in to MyApp Address: ${address} Timestamp: ${new Date().toISOString()} Nonce: ${crypto.randomUUID()}`; const { signature } = await sdk.solana.signMessage(message); // Send to backend for verification const response = await fetch("/api/auth/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address, message, signature }), }); const { token } = await response.json(); return token; } ``` ## React Native ```tsx import { useSolana, useAccounts } from "@phantom/react-native-sdk"; import { View, Button, Alert, StyleSheet } from "react-native"; function SignInWithSolana() { const { solana } = useSolana(); const addresses = useAccounts(); const signIn = async () => { try { const address = addresses?.[0]?.address; if (!solana || !address) { Alert.alert("Error", "Please connect your wallet first"); return; } const message = `Sign in to MyApp Address: ${address} Timestamp: ${new Date().toISOString()} Nonce: ${crypto.randomUUID()}`; const { signature } = await solana.signMessage(message); const response = await fetch("/api/auth/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address, message, signature }), }); const { token } = await response.json(); Alert.alert("Success", "Authenticated!"); console.log("Token:", token); } catch (error) { Alert.alert("Error", error.message); } }; return ( ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage, SystemProgram, LAMPORTS_PER_SOL, } from "@solana/web3.js"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function sendSOL(to: string, amount: number) { const connection = new Connection("https://api.mainnet-beta.solana.com"); const { blockhash } = await connection.getLatestBlockhash(); const from = new PublicKey(await sdk.solana.getPublicKey()); const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions: [ SystemProgram.transfer({ fromPubkey: from, toPubkey: new PublicKey(to), lamports: amount * LAMPORTS_PER_SOL, }), ], }).compileToV0Message() ); const { signature } = await sdk.solana.signAndSendTransaction(transaction); return signature; } ``` ## React Native ```tsx import { useSolana } from "@phantom/react-native-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage, SystemProgram, LAMPORTS_PER_SOL, } from "@solana/web3.js"; import { View, Button, Alert, StyleSheet } from "react-native"; function SendSOL() { const { solana } = useSolana(); const send = async (to: string, amount: number) => { try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const { blockhash } = await connection.getLatestBlockhash(); const from = new PublicKey(await solana.getPublicKey()); const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions: [ SystemProgram.transfer({ fromPubkey: from, toPubkey: new PublicKey(to), lamports: amount * LAMPORTS_PER_SOL, }), ], }).compileToV0Message() ); const { signature } = await solana.signAndSendTransaction(transaction); Alert.alert("Success", `Transaction sent: ${signature}`); return signature; } catch (error) { Alert.alert("Error", error instanceof Error ? error.message : "Transaction failed"); throw error; } }; return ( ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage } from "@solana/web3.js"; import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, getAccount } from "@solana/spl-token"; const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const USDC_DECIMALS = 6; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function sendUSDC(to: string, amount: number) { const connection = new Connection("https://api.mainnet-beta.solana.com"); const from = new PublicKey(await sdk.solana.getPublicKey()); const recipient = new PublicKey(to); const fromATA = await getAssociatedTokenAddress(USDC_MINT, from); const toATA = await getAssociatedTokenAddress(USDC_MINT, recipient); const instructions = []; try { await getAccount(connection, toATA); } catch { instructions.push( createAssociatedTokenAccountInstruction(from, toATA, recipient, USDC_MINT) ); } instructions.push( createTransferInstruction(fromATA, toATA, from, amount * 10 ** USDC_DECIMALS) ); const { blockhash } = await connection.getLatestBlockhash(); const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions, }).compileToV0Message() ); const { signature } = await sdk.solana.signAndSendTransaction(transaction); return signature; } ``` ## React Native ```tsx import { useSolana } from "@phantom/react-native-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage } from "@solana/web3.js"; import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, getAccount } from "@solana/spl-token"; import { View, Button, Alert, StyleSheet } from "react-native"; const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const USDC_DECIMALS = 6; function SendUSDC() { const { solana } = useSolana(); const send = async (to: string, amount: number) => { try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const from = new PublicKey(await solana.getPublicKey()); const recipient = new PublicKey(to); const fromATA = await getAssociatedTokenAddress(USDC_MINT, from); const toATA = await getAssociatedTokenAddress(USDC_MINT, recipient); const instructions = []; try { await getAccount(connection, toATA); } catch { instructions.push( createAssociatedTokenAccountInstruction(from, toATA, recipient, USDC_MINT) ); } instructions.push( createTransferInstruction(fromATA, toATA, from, amount * 10 ** USDC_DECIMALS) ); const { blockhash } = await connection.getLatestBlockhash(); const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions, }).compileToV0Message() ); const { signature } = await solana.signAndSendTransaction(transaction); Alert.alert("Success", `Transaction sent: ${signature}`); return signature; } catch (error) { Alert.alert("Error", error instanceof Error ? error.message : "Transaction failed"); throw error; } }; return ( ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage } from "@solana/web3.js"; import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, getAccount } from "@solana/spl-token"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function sendToken( to: string, amount: number, mintAddress: string, decimals: number ) { const connection = new Connection("https://api.mainnet-beta.solana.com"); const from = new PublicKey(await sdk.solana.getPublicKey()); const recipient = new PublicKey(to); const mint = new PublicKey(mintAddress); const fromATA = await getAssociatedTokenAddress(mint, from); const toATA = await getAssociatedTokenAddress(mint, recipient); const instructions = []; try { await getAccount(connection, toATA); } catch { instructions.push( createAssociatedTokenAccountInstruction(from, toATA, recipient, mint) ); } instructions.push( createTransferInstruction(fromATA, toATA, from, amount * 10 ** decimals) ); const { blockhash } = await connection.getLatestBlockhash(); const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions, }).compileToV0Message() ); const { signature } = await sdk.solana.signAndSendTransaction(transaction); return signature; } ``` ## React Native ```tsx import { useSolana } from "@phantom/react-native-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage } from "@solana/web3.js"; import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, getAccount } from "@solana/spl-token"; import { View, Button, Alert, StyleSheet } from "react-native"; function SendToken() { const { solana } = useSolana(); const send = async ( to: string, amount: number, mintAddress: string, decimals: number ) => { try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const from = new PublicKey(await solana.getPublicKey()); const recipient = new PublicKey(to); const mint = new PublicKey(mintAddress); const fromATA = await getAssociatedTokenAddress(mint, from); const toATA = await getAssociatedTokenAddress(mint, recipient); const instructions = []; try { await getAccount(connection, toATA); } catch { instructions.push( createAssociatedTokenAccountInstruction(from, toATA, recipient, mint) ); } instructions.push( createTransferInstruction(fromATA, toATA, from, amount * 10 ** decimals) ); const { blockhash } = await connection.getLatestBlockhash(); const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions, }).compileToV0Message() ); const { signature } = await solana.signAndSendTransaction(transaction); Alert.alert("Success", `Transaction sent: ${signature}`); return signature; } catch (error) { Alert.alert("Error", error instanceof Error ? error.message : "Transaction failed"); throw error; } }; return ( ; } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); const { signature, publicKey } = await sdk.solana.signMessage("Sign to verify"); console.log("Signature:", signature); console.log("Public key:", publicKey); ``` ## React Native ```tsx import { useSolana } from "@phantom/react-native-sdk"; import { View, Button, Alert, StyleSheet } from "react-native"; function SignMessage() { const { solana } = useSolana(); const sign = async () => { try { const message = "Hello, please sign this message to verify your identity."; const { signature, publicKey } = await solana.signMessage(message); Alert.alert("Signed!", `Public key: ${publicKey}`); return signature; } catch (error) { Alert.alert("Error", error instanceof Error ? error.message : "Signing failed"); throw error; } }; return ( ; } ``` ### Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function signAndVerify() { const message = "Hello, please sign this message"; // Sign the message using the SDK const { signature } = await sdk.solana.signMessage(message); const address = await sdk.solana.getPublicKey(); // Send to your backend for verification const response = await fetch("/api/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address, message, signature }), }); const { verified } = await response.json(); return verified; } ``` ### React Native ```tsx import { useSolana } from "@phantom/react-native-sdk"; import { View, Button, Alert, StyleSheet } from "react-native"; function SignForVerification() { const { solana } = useSolana(); const signAndVerify = async () => { try { const message = "Hello, please sign this message"; const { signature } = await solana.signMessage(message); const address = await solana.getPublicKey(); const response = await fetch("/api/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address, message, signature, }), }); const { verified } = await response.json(); if (verified) { Alert.alert("Success", "Signature verified!"); } else { Alert.alert("Error", "Signature verification failed"); } } catch (error) { Alert.alert("Error", error.message); } }; return ( ; } ``` ### React Native ```tsx import { useEthereum } from "@phantom/react-native-sdk"; import { View, Button, Alert, StyleSheet } from "react-native"; function SignTypedData() { const { ethereum } = useEthereum(); const sign = async () => { try { const accounts = await ethereum.getAccounts(); const typedData = { types: { EIP712Domain: [ { name: "name", type: "string" }, { name: "version", type: "string" }, { name: "chainId", type: "uint256" }, { name: "verifyingContract", type: "address" }, ], Order: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "amount", type: "uint256" }, { name: "nonce", type: "uint256" }, ], }, primaryType: "Order", domain: { name: "My App", version: "1", chainId: 1, verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", }, message: { from: accounts[0], to: "0x742d35Cc6634C0532925a3b8D4C8db86fB5C4A7E", amount: "1000000000000000000", nonce: 1, }, }; const { signature } = await ethereum.signTypedData(typedData, accounts[0]); Alert.alert("Success", `Signature: ${signature.slice(0, 20)}...`); return signature; } catch (error) { Alert.alert("Error", error.message); throw error; } }; return ( ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function getWalletBalance() { if (!sdk.solana.isConnected()) { await sdk.connect({ provider: "google" }); } const connection = new Connection("https://api.mainnet-beta.solana.com"); const publicKey = new PublicKey(await sdk.solana.getPublicKey()); // Get SOL balance const balance = await connection.getBalance(publicKey); const solBalance = balance / LAMPORTS_PER_SOL; return { address: publicKey.toString(), solBalance, lamports: balance, }; } // Usage const balance = await getWalletBalance(); console.log(`${balance.solBalance} SOL`); ``` ## React Native ```tsx import { useSolana, useAccounts } from "@phantom/react-native-sdk"; import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js"; import { View, Text, Button, StyleSheet, ActivityIndicator } from "react-native"; import { useEffect, useState } from "react"; function WalletBalance() { const { solana } = useSolana(); const addresses = useAccounts(); const [solBalance, setSolBalance] = useState(null); const [loading, setLoading] = useState(false); const checkBalance = async () => { if (!addresses?.[0]) return; setLoading(true); try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const publicKey = new PublicKey(addresses[0].address); const balance = await connection.getBalance(publicKey); setSolBalance(balance / LAMPORTS_PER_SOL); } catch (error) { console.error("Failed to fetch balance:", error); } finally { setLoading(false); } }; useEffect(() => { if (addresses?.[0]) { checkBalance(); } }, [addresses]); if (!addresses?.[0]) { return ( Please connect your wallet ); } return ( Wallet Balance {addresses[0].address} {loading ? ( ) : ( {solBalance?.toFixed(4) ?? "0.0000"} SOL )} {tokenBalance !== null &&

USDC Balance: {tokenBalance}

} ); } ``` ### Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey } from "@solana/web3.js"; import { getAssociatedTokenAddress, getAccount, getMint } from "@solana/spl-token"; const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function getTokenBalance(mintAddress: PublicKey) { const connection = new Connection("https://api.mainnet-beta.solana.com"); const publicKey = new PublicKey(await sdk.solana.getPublicKey()); try { const tokenAccount = await getAssociatedTokenAddress(mintAddress, publicKey); const accountInfo = await getAccount(connection, tokenAccount); const mintInfo = await getMint(connection, mintAddress); const balance = Number(accountInfo.amount) / 10 ** mintInfo.decimals; return balance; } catch { return 0; // Token account doesn't exist } } // Usage const usdcBalance = await getTokenBalance(USDC_MINT); console.log(`USDC Balance: ${usdcBalance}`); ``` # Get all token accounts Source: https://docs.phantom.com/recipes/wallet-operations/get-token-accounts Get all token accounts and their balances for the connected wallet. Useful for displaying a complete token portfolio. ## React ```tsx import { useSolana, useAccounts } from "@phantom/react-sdk"; import { Connection, PublicKey } from "@solana/web3.js"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { useEffect, useState } from "react"; interface TokenAccount { mint: string; balance: string; decimals: number; } function TokenAccounts() { const { solana } = useSolana(); const addresses = useAccounts(); const [tokens, setTokens] = useState([]); const [loading, setLoading] = useState(false); const fetchTokenAccounts = async () => { if (!addresses?.[0]) return; setLoading(true); try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const publicKey = new PublicKey(addresses[0].address); // Get all token accounts using connection method const response = await connection.getParsedTokenAccountsByOwner( publicKey, { programId: TOKEN_PROGRAM_ID } ); const tokenList: TokenAccount[] = response.value.map((item) => { const info = item.account.data.parsed.info; return { mint: info.mint, balance: info.tokenAmount.uiAmountString || "0", decimals: info.tokenAmount.decimals, }; }); setTokens(tokenList); } catch (error) { console.error("Failed to fetch token accounts:", error); } finally { setLoading(false); } }; useEffect(() => { if (addresses?.[0]) { fetchTokenAccounts(); } }, [addresses]); if (!addresses?.[0]) { return
Please connect your wallet
; } return (

Token Accounts

{tokens.length === 0 ? (

No token accounts found

) : (
    {tokens.map((token) => (
  • Mint: {token.mint}
    Balance: {token.balance}
  • ))}
)}
); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey } from "@solana/web3.js"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); interface TokenAccount { mint: string; balance: string; decimals: number; } async function getAllTokenAccounts(): Promise { if (!sdk.solana.isConnected()) { await sdk.connect({ provider: "google" }); } const connection = new Connection("https://api.mainnet-beta.solana.com"); const publicKey = new PublicKey(await sdk.solana.getPublicKey()); const response = await connection.getParsedTokenAccountsByOwner( publicKey, { programId: TOKEN_PROGRAM_ID } ); const tokenList: TokenAccount[] = response.value.map((item) => { const info = item.account.data.parsed.info; return { mint: info.mint, balance: info.tokenAmount.uiAmountString || "0", decimals: info.tokenAmount.decimals, }; }); return tokenList; } // Usage const tokens = await getAllTokenAccounts(); tokens.forEach((token) => { console.log(`${token.mint}: ${token.balance}`); }); ``` ## React Native ```tsx import { useSolana, useAccounts } from "@phantom/react-native-sdk"; import { Connection, PublicKey } from "@solana/web3.js"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { View, Text, Button, FlatList, StyleSheet, ActivityIndicator } from "react-native"; import { useEffect, useState } from "react"; interface TokenAccount { mint: string; balance: string; decimals: number; } function TokenAccounts() { const { solana } = useSolana(); const addresses = useAccounts(); const [tokens, setTokens] = useState([]); const [loading, setLoading] = useState(false); const fetchTokenAccounts = async () => { if (!addresses?.[0]) return; setLoading(true); try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const publicKey = new PublicKey(addresses[0].address); const response = await connection.getParsedTokenAccountsByOwner( publicKey, { programId: TOKEN_PROGRAM_ID } ); const tokenList: TokenAccount[] = response.value.map((item) => { const info = item.account.data.parsed.info; return { mint: info.mint, balance: info.tokenAmount.uiAmountString || "0", decimals: info.tokenAmount.decimals, }; }); setTokens(tokenList); } catch (error) { console.error("Failed to fetch token accounts:", error); } finally { setLoading(false); } }; useEffect(() => { if (addresses?.[0]) { fetchTokenAccounts(); } }, [addresses]); if (!addresses?.[0]) { return ( Please connect your wallet ); } return ( Token Accounts ); } ``` ## Browser SDK ```typescript import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage, SystemProgram, LAMPORTS_PER_SOL, ComputeBudgetProgram, } from "@solana/web3.js"; const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", addressTypes: [AddressType.solana], }); async function sendWithPriorityFees(to: string, amount: number, priorityFee: number = 1000) { const connection = new Connection("https://api.mainnet-beta.solana.com"); const { blockhash } = await connection.getLatestBlockhash(); const from = new PublicKey(await sdk.solana.getPublicKey()); const recipient = new PublicKey(to); const transferInstruction = SystemProgram.transfer({ fromPubkey: from, toPubkey: recipient, lamports: amount * LAMPORTS_PER_SOL, }); const instructions = [ ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }), transferInstruction, ]; const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions }).compileToV0Message() ); const { signature } = await sdk.solana.signAndSendTransaction(transaction); return signature; } ``` ## React Native ```tsx import { useSolana } from "@phantom/react-native-sdk"; import { Connection, PublicKey, VersionedTransaction, TransactionMessage, SystemProgram, LAMPORTS_PER_SOL, ComputeBudgetProgram, } from "@solana/web3.js"; import { View, Button, Alert, StyleSheet } from "react-native"; function SendWithPriorityFees() { const { solana } = useSolana(); const sendWithPriorityFees = async (to: string, amount: number, priorityFee: number = 1000) => { try { const connection = new Connection("https://api.mainnet-beta.solana.com"); const { blockhash } = await connection.getLatestBlockhash(); const from = new PublicKey(await solana.getPublicKey()); const recipient = new PublicKey(to); const transferInstruction = SystemProgram.transfer({ fromPubkey: from, toPubkey: recipient, lamports: amount * LAMPORTS_PER_SOL, }); const instructions = [ ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }), transferInstruction, ]; const transaction = new VersionedTransaction( new TransactionMessage({ payerKey: from, recentBlockhash: blockhash, instructions }).compileToV0Message() ); const { signature } = await solana.signAndSendTransaction(transaction); Alert.alert("Success", `Transaction sent: ${signature}`); return signature; } catch (error) { Alert.alert("Error", error instanceof Error ? error.message : "Transaction failed"); throw error; } }; return ( ); } return (

Welcome

); } ``` ## 6. Configure Phantom Portal 1. Go to phantom.com/portal 2. Create or select your app 3. Add your domain to allowed URLs 4. Add `https://yourapp.com/auth/callback` to redirect URLs 5. Copy your App ID # React (Vite) Quickstart Source: https://docs.phantom.com/recipes/quickstarts/react Complete guide to integrating Phantom Connect in a React application. ## 1. Install dependencies ```bash npm install @phantom/react-sdk @solana/web3.js ``` ## 2. Wrap your app with the provider ```tsx // src/main.tsx import React from "react"; import ReactDOM from "react-dom/client"; import { PhantomProvider, darkTheme } from "@phantom/react-sdk"; import { AddressType } from "@phantom/browser-sdk"; import App from "./App"; ReactDOM.createRoot(document.getElementById("root")!).render( ); ``` ## 3. Create your app component ```tsx // src/App.tsx import { useModal, usePhantom, useAccounts, useDisconnect, useSolana } from "@phantom/react-sdk"; function App() { const { open } = useModal(); const { isConnected, isLoading } = usePhantom(); const addresses = useAccounts(); const { disconnect } = useDisconnect(); const { solana } = useSolana(); if (isLoading) return

Loading...

; if (isConnected && addresses) { return (

Connected: {addresses[0]?.address}

); } return (

Welcome

); } export default App; ``` ## 4. Create a callback page ```tsx // src/Callback.tsx import { ConnectBox } from "@phantom/react-sdk"; function Callback() { return (
); } export default Callback; ``` ## 5. Add routing (React Router) ```tsx // src/main.tsx import { BrowserRouter, Routes, Route } from "react-router-dom"; import App from "./App"; import Callback from "./Callback"; // Inside your PhantomProvider: } /> } /> ``` ## 6. Configure Phantom Portal 1. Go to phantom.com/portal 2. Create or select your app 3. Add your domain to allowed URLs 4. Add your callback URL to redirect URLs 5. Copy your App ID # Vanilla JavaScript Quickstart Source: https://docs.phantom.com/recipes/quickstarts/vanilla-js Complete guide to integrating Phantom Connect using plain JavaScript. ## 1. Install the Browser SDK ```bash npm install @phantom/browser-sdk @solana/web3.js ``` ## 2. Initialize the SDK ```typescript // wallet.ts import { BrowserSDK, AddressType } from "@phantom/browser-sdk"; // These functions are defined in main.ts declare function updateUI(address: string): void; declare function showConnectButton(): void; export const sdk = new BrowserSDK({ providers: ["google", "apple", "injected"], appId: "your-app-id", // Get from phantom.com/portal addressTypes: [AddressType.solana], authOptions: { redirectUrl: "https://yourapp.com/callback.html", }, }); // Set up event listeners sdk.on("connect", (data) => { console.log("Connected:", data.addresses); updateUI(data.addresses[0].address); }); sdk.on("disconnect", () => { console.log("Disconnected"); showConnectButton(); }); // Try to restore existing session sdk.autoConnect(); ``` ## 3. Create the HTML ```html My App

Welcome

``` ## 4. Add the JavaScript ```typescript // main.ts import { sdk } from "./wallet"; // Connect buttons document.getElementById("connect-google")!.onclick = () => { sdk.connect({ provider: "google" }); }; document.getElementById("connect-apple")!.onclick = () => { sdk.connect({ provider: "apple" }); }; document.getElementById("connect-extension")!.onclick = () => { sdk.connect({ provider: "injected" }); }; // Sign message document.getElementById("sign")!.onclick = async () => { const { signature } = await sdk.solana.signMessage("Hello!"); alert("Signature: " + signature); }; // Disconnect document.getElementById("disconnect")!.onclick = () => { sdk.disconnect(); }; // UI helpers function updateUI(address: string) { document.getElementById("disconnected")!.style.display = "none"; document.getElementById("connected")!.style.display = "block"; document.getElementById("address")!.textContent = address; } function showConnectButton() { document.getElementById("disconnected")!.style.display = "block"; document.getElementById("connected")!.style.display = "none"; } ``` ## 5. Create the callback page ```html Connecting...

Connecting to Phantom...

``` ## 6. Configure Phantom Portal 1. Go to phantom.com/portal 2. Create or select your app 3. Add your domain to allowed URLs 4. Add your callback URL to redirect URLs 5. Copy your App ID # React Native Quickstart Source: https://docs.phantom.com/recipes/quickstarts/react-native Complete guide to integrating Phantom Connect in a React Native app. ## 1. Install dependencies ```bash npm install @phantom/react-native-sdk @solana/web3.js # Required polyfill for cryptographic operations npm install react-native-get-random-values # Install peer dependencies npx expo install expo-secure-store expo-web-browser expo-auth-session react-native-svg ``` If you're using Expo, use `npx expo install` to ensure compatibility. For bare React Native projects, you can install the packages directly with npm. ## 2. Configure app scheme Add your custom scheme to `app.json`: ```json { "expo": { "name": "My App", "slug": "my-app", "scheme": "myapp", "plugins": [ "expo-router", "expo-secure-store", "expo-web-browser", "expo-auth-session" ] } } ``` For bare React Native projects, configure deep linking in your native project files. See React Native deep linking documentation for details. ## 3. Create the app entry point The `react-native-get-random-values` import must be the very first import in your app's entry point, before any other imports. ### With Expo Router ```tsx // app/_layout.tsx import "react-native-get-random-values"; // Must be first! import { Stack } from "expo-router"; import { PhantomProvider, AddressType, darkTheme } from "@phantom/react-native-sdk"; export default function Layout() { return ( ); } ``` ### Without Expo Router ```tsx // App.tsx or index.js import "react-native-get-random-values"; // Must be first! import React from "react"; import { PhantomProvider, AddressType, darkTheme } from "@phantom/react-native-sdk"; import { WalletScreen } from "./WalletScreen"; export default function App() { return ( ); } ``` ## 4. Create the wallet screen ```tsx // WalletScreen.tsx import React from "react"; import { View, Text, Button, StyleSheet, Alert } from "react-native"; import { useModal, usePhantom, useAccounts, useDisconnect, useSolana, } from "@phantom/react-native-sdk"; export function WalletScreen() { const { open } = useModal(); const { isConnected } = usePhantom(); const addresses = useAccounts(); const { disconnect } = useDisconnect(); const { solana } = useSolana(); if (isConnected && addresses) { return ( Wallet Connected {addresses[0]?.address}