Portfolio Dashboard
This guide covers the portfolio dashboard, its sections, data sources, and features such as the net worth overview, allocation chart, transaction history, and CSV export.
Overview
The portfolio dashboard (/portfolio) gives users a consolidated view of their net worth across on-chain wallets, manual tokens, and investment commitments. It is available to users with the personal or administrator role.
Dashboard sections
Total assets overview
Displays the aggregate portfolio value with profit/loss when entry prices are available.
Total assets
Sum of on-chain wallet balances + manual tokens. An info tooltip explains what is included.
P&L
Total value - Total invested. Requires entry prices (set via the "Manage Tokens" dialog).
Breakdown
Shows "On-chain & tokens: $X" and "Committed to opportunities: $Y" separately.
Compact trend
A small sparkline (1-month history) rendered inline from portfolio_snapshots.
Key files: src/components/portfolio/PortfolioOverview.tsx, src/hooks/usePortfolioHistory.ts
Allocation by asset
A horizontal bar chart showing the top 6 assets by USD value. Computed client-side from current wallet balances — no additional API call.
Key file: src/components/portfolio/PortfolioOverview.tsx
Performance chart
A larger area chart with selectable time ranges (1M, 6M, 12M, 24M). Data comes from portfolio_snapshots via the get-portfolio-snapshots edge function.
Portfolio snapshots are saved automatically (at most once per hour) when the user visits the dashboard. The save-portfolio-snapshot edge function aggregates all wallet balances for the user and inserts a row into portfolio_snapshots.
Key files: src/components/portfolio/PortfolioChart.tsx, src/hooks/usePortfolioHistory.ts
My Investments
Lists all investments the user has made through InvestHub opportunities. Each card shows: opportunity name, amount, currency, status badge, token/network info, payment method, and a link to the opportunity or block explorer (if a tx_hash exists).
Key file: src/components/portfolio/MyInvestments.tsx
Transaction history
A chronological ledger of investment events (created, paid, refunded). Sorted newest-first and capped at 10 entries by default. Each row includes: date, opportunity name, amount, status, and an explorer/opportunity link.
This section reuses the same get-user-investments data — no additional backend endpoint is required.
Key file: src/components/portfolio/TransactionHistory.tsx
Assets table
Shows all detected on-chain assets aggregated by symbol. Includes balance, price, value, allocation %, and chain badges. Two view modes:
Combined view (
AssetsTable) — all assets in one table, grouped by symbol.Per-wallet view (
WalletAssetsView) — assets grouped by wallet address.
Key files: src/components/portfolio/AssetsTable.tsx, src/components/portfolio/WalletAssetsView.tsx
Wallets list
Lists all tracked wallets (address, source label). Users can add wallets via the "Add Wallet" dialog.
Key file: src/components/portfolio/WalletsList.tsx
Rewards
Rewards functionality is planned but not yet available. The section displays a "Coming soon" state with an icon and short description.
Key file: src/components/portfolio/RewardsSection.tsx
Export CSV
Users can export their portfolio and investment data as a CSV file by clicking the Export CSV button in the dashboard header.
The CSV contains three sections:
Summary — export date, total value, total invested.
Assets — symbol, name, balance, price, value, chain, wallet address.
Investments — date, opportunity name, amount, currency, status, network.
Export is performed entirely client-side (no backend endpoint); the browser generates the file from the current dashboard state.
Key files: src/utils/exportPortfolioCsv.ts, src/pages/PortfolioDashboard.tsx
Data flow
Caching
The dashboard uses two layers of caching for instant display:
Local storage cache
usePortfolioCache stores summary values (total, counts) in localStorage. On next visit the cached data renders immediately while fresh data loads.
Until overwritten by fresh data
React Query
useWalletAssets, usePortfolioData, and usePortfolioHistory use staleTime of 5-10 minutes. No auto-refetch on window focus.
5-10 minutes
View modes
The ViewModeToggle lets users switch between:
All wallets — aggregated view across every tracked wallet.
Single wallet — filters assets and value to one specific wallet.
Related API endpoints
get-wallets
List tracked wallets for the user
get-wallet-assets-v2
Fetch on-chain balances for a wallet (Alchemy)
get-user-investments
List all investment records
get-portfolio-cached
Return cached portfolio summary
get-portfolio-snapshots
Historical snapshots for charting
save-portfolio-snapshot
Save a new snapshot (triggered on visit)
For details on each endpoint, see the API Reference.
Testing
Below is a manual checklist to verify each dashboard feature.
Total assets overview
Log in and navigate to
/portfolio.Verify the "Total assets" headline is visible with an info (i) icon. Hover over the icon to see the tooltip.
If entry prices have been set for any token, confirm P&L appears with a green or red arrow and percentage.
If the user has investments, confirm the breakdown line shows both "On-chain & tokens" and "Committed to opportunities" values.
With no wallets and no investments, verify the value reads
$0.00and no breakdown or P&L appears.
Allocation by asset
Add at least two wallets with different token holdings.
Confirm the "Allocation by asset" bar chart appears below the total value, showing up to 6 assets sorted by value.
Each bar should have a symbol label (left), proportional bar, and USD amount (right).
With zero assets, confirm the section is hidden entirely (no empty chart).
Compact trend sparkline
Ensure the user has at least 2 data points in
portfolio_snapshots(visit the dashboard on two different days, or insert test rows directly in Supabase).Confirm a small sparkline appears under the total assets value.
With fewer than 2 snapshots, confirm the sparkline is hidden.
Performance chart
With snapshot data, select each time range (1M, 6M, 12M, 24M) and verify the area chart updates.
With no data, confirm the "No historical data available yet" empty state appears.
On mobile, verify the time range selector is a dropdown rather than buttons.
Transaction history
Create one or more investments via
/instant-investmentor an opportunity page.Navigate to
/portfolioand scroll to "Transaction history".Verify entries are sorted newest-first, showing date, opportunity name, amount, status badge, and explorer link (when
tx_hashis present).Click an explorer link and confirm it opens the correct block explorer for the network (Base, Polygon, Ethereum, etc.).
With zero investments, confirm the empty-state message "No transactions yet" appears.
Export CSV
With assets and/or investments loaded, click Export CSV in the header.
Verify a file named
portfolio-export-YYYY-MM-DD.csvdownloads.Open the CSV and confirm three sections: Summary (total value, total invested), Assets (symbol, name, balance, price, value, chain, wallet), and Investments (date, opportunity, amount, currency, status, network).
Verify values with commas or quotes are properly escaped.
With no data, confirm the CSV still downloads with headers but empty data rows.
Rewards section
Navigate to
/portfolioand scroll to the right column.Confirm the Rewards card shows a sparkles icon, "Coming soon" heading, and a short description.
Verify there are no mock data items, claim buttons, or "total earned" amounts.
View mode toggle
Add two or more wallets.
Use the view mode toggle to switch between "All wallets" and a specific wallet address.
Confirm the assets table, overview values, and allocation chart update to reflect only the selected wallet.
Switch back to "All wallets" and confirm the aggregated view returns.
Refresh
Click the Refresh button in the header.
Confirm the spinner icon animates while data reloads.
In the browser network tab, verify new calls to
get-wallet-assets-v2andget-user-investments.
Skeleton loading states
Clear the portfolio cache from
localStorage(DevTools > Application > Local Storage > removeportfolio_cachekey).Hard-reload
/portfolio.Confirm skeleton placeholders appear for the overview, investments, assets, and rewards sections until data loads.
Last updated