Skip to main content

Phantom React Native SDK

The Phantom React Native SDK provides React hooks for connecting to existing Phantom user wallets in your mobile React Native applications with native transaction support across multiple blockchains.

Features

  • Framework Agnostic: Works with vanilla JavaScript/TypeScript and any web framework
  • Multi-Chain Support: Solana (available now); Ethereum, Bitcoin, and Sui networks (coming soon)
  • Native Transaction Support: Direct transaction object handling
  • User Wallet Integration: Connect to existing Phantom user wallets
  • TypeScript Support: Full TypeScript definitions included

Security

The React Native SDK connects to existing Phantom user wallets with secure authentication:
  • OAuth authentication with Google, Apple, or custom JWT providers
  • PKCE support for secure OAuth flows
  • User maintains full control of their existing wallets
  • Integration with Phantom’s secure infrastructure

Prerequisites

  • Register your application
    Sign up or log in to the Phantom Portal and register your application.
  • Obtain your App ID
    • In the Portal, go to your app and open URL Config (left-hand menu).
    • This page shows your allowed origins and redirect URLs.
    • Scroll down to the App ID section at the bottom of the page — your App ID is listed there, below the URL configurations.
    • Copy the App ID for use in your integration.
  • Whitelist your domains and redirect URLs
    • Add your application’s domains and redirect URLs in the Phantom Portal to enable wallet connections.

Installation

npm install @phantom/react-native-sdk@beta

Install peer dependencies

# For Expo projects
npx expo install expo-secure-store expo-web-browser expo-auth-session expo-router

# For bare React Native projects (additional setup required)
npm install expo-secure-store expo-web-browser expo-auth-session

# Required polyfill for cryptographic operations
npm install react-native-get-random-values

Required Polyfill

You must polyfill random byte generation to ensure cryptographic operations work properly. Add this import at the very top of your app’s entry point (before any other imports):
// index.js, App.tsx, or _layout.tsx - MUST be the first import
import "react-native-get-random-values";

import { PhantomProvider } from "@phantom/react-native-sdk";
// ... other imports
⚠️ Important: The polyfill import must be the first import in your application entry point to ensure proper initialization of cryptographic functions.

Configure your app scheme

For Expo projects

Add your custom scheme to app.json:
{
  "expo": {
    "name": "My Wallet App",
    "slug": "my-wallet-app",
    "scheme": "mywalletapp",
    "plugins": ["expo-router", "expo-secure-store", "expo-web-browser", "expo-auth-session"]
  }
}

Quick Start

Set up the provider

Wrap your app with PhantomProvider:
// App.tsx or _layout.tsx (for Expo Router)
import { PhantomProvider, AddressType } from "@phantom/react-native-sdk";

export default function App() {
  return (
    <PhantomProvider
      config={{
        providerType: "embedded", // Only embedded provider supported in React Native
        appId: "your-app-id",
        scheme: "mywalletapp", // Must match app.json scheme
        addressTypes: [AddressType.solana],
        authOptions: {
          redirectUrl: "mywalletapp://phantom-auth-callback",
        },
        appName: "My Wallet App", // Optional branding
      }}
    >
      <YourAppContent />
    </PhantomProvider>
  );
}

Use hooks in your components

// WalletScreen.tsx
import React from "react";
import { View, Button, Text, Alert } from "react-native";
import { useConnect, useAccounts, useSolana, useEthereum, useDisconnect } from "@phantom/react-native-sdk";

export function WalletScreen() {
  const { connect, isConnecting, error: connectError } = useConnect();
  const { addresses, isConnected } = useAccounts();
  const { solana } = useSolana();
  const { ethereum } = useEthereum();
  const { disconnect } = useDisconnect();

  const handleConnect = async () => {
    try {
      await connect({ provider: "google" });
      Alert.alert("Success", "Wallet connected!");
    } catch (error) {
      Alert.alert("Error", `Failed to connect: ${error.message}`);
    }
  };

  const handleSignSolanaMessage = async () => {
    try {
      const signature = await solana.signMessage("Hello from Solana!");
      Alert.alert("Solana Signed!", `Signature: ${signature.signature.slice(0, 10)}...`);
    } catch (error) {
      Alert.alert("Error", `Failed to sign: ${error.message}`);
    }
  };

  const handleSignEthereumMessage = async () => {
    try {
      const accounts = await ethereum.getAccounts();
      const signature = await ethereum.signPersonalMessage("Hello from Ethereum!", accounts[0]);
      Alert.alert("Ethereum Signed!", `Signature: ${signature.signature.slice(0, 10)}...`);
    } catch (error) {
      Alert.alert("Error", `Failed to sign: ${error.message}`);
    }
  };

  if (!isConnected) {
    return (
      <View style={{ padding: 20 }}>
        <Button
          title={isConnecting ? "Connecting..." : "Connect Wallet"}
          onPress={handleConnect}
          disabled={isConnecting}
        />
        {connectError && <Text style={{ color: "red", marginTop: 10 }}>Error: {connectError.message}</Text>}
      </View>
    );
  }

  return (
    <View style={{ padding: 20 }}>
      <Text style={{ fontSize: 18, marginBottom: 10 }}>Wallet Connected</Text>
      {addresses.map((addr, index) => (
        <Text key={index}>
          {addr.addressType}: {addr.address}
        </Text>
      ))}

      <Button
        title="Sign Solana Message"
        onPress={handleSignSolanaMessage}
        style={{ marginTop: 10 }}
      />

      <Button
        title="Sign Ethereum Message"
        onPress={handleSignEthereumMessage}
        style={{ marginTop: 10 }}
      />

      <Button title="Disconnect" onPress={disconnect} style={{ marginTop: 10 }} />
    </View>
  );
}

Hooks

useConnect

Manages wallet connection functionality.
const { connect, isConnecting, error } = useConnect();

// Connect with specific provider
await connect({ provider: "google" });
await connect({ provider: "apple" });
await connect({ provider: "jwt", jwtToken: "your-jwt-token" });

useAccounts

Provides access to connected wallet information.
const {
  addresses, // Array of wallet addresses
  isConnected, // Connection status
  walletId, // Phantom wallet ID
} = useAccounts();

useSolana

Provides access to Solana-specific operations.
const { solana } = 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.
const { ethereum } = useEthereum();

// Get accounts
const accounts = await ethereum.getAccounts();

// Sign a personal message
const signature = await ethereum.signPersonalMessage("Hello Ethereum!", accounts[0]);

// Send a transaction
const result = await ethereum.sendTransaction(transactionData);

// Get current chain ID
const chainId = await ethereum.getChainId();

useDisconnect

Manages wallet disconnection.
const { disconnect, isDisconnecting } = useDisconnect();

await disconnect();

Authentication Flows

Authentication Process

  1. User taps “Connect Wallet” in your app
  2. System browser opens (Safari on iOS, Chrome Custom Tab on Android)
  3. User authenticates with their chosen provider
  4. Browser redirects back to your app using the custom scheme
  5. SDK automatically processes the authentication result
  6. Wallet is connected and ready to use
The SDK automatically handles deep link redirects. Ensure your app’s URL scheme is properly configured: Redirect URL format: {scheme}://phantom-auth-callback?wallet_id=...&session_id=...

Security Features

Secure Storage

  • iOS: Uses Keychain Services with hardware security
  • Android: Uses Android Keystore with hardware-backed keys

Authentication Security

  • System Browser: Uses secure system browsers, not in-app webviews
  • Origin Verification: Automatic verification of redirect origins

Configuration Examples

Basic Configuration

import { PhantomProvider, AddressType } from "@phantom/react-native-sdk";

<PhantomProvider
  config={{
    providerType: "embedded", // Only embedded provider supported in React Native
    appId: "app_123456789",
    scheme: "myapp",
    addressTypes: [AddressType.solana],
  }}
>
  <App />
</PhantomProvider>;

Multi-Chain Configuration

import { PhantomProvider, AddressType } from "@phantom/react-native-sdk";

<PhantomProvider
  config={{
    providerType: "embedded", // Only embedded provider supported in React Native
    appId: "app_123456789",
    scheme: "mycompany-wallet",
    addressTypes: [AddressType.solana, AddressType.ethereum],
    authOptions: {
      redirectUrl: "mycompany-wallet://auth/success",
    },
  }}
>
  <App />
</PhantomProvider>;

Next Steps

I