← Protocols
Phantom
Wallet / Auth·Solana · EVM · Bitcoin · Sui

Phantom

01Description

Multichain wallet (Solana, Ethereum/EVM, Bitcoin, Sui) available as browser extension, mobile app, and an Embedded SDK that creates social-login wallets directly in your app. Provider injected at `window.phantom.{solana,ethereum,bitcoin,sui}`.

02Best for
  • 01Solana-first apps
  • 02consumer apps wanting Google/Apple login + non-custodial wallet
  • 03multi-chain (Solana + EVM + BTC) UX in one provider
  • 04embedded wallets without redirects
  • 05extension-or-embedded fallback
03Install
  • pnpm add @phantom/react-sdk
  • pnpm add @phantom/browser-sdk
  • pnpm add @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/web3.js
04Environment variables
VariableScopeDescription
NEXT_PUBLIC_PHANTOM_APP_IDClientPhantom Embedded App ID from the Phantom Portal (https://phantom.com/portal). Client-safe; required for Phantom Connect / Embedded flows.
05Prompt snippet
Use Phantom for multichain wallet auth. For extension-only flows, detect the provider with `const provider = window.phantom?.solana; if (!provider?.isPhantom) window.open('https://phantom.com/download');` then call `await provider.connect()` (returns `{ publicKey }`) and sign with `provider.signMessage(new TextEncoder().encode(msg), 'utf8')` or `provider.signAndSendTransaction(tx)`. For embedded + extension fallback, wrap the app in `<PhantomProvider config={{ appId: process.env.NEXT_PUBLIC_PHANTOM_APP_ID, addressTypes: ['solana','ethereum','bitcoinSegwit'] }}>` from `@phantom/react-sdk` and use `useConnect()`, `useAccounts()`, `useSignMessage()`. On EVM, the same wallet exposes an EIP-1193 provider at `window.phantom.ethereum` — pass it to wagmi/viem via the `injected({ target: 'phantom' })` connector. If a user has the Phantom extension installed the embedded SDK defers to it automatically; pass `skipInjection: true` if you want to avoid global namespace pollution and only use the returned instance.
06Gotchas
  • Provider detection collides with other wallets that also write to `window.ethereum` (MetaMask, Rabby, Coinbase) — read `window.phantom.ethereum` directly or use the EIP-6963 `eip6963:announceProvider` event to disambiguate; `isPhantom` on `window.ethereum` is unreliable when multiple wallets are installed.
  • Phantom Embedded refuses to inject when the extension is detected so existing extension users keep their UX — if you need both side-by-side, pass a custom `namespace` to the embedded SDK and read from `window.phantom[namespace]` instead of the default.
  • Mobile browsers cannot use the extension — the SDK falls back to a deep link to the Phantom mobile app; iOS Safari's PWA mode strips the deep-link return URL, so test on real devices and configure universal links in the Phantom Portal.
  • `connect({ onlyIfTrusted: true })` silently returns nothing (does not throw) if the dApp isn't already trusted — branch on the resolved value, not on a try/catch, when implementing auto-reconnect.
  • Hardware wallets paired through Phantom (Ledger Solana app) cannot sign off-chain messages with version > 0 of `signMessage` — fall back to legacy ed25519 message signing or display a clear hardware-wallet-incompatible error.
  • The browser extension reloads its background service worker aggressively — the provider object on `window.phantom.solana` can become stale after long idle periods; re-read it on every `connect()` call instead of caching a reference at module load.
07Alternatives