SolVault is a Solana NFT portfolio and wallet dashboard built with Next.js 14. It connects with browser wallets (Phantom, Solflare, Backpack), loads NFTs through Helius Digital Asset Standard (DAS), shows SOL price (CoinGecko) and collection floors (Magic Eden), supports single and batch NFT transfers, optional Anchor vault deposit/withdraw, and a multi-wallet sidebar with persisted preferences.
| Area | What it does |
|---|---|
| Wallets | Connect via wallet adapter; track extra addresses in the sidebar (read-only labels). Active view switches which address’s NFTs and activity load. |
| NFTs | Grid with filters (collection, rarity trait, “has image”), selection for batch send, detail modal with metadata and vault actions. |
| Transfers | Send one NFT (legacy transaction); batch send in groups of five (versioned transactions, sequential confirm). Import flow moves NFTs from a secondary address when that wallet is connected to sign. |
| Vault (on-chain) | Optional Anchor program: deposit NFT into a PDA-backed vault, withdraw, or batch transfer to multiple recipients (advanced; requires deployed program ID). |
| Activity | Recent signatures for the viewed address; heuristics on token balance changes to label in/out when possible; links to Solscan. |
| Stats | SOL balance (and USD) for the viewed address, filtered NFT count, rough “portfolio hint” when a collection filter + Magic Eden floor are available. |
- Framework: Next.js 14 (App Router), TypeScript, React 18
- Styling: Tailwind CSS, shadcn/ui–style primitives (Radix), custom Solana-themed tokens
- Solana: @solana/web3.js, @solana/spl-token, @solana/wallet-adapter (+ separate Backpack adapter package)
- On-chain app layer: @coral-xyz/anchor (TypeScript client + IDL)
- Data: TanStack Query (caching, mutations), Zustand +
persist(wallets, filters, selection) - Motion: Framer Motion, canvas-confetti on successful sends
- External APIs: Helius DAS (
getAssetsByOwner), Magic Eden collection stats, CoinGecko SOL price
SolVault/
├── app/
│ ├── layout.tsx # Root layout, fonts, background orbs
│ ├── page.tsx # Landing (disconnected) vs dashboard (connected)
│ ├── globals.css # Theme variables, grid, orbs
│ ├── providers.tsx # RPC Connection, Wallet, Modal, React Query
│ └── api/
│ ├── nfts/route.ts # POST → Helius DAS by owner
│ ├── price/route.ts # GET → CoinGecko SOL
│ ├── floor/route.ts # GET → Magic Eden floor by symbol
│ └── activity/route.ts # GET → RPC signatures + parsed txs
├── components/
│ ├── layout/ # Header, Sidebar
│ ├── wallet/ # Connect button, badge, add-wallet modal
│ ├── nft/ # Grid, card, skeleton, detail modal
│ ├── transfer/ # Send, batch send, import modals
│ ├── dashboard/ # Stats, filters, activity feed
│ └── ui/ # Button, Card, Dialog, etc.
├── hooks/ # React Query + wallet mutations + Anchor helpers
├── lib/ # RPC/DAS clients, transfers, batch builder, IDL JSON
├── store/walletStore.ts # Zustand (persisted)
├── types/index.ts # Shared TS types
└── anchor/ # Rust Anchor program + tests (separate workflow)
├── programs/nft_vault/src/lib.rs
├── tests/nft_vault.ts
├── Anchor.toml
└── Cargo.toml
-
NFT list
Browser →POST /api/nftswith{ address }→ server calls Helius JSON-RPCgetAssetsByOwner→ maps assets to the app’sNFTshape → TanStack Query key["nfts", address]. -
SOL price
GET /api/price→ CoinGeckosimple/price→ used in the header and balance USD estimates. -
Floor price
GET /api/floor?symbol=...→ Magic Eden collection endpoint (when a collection filter is active). -
Activity
GET /api/activity?address=...→getSignaturesForAddress+getParsedTransactionon your configured RPC → compares pre/post token balances (decimals0) to guess NFT in/out. -
Send / batch send
Client builds SPL token (NFT) transfer instructions; wallet signs. Batch paths useVersionedTransactionchunks of up to five mints, then send/confirm with a short delay between chunks. -
Vault
useVaultProgrambuilds an AnchorProgramfromlib/idl/nft_vault.jsonandNEXT_PUBLIC_VAULT_PROGRAM_ID, thendepositNFT/withdrawNFT/batchTransferwith PDAs andremainingAccountsas implemented in the hook.
Copy .env.local.example to .env.local (or use .env; both are gitignored in this project). All public vars use the NEXT_PUBLIC_ prefix so the browser can read them where needed.
| Variable | Purpose |
|---|---|
HELIUS_API_KEY |
Recommended for NFT API: Helius key used only on the server for DAS (/api/nfts). Not exposed to the browser. |
NEXT_PUBLIC_HELIUS_API_KEY |
Alternative: same key, but embedded in the client bundle. Used to build https://{cluster}.helius-rpc.com/?api-key=... for DAS if you do not pass a full Helius URL in NEXT_PUBLIC_RPC_URL. |
NEXT_PUBLIC_SOLANA_NETWORK |
mainnet-beta, devnet, or testnet. Must match the cluster your wallet uses. Use devnet, not devnet-beta. |
NEXT_PUBLIC_RPC_URL |
Preferred RPC for ConnectionProvider and server routes that use createConnection. For NFT loading, either this must be a Helius URL (with api-key) or NEXT_PUBLIC_HELIUS_API_KEY must be set—plain public Solana RPC does not implement DAS. |
NEXT_PUBLIC_MAGIC_EDEN_API |
Base URL for Magic Eden (default mainnet v2 in example). |
NEXT_PUBLIC_COINGECKO_API |
Base URL for CoinGecko v3. |
NEXT_PUBLIC_VAULT_PROGRAM_ID |
Deployed Anchor program id; required for vault UI actions. Update lib/idl/nft_vault.json address / metadata when you deploy your own program. |
After changing env vars, restart npm run dev.
| Command | Description |
|---|---|
npm install |
Install dependencies. |
npm run dev |
Development server (http://localhost:3000). |
npm run clean |
Deletes .next (fixes many stale ChunkLoadError / timeout issues after restarts). |
npm run dev:clean |
Clean then next dev. |
npm run build |
Production build. |
npm run start |
Serve production build. |
npm run lint |
ESLint. |
Anchor (from anchor/):
cd anchor
anchor build
anchor deploy --provider.cluster devnet # example
anchor testInstall Solana CLI, Anchor, and avm per Anchor docs if you have not already.
Located under anchor/programs/nft_vault/. High level:
deposit_nft— Moves one NFT from the user’s ATA into a vault ATA keyed by aVaultEntryPDA (seeds: ["vault", owner, mint]).withdraw_nft— Owner signs; NFT returns to user; vault ATA closed;VaultEntryclosed (rent returned).batch_transfer— Up to five(mint → recipient)operations in one instruction using remaining accounts (vault entry, vault ATA, recipient ATA per slot); closes vault state after transfer.
IDL consumed by the app lives at lib/idl/nft_vault.json. Regenerate from your deployment when the program id or interface changes (when anchor idl works in your toolchain).
- Landing when no wallet is connected; dashboard when connected.
- Header: logo, live SOL quote, wallet badge, connect control.
- Sidebar (lg+): tracked wallets, “Add wallet” for extra addresses.
- Dashboard panels use a blue panel / green accent theme for stats, filters, and activity.
- Compressed NFTs are listed when Helius returns them; SPL one-click send is disabled in the UI for compressed items (different transfer mechanics).
| Symptom | Things to check |
|---|---|
| No NFTs / NFT API 500 | Set HELIUS_API_KEY or NEXT_PUBLIC_HELIUS_API_KEY (or a Helius NEXT_PUBLIC_RPC_URL with a real api-key). Public RPC URLs (solana.com, ankr, etc.) do not support DAS. Match NEXT_PUBLIC_SOLANA_NETWORK to the wallet cluster. |
bigint: Failed to load bindings |
Harmless Node fallback from a dependency during next build / dev; not the cause of empty NFTs. |
| “Maximum update depth” (fixed in tree) | Effects must not depend on unstable refs (e.g. wallet publicKey object identity, or entire useMutation() result). |
| ChunkLoadError / layout.js timeout | npm run clean, restart dev server, hard refresh or private window; only one next dev on port 3000. |
| Import / batch send fails | Source wallet must be connected in the adapter to sign; recipient must match expectations (import modal checks primary address). |
| IDL / vault errors | NEXT_PUBLIC_VAULT_PROGRAM_ID and IDL address must match the deployed program; wallet must be on the same cluster. |
NEXT_PUBLIC_*values are exposed to the browser; treat API keys as rate-limited public keys where possible.- Prefer server-only secrets for sensitive operations if you extend the app (this template uses public RPC/DAS patterns).
- Audit any on-chain program before mainnet use; vault and batch paths move real assets.
This project is released under the MIT License. You may use, modify, and distribute it with minimal restrictions; the software is provided as-is without warranty.
Optional: Replace SolVault contributors in LICENSE with your legal name or organization if you want a specific copyright holder. If you prefer copyleft (derivatives must stay open) or stricter patent terms, consider Apache-2.0 or GPL-3.0 instead—those require changing LICENSE and package.json’s "license" field to match.
The Anchor program under anchor/ is part of the same repository license unless you add a separate notice (some teams use Apache-2.0 for on-chain code only).