Introduction
The Wallet SDK (Software Development Kit) is a set of client-side utilities and patterns designed to let web applications securely interact with Phantom wallet instances. This overview provides a conceptual map and practical examples so you can get integrated quickly. For official documentation and downloads, visit the Phantom website and docs. Below are official links for quick reference.
What is the Wallet SDK?
The Wallet SDK is a browser-focused library that standardizes how DApps (decentralized applications) connect, request signatures, and send transactions via a user's Phantom wallet. It builds a secure messaging bridge between a web page and the wallet extension or mobile wallet.
Goals of the SDK
At a high level, the SDK aims to:
- Offer a clear connection lifecycle (connect, disconnect, events).
- Expose safe APIs for signing and sending transactions.
- Support both popup and deep-link mobile flows.
- Prioritize user consent and least-privilege access.
Integration overview
Integrating the Wallet SDK typically involves three steps: feature-detection (is Phantom available?), connection (ask the user to connect), and then using the SDK to sign or submit transactions. The pseudocode below shows the typical lifecycle.
Detect & Connect
// Detect Phantom
if (window?.phantom?.solana?.isPhantom) {
const provider = window.phantom.solana;
// Connect (will prompt user)
await provider.connect();
// provider.publicKey is now available
}
Connection lifecycle
The provider exposes events — e.g., `connect`, `disconnect`, and `accountChanged`. Your app should subscribe and handle these events gracefully.
// Subscribe to events
provider.on("connect", publicKey => { /* show user as connected */ });
provider.on("disconnect", () => { /* clear session */ });
provider.on("accountChanged", publicKey => { /* update UI */ });
Core SDK APIs (conceptual)
The exact method names and signatures may evolve between releases; this section focuses on what you can expect conceptually from a wallet SDK:
Provider object
Most wallets inject a `provider` object into the `window` namespace (e.g., `window.phantom.solana`) when the extension or adapter is available. The provider offers:
- connect() — ask the user to connect and return the public key
- disconnect() — end the session
- signTransaction(tx) — sign a single transaction
- signAllTransactions(txs) — sign multiple transactions atomically
- sendTransaction(tx, options) — optionally sign + send to the network
- on(event, handler) — event subscriptions
Example: request a signature
const tx = createTransactionToTransferLamports(...);
const signed = await provider.signTransaction(tx);
// now submit signed.serialize()
Mobile & deep-link flows
On mobile, integration commonly uses deep links and universal links. The DApp constructs a link with required payload and the wallet handles the request. Many SDKs abstract this to provide uniform APIs across web and mobile. Make sure to provide a fallback experience for users without Phantom installed.
UX considerations
Important UX patterns include:
- Explain why you need the requested permission (connect or sign).
- Show a transaction preview before asking the wallet to sign.
- Handle declines or rejections gracefully — never assume success.
Security & privacy
Security is central to any wallet integration. The wallet SDK preserves user autonomy by requiring explicit, per-action consent. Here are best practices to keep your integration secure:
Adopt least-privilege
Request only the permissions you need. If you only need to read a public key, don't request full signing rights until required.
Provide clear transaction previews
Always render a human-readable summary of what will be signed — amounts, destination addresses, and any program instructions involved. This helps users detect malicious or mistaken transactions.
Signature intent separation
Separate signing from broadcasting where possible. Let the user confirm they intend to broadcast a transaction after reviewing the signed payload.
Example: Simple transfer flow (full)
Below is a compact example that demonstrates detection, connection, sign, and send. This is intentionally minimal — in production add robust error handling and retries.
// Full flow pseudo-code
async function transfer(toPubkey, lamports) {
if (!window.phantom?.solana?.isPhantom) throw new Error("Phantom not installed");
const provider = window.phantom.solana;
await provider.connect();
const payer = provider.publicKey;
const tx = new Transaction().add(
SystemProgram.transfer({ fromPubkey: payer, toPubkey, lamports })
);
const signed = await provider.signTransaction(tx);
const raw = signed.serialize();
// send raw to Solana RPC (or use provider.sendTransaction)
const txid = await sendRawTransactionToCluster(raw);
return txid;
}
Event handling and common UX patterns
The Wallet SDK usually emits events when the wallet connects or when the selected account changes. Listen and update application state to match the wallet state.
Event example
provider.on("connect", (key) => {
// persist key, update UI
});
provider.on("disconnect", () => {
// clear keys, show 'connect' button
});
provider.on("accountChanged", (newKey) => {
// refresh user balances and data
});
Reconnect strategies
If a user previously connected, consider calling `provider.connect({ onlyIfTrusted: true })` if the provider exposes such an option. This avoids prompting them again while still allowing automatic reconnection for trusted origins.
Testing & local development
For a reliable developer experience:
- Run against a local or devnet cluster to avoid spending real funds.
- Mock provider objects in unit tests to simulate user acceptance and rejection flows.
- Use wallet adapters or emulator tooling if available to test mobile deep links.
Mocking provider (example)
// simple mock
const mockProvider = {
isPhantom: true,
publicKey: "FakePublicKey11111111111111111111111111",
connect: async () => {},
signTransaction: async (tx) => tx,
on: () => {}
};
Best practices
UI & UX
Show clear connection state, never auto-trigger signature requests, and provide fallback instructions (How to install Phantom) if `window.phantom` is undefined. Consider allowing users to copy a deep link or QR code for mobile flows.
Performance
Wallet interactions are user-blocking; minimize synchronous operations during sign flows. Precompute transaction data on your backend where possible, and keep UI responsive during wallet prompts.
Rate limits & RPC
If your app broadcasts many transactions, use a robust RPC provider and backoff/retry logic for transient errors. Never assume immediate finality — show pending/confirmed states.
Troubleshooting & common pitfalls
Phantom not found
If `window.phantom` is undefined, instruct users to install Phantom and provide links. Example prompt:
// alert user
if (!window.phantom) {
// show UI: "Phantom extension not detected — install from phantom.app"
}
Users rejecting signatures
Treat a signature rejection as a valid outcome. Record metrics for rejections to understand UX friction, but never retry automatically.
Network-related errors
If sign succeeded but broadcast fails, ensure persistence: keep the signed transaction and retry submission after short backoff. Always surface the transaction signature to the user so they can check on-chain status.
Accessibility & internationalization
Make sure connect and signing UI elements are keyboard accessible, screen-reader friendly, and localized. Provide clear labels for actions (e.g., "Connect wallet", "Sign transaction") and use progressive enhancement so the site still works without JS for viewing static data pages.
Advanced patterns
Batching & atomic operations
For multi-instruction flows, prefer atomic transactions where possible. Some wallets provide `signAllTransactions` and the ability to co-sign or partially sign for advanced program flows.
Multi-signature flows
Multi-sig requires coordination between parties; consider a backend orchestration service or use on-chain programmatic approaches. Wallet SDKs typically only assist with single-user signatures so multi-party flows will require additional design.
UI snippet: Connect button (React-like pseudocode)
function ConnectButton({ provider }) {
const [connected, setConnected] = useState(false);
useEffect(() => {
provider?.on("connect", () => setConnected(true));
provider?.on("disconnect", () => setConnected(false));
}, [provider]);
return (
<button onClick={async ()=>{
if (!provider) return alert("Install Phantom: https://phantom.app/");
try { await provider.connect(); } catch(e){ console.error(e); }
}}>
{connected ? "Connected" : "Connect Phantom"}
</button>
);
}
References & official resources
Below are official Phantom resources for quick access. I included multiple direct references so you can navigate to installation guides, API docs, and official support pages.
- Phantom homepage — phantom.app
- Phantom Developer Docs — docs.phantom.app
- Developer documentation (again) — docs.phantom.app
- Install Phantom extension — phantom.app
- API reference — docs.phantom.app
- Official downloads and links — phantom.app
- Guides and examples — docs.phantom.app
- Phantom support & FAQ — phantom.app
- Developer SDK changelog — docs.phantom.app
- Official announcements & blog — phantom.app
Conclusion & next steps
Integrating Phantom's Wallet SDK unlocks a familiar and secure signing experience for users. Start by detecting the provider, implement a clear connect flow, add transaction previews, and handle errors and rejections gracefully. Use devnet for testing and review the official documentation regularly to catch any API changes.
Next steps
- Read the official Phantom docs — docs.phantom.app.
- Prototype a connect + sign flow on devnet.
- Add end-to-end tests with mocked providers and integrate accessible UX patterns.