On Solana, users hold keys in a wallet (browser extension, mobile app, or hardware device). Your dApp never receives a private key. It discovers wallets, requests connection, builds unsigned transaction messages, and asks the wallet to sign. The wallet shows a human-readable approval UI; after the user confirms, your app sends the signed payload to RPC and tracks confirmation.
This page is the conceptual map for Wallet Integration - adapter layers, connect and disconnect, transaction and message signing, autoconnect and session UX, hardware and mobile paths, and the security boundaries that keep keys out of your process on Agave 4.1.1-aligned stacks.
Wallet integration is a capability bridge: discover a standard wallet, establish an origin-scoped session, sign only what the user approves, then broadcast with your own RPC and UX. Secrets stay inside the wallet.
Insight: Every user-paid action (swap, mint, transfer, game move) and most off-chain auth (SIWS) depend on correct connect state, fee-payer identity, cluster alignment, and honest previews. Mistakes produce failed txs, wrong-account data, or phishing-grade UX.
When to Use This Model: Shipping a React or Next.js dApp, wiring @solana/kit 7.0.0 message pipelines to browser signers, designing session UX, supporting Ledger or mobile, or auditing where keys and signatures are allowed to live.
Limitations/Trade-offs: Connect is not a blank check for future spends; each sensitive action needs approval. Adapters differ by product; mobile and hardware add latency and failure modes. Server auth needs separate signature verification, not "connected in React" alone.
A Solana wallet is software or hardware that holds keypairs (or seed-derived accounts), tracks cluster context (mainnet, devnet, testnet), and exposes a narrow API: connect, disconnect, sign transactions, and optionally sign arbitrary messages.
The dApp owns product UX, instruction construction, RPC reads, simulation, submission, and confirmation. It must not take seed phrases or private keys if it claims non-custodial signing.
User key material | v Wallet (extension / mobile / hardware) | connect, signTransaction, signMessage v Adapter / Wallet Standard bridge | React context: publicKey, connected, signers v dApp (@solana/kit build + RPC send + UI)
Wallet Standard (and the multi-adapter ecosystem around it) is how browsers discover installed wallets without hardcoding one brand. Extensions register; your app lists them for user selection. On mobile, Mobile Wallet Adapter (MWA) and deep links play a similar role with different plumbing.
In React, @solana/wallet-adapter-react (ConnectionProvider, WalletProvider, useWallet, optional modal) is still common. Newer scaffolds may use @wallet-ui/react. Both share the same contract: client-only providers, registered adapters, and hooks for publicKey, connected, connecting, select, connect, disconnect, and signing.
Connection means this origin may request signatures for a chosen account. It is not an on-chain transaction and not server login. publicKey is the client session identity for address-keyed state.
Cluster alignment is mandatory: wallet network, RPC URL, and program IDs must match. Mainnet wallet against devnet RPC yields opaque simulation and send failures. Show a cluster badge and block send on mismatch when you can detect it.
Read-only work (balances, explorers, marketing) uses RPC without connect. Gate only actions that need a fee payer or a user signature.
Mount providers only on the client ("use client" in Next.js App Router). Register popular wallets so the modal is not empty. Pair adapter signing with @solana/kit 7.0.0 for message construction and RPC.
Adapters map Wallet Standard events into React context. useWallet() is the app surface. Log wallet?.adapter.name in support tooling for wallet-specific bugs.
Your app builds an unsigned message: fee payer = connected pubkey, recent blockhash, instructions (and compute budget as needed). Optionally simulate before the wallet modal. Call the wallet sign API. After signatures return, you still serialize, send via RPC, and confirm.
signMessage covers off-chain bytes (for example Sign-In with Solana). The wallet returns an ed25519 signature. Servers must verify signature, domain binding, and nonce or expiry; never treat "client says connected" as auth.
Common failure modes: fee payer ≠ connected account; stale blockhash after slow hardware or mobile approval (rebuild); oversized txs without ALTs; assuming signTransaction also confirms on-chain (signing is local; send is yours).
Bridge the wallet into kit's TransactionSendingSigner (or equivalent) so the typed message path and adapter stay one pipeline.
autoConnect restores the last chosen wallet on mount (often a localStorage preference, never secret keys). That reduces friction for return visits.
Pattern
When it fits
Risk if misused
autoConnect + loading gate
Consumer dApps, frequent txs
Flash of "disconnected" UI if you ignore connecting
Manual connect only
High-security admin, rare ops
Extra friction for daily users
Lazy connect
Marketing sites; connect on first sign intent
Higher conversion than landing-page modals
SIWS + server cookie/JWT
Personalized backends, gated APIs
Forged sessions if verification is client-only
Show restoring session while connecting is true. Do not open the full connect modal on every load. Always expose disconnect. Treat SIWS as a separate layer: adapter reconnect does not re-prove identity to your API until you re-verify a signature or a still-valid server session.
On desktop, Ledger users often connect through a broker wallet that forwards previews to the device. App code stays adapter-driven; latency and multi-step confirmation increase. On mobile, extension injection differs: use MWA, deep links, or injected in-app browsers. Lengthen timeouts. Test on real devices. Seeker and Seed Vault keep the same rule: dApp requests signatures; device holds seeds. Batch instructions so one product action does not mean many device prompts.
Treat wallet integration as a security boundary, not only UI chrome.
Boundary
Allowed in dApp
Must stay in wallet / device
Private keys / seeds
Never
Always
Connection preference
localStorage OK
N/A
Transaction bytes to sign
Build and display intent
Sign after user review
Off-chain auth message
Compose with domain + expiry
Sign; server verifies
Session cookies
After SIWS verify
Not a substitute for keys
Origin isolation: Connection is per site. Do not teach users to export seeds to "fix" a bad connection.
Preview honesty: Simulate and summarize token deltas and fee payer before the wallet prompt when you can.
No secret storage: Only public keys, cluster preference, and UI flags in browser storage.
Server trust: Verify SIWS (ed25519, domain binding, replay protection) server-side for authenticated APIs.
Cluster and program IDs: Config must not silently point a mainnet wallet at the wrong cluster or program.
Keep wallet-adapter or wallet-ui versions aligned with kit and your scaffold. Dual-stack (kit reads + adapter sign) is fine if documented; dual send paths are not. Log adapter name, cluster, and truncated pubkey for support. QA two software wallets plus hardware and mobile for consumer apps. Connect modals need focus trap, keyboard nav, and friendly reject copy.
On an Agave 4.1.1-aligned stack (CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, @solana/kit 7.0.0), exercise connect, reject, disconnect, delayed sign (stale blockhash), and cluster mismatch before mainnet.
"Connect means the site can spend freely." Connect only opens a session for later prompts. Each transaction still needs user approval.
"I can store the private key in localStorage for convenience." That turns a non-custodial product into a malware honeypot. Never.
"signTransaction also lands the transaction." Signing returns signatures; your code must send and confirm.
"One wallet brand is enough." Users arrive with Phantom, Solflare, Ledger-brokered setups, and mobile wallets. Discover multiple options.
"autoConnect is a security feature." It is a UX feature. High-security admin UIs may prefer manual connect every time.
"Connected in React equals logged in on the server." Server sessions need SIWS verification. Client publicKey alone is forgeable in requests.
"Mobile is the same API as the extension." Deep links and MWA change session and timeout behavior.
"Hardware wallets need special instruction bytes from the dApp." Usually the same message is signed; timers and previews must adapt, not a second protocol.
"RPC network and wallet network can disagree if I retry hard enough." Align cluster badge, RPC, and wallet network first.
"Disconnect only hides the address chip." Disconnect must drop the adapter session and clear pubkey-keyed application state.
It holds keys, shows approval UI, and returns signatures. The dApp discovers the wallet, builds messages, requests signatures, and handles RPC send and confirmation.
What is Wallet Standard?
A browser discovery convention so multiple wallets can register and dApps can list them without hardcoding one extension API.
wallet-adapter vs wallet-ui?
@solana/wallet-adapter-react remains widely used. @wallet-ui/react appears in newer scaffolds. Both sit above Wallet Standard-style discovery; pick one stack and keep versions aligned with kit.
When should I prompt the user to connect?
Prefer lazy connect: prompt when the user starts an action that needs signing or a pubkey-specific view. Do not block pure read content. See Connecting & Disconnecting.
What should disconnect clear?
Adapter session plus app caches and stores keyed by the previous publicKey. Leave public marketing data alone.
How do I sign with @solana/kit 7.0.0 and a browser wallet?
Build the message with kit (fee payer, blockhash, instructions), bridge the wallet into a kit-compatible signer, sign, then send via kit RPC. See Signing Transactions & Messages.
signTransaction vs signMessage?
signTransaction (or sign-all) authorizes on-chain transaction bytes. signMessage signs off-chain bytes for auth or attestations. Only signatures return to the page.
Should I enable autoConnect?
Yes for most consumer apps, with a loading gate while connecting is true and a visible disconnect. Prefer manual connect for high-security admin. See Autoconnect & Session UX.
How does SIWS relate to wallet connect?
Connect gives the client a publicKey. SIWS proves control of that key to your backend via a domain-bound message and server-side ed25519 verification. They are not the same layer.
Why do hardware wallet users fail more often?
Longer approvals expire blockhashes; short UI timeouts abort early; multi-prompt flows cause abandonment. Extend timers, rebuild messages, and batch instructions. See Hardware & Mobile Wallets.
How should mobile wallets integrate?
Use MWA and deep-link return paths for your stack (web vs React Native). Test on devices. Do not assume desktop extension injection.
Do I need a wallet to read balances?
No. Public RPC reads work with any address. Require connect only when the user must sign or when you personalize UI around their pubkey.
What is the main security boundary?
Secret key material never enters dApp memory or storage. The dApp may hold public keys, preferences, unsigned messages it built, and signatures returned after user approval.
How do I handle user rejection?
Treat cancel as normal: friendly copy, enable retry, no stack-trace banners. Do not auto-resubmit in a loop.
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 15, 2026