feat: portfolio reconciliation with real on-chain balances (SOL2-05) - #5
Conversation
- Created lib/solana/balances.ts: fetches native SOL + SPL tokens via getBalance + getParsedTokenAccountsByOwner, enriches with catalog metadata, filters dust accounts - Created lib/stores/balance-store.ts: Zustand refreshCounter for cross-component post-transaction refresh signals - Created hooks/use-wallet-balances.ts: React hook with auto-fetch on wallet connect/disconnect and Zustand counter subscription - Replaced all mock data in dashboard components: - holdings-table.tsx: real token list with loading/empty states - net-worth.tsx: real totalValueUsd (day change deferred to SOL6) - asset-allocation.tsx: dynamic doughnut chart from actual holdings - deposit-modal.tsx: calls triggerRefresh() after confirmed session - bag-card.tsx: shows Your Position with actual vs. target allocation per asset and drift indicators - USD pricing uses temporary hardcoded price map — real feed in SOL3
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e36da4e283
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (const { account } of tokenAccounts.value) { | ||
| const parsed = account.data.parsed?.info; | ||
| if (!parsed) continue; | ||
|
|
||
| const mint: string = parsed.mint; |
There was a problem hiding this comment.
Aggregate token accounts by mint before computing totals
This loop appends one row per token account, not per mint, so wallets that hold the same mint in multiple accounts (common on Solana) will show duplicated assets and incorrect portfolio math. The net worth/allocation totals are inflated by split accounts, and holdings-table.tsx keys rows by asset.mint, which creates duplicate React keys for this data shape. Collapse balances by mint (summing raw/UI amounts) before building the final array.
Useful? React with 👍 / 👎.
| connection.getParsedTokenAccountsByOwner( | ||
| publicKey, | ||
| { programId: TOKEN_PROGRAM_ID }, | ||
| 'confirmed' |
There was a problem hiding this comment.
Include Token-2022 accounts when fetching SPL balances
The query only targets TOKEN_PROGRAM_ID, so wallets holding Token-2022 assets will silently miss those balances and show understated net worth/allocations. This is user-visible whenever the connected wallet has any Token-2022 mint. Fetch both classic SPL Token Program and Token-2022 program accounts (or use a merged approach) to match the "all SPL tokens" behavior.
Useful? React with 👍 / 👎.
Changes
New Infrastructure
lib/solana/balances.ts— Fetches native SOL (getBalance) + all SPL tokens (getParsedTokenAccountsByOwner), enriches with catalog metadata, filters dust accounts, sorts by USD valuelib/stores/balance-store.ts— Zustand store withrefreshCounterfor cross-component post-transaction refresh signalshooks/use-wallet-balances.ts— React hook with auto-fetch on wallet connect/disconnect, stale-fetch prevention, and Zustand counter subscriptionDashboard — Mock Data Eliminated
holdings-table.tsx— Real token balances with loading skeleton and "No holdings" empty statenet-worth.tsx— RealtotalValueUsdfrom on-chain data (day change deferred to SOL6 snapshots)asset-allocation.tsx— Doughnut chart built dynamically from actual wallet holdingsTransaction Refresh
deposit-modal.tsx— CallstriggerRefresh()after all receipts confirmedbag-card.tsx— Shows "Your Position" section with actual vs. target allocation and drift indicatorsPricing Note
USD pricing uses a temporary hardcoded price map for SOL/USDC/USDT/JUP/BONK/JitoSOL — real price feed integration is deferred to SOL3-01.
Verification
npm run lint✅ (0 errors, 2 pre-existing warnings)npm run build✅ (all 11 pages)grep MOCK_HOLDINGS components/— no results ✅grep 12450.75 components/— no results ✅