feat(portfolio): multi-LST page with RPC snapshot and holdings API - #274
Open
EjembiEmmanuel wants to merge 5 commits into
Open
feat(portfolio): multi-LST page with RPC snapshot and holdings API#274EjembiEmmanuel wants to merge 5 commits into
EjembiEmmanuel wants to merge 5 commits into
Conversation
Add portfolio snapshot endpoint and RPC aggregation for all LSTs. Extend holdings API with lstSymbol; share block helpers across routes. Rebuild /portfolio UI with asset selector, snapshot stats, and per-LST protocol table/chart. Switch balance route to RPC; parameterize Ekubo by LST. Add Portfolio to sidebar and mobile nav; fix UserIcon hover animation.
Contributor
|
@EjembiEmmanuel is attempting to deploy a commit to the unwrap labs's projects Team on Vercel. A member of the Team first needs to authorize it. |
akcharlie24
reviewed
May 25, 2026
| XLBTC: rates.xlbtc, | ||
| XSBTC: rates.xsbtc, | ||
| XTBTC: rates.xtbtc, | ||
| XSTRKBTC: rates.xwbtc, |
Collaborator
There was a problem hiding this comment.
why are we proxying over here ?
Contributor
Author
There was a problem hiding this comment.
not sure I understand what you mean here
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR restores and substantially upgrades the
/portfolioexperience so it works for every supported LST (xSTRK and all BTC LSTs), not only xSTRK on STRK. Balances and VIP valuation are driven by on-chain RPC reads instead of a GraphQL points backend that did not scale cleanly to multi-asset. A new snapshot API plus PostgreSQL cache reduces repeated RPC load and improves perceived performance. Historical holdings logic is extracted into shared server libraries so chart, block, and per-block routes stay consistent. The holdings chart is fixed so changing the time range (7d / 30d / 90d / 180d) no longer filters against a hardcoded November 2024 window or drops data incorrectly.Motivation (why this work exists)
pointsApolloClient) for live positions. That path is harder to extend per LST and couples UI freshness to an indexer. RPC reads reuse the same contract helpers as the rest of the app and align balances with what users see on-chain.2024-11-25anchor, so switching ranges often showed empty or wrong series. Server-side range selection and honest “N days available” messaging fix that.Changes (what changed and why)
Environment and documentation
.env.sampleNEXT_PUBLIC_PORTFOLIO_RPCNEXT_PUBLIC_RPC_URLwhen unset.CLAUDE.mdDatabase and caching
prisma/schema.prismaPortfolioSnapshotCachemodel (address,snapshotJSON,blockNumber,fetchedAt)prisma/migrations/20260527120000_add_portfolio_snapshot_cache/migration.sqlportfolio_snapshot_cachetable + unique address index +fetchedAtindexfetchedAtsupports future cleanup or monitoring of stale rows.src/app/api/portfolio/snapshot/[address]/route.tsGETendpoint with 2 min fresh TTL, up to 10 min stale-while-revalidate background refresh,X-Portfolio-Cache: hit|stale|missShared server libraries (new)
src/lib/portfolio-types.tsPortfolioData,PortfolioBalance, LST token keys (XSTRK…XSTRKBTC), USD helpers (portfolioLstUsdValue,portfolioDefiPieSlices), asset→protocol mapping (isProtocolAllowedForAsset,getHoldingsKeyForProtocol)src/lib/portfolio-rpc.tsbuildPortfolioDataForLst,getAllLstTokenBalancesRpc,getPortfolioBalanceRpc,getLstConfigBySymbolLST_CONFIG. Parallel fetch across all LST assets; per-LST failures degrade toemptyPortfolioData()instead of failing the whole page.src/lib/portfolio-blocks.tsgetBlocksWithExchangeRatesForDays,BlockInfo, block-for-time estimation, exchange rate retries/api/blocks/[nDays]. Shared so holdings history and blocks API use identical sampling gaps (7d→1d, 30d→3d, 90/180d→7d) and retry behavior.src/lib/portfolio-holdings-history.tsfetchHistoricalHoldingsForAsset, transient RPC error detection, per-block protocol serieslstSymbolso historical chart works for any LST. Retries on JSON/connection errors reduce flaky chart loads.src/lib/portfolio.tsgetAllLstTokenBalancesRpc/getPortfolioBalanceRpcwrappers;buildPortfolioSnapshotcomposes RPC balances + native tokens + Pragma USD rates + category totalsgetPortfolioBalance/getAllLstTokenBalancesremain for legacy/points flows; snapshot and balance routes explicitly use RPC paths.buildPortfolioSnapshotis the canonical payload written to cache.Constants and RPC provider
src/constants/index.tsgetPortfolioProvider()usingNEXT_PUBLIC_PORTFOLIO_RPC→RPC_URL→NEXT_PUBLIC_RPC_URLAPI route refactors
src/app/api/blocks/[nDays]/route.tsgetBlocksWithExchangeRatesForDays(~150 lines removed)src/app/api/holdings/[address]/[nDays]/route.tsportfolio-blocks+portfolio-holdings-history; addslstSymbolquery param (defaultSTRK)axiosself-calls toHOSTNAMEfor blocks—same process, fewer failure modes.src/app/api/block-holdings/[address]/[block]/route.ts@/lib/portfolio-holdings-historyinstead of holdings routesrc/app/api/portfolio/balance/[address]/route.tsgetAllLstTokenBalances→getAllLstTokenBalancesRpcsrc/app/api/portfolio/isVIP/[address]/route.tsPORTFOLIO_LST_TOKEN_KEYSwithportfolioLstUsdValue; includesstrkbtcin native BTC USD sumClient state
src/store/portfolio.store.tsportfolioAssetSymbolAtom(defaultSTRK);portfolioSnapshotAtomvia TanStack Query →/api/portfolio/snapshot/:address(2 min stale)src/store/ekubo.store.tsgetEkuboHoldingsaccepts optionallstAddress(defaults xSTRK)src/store/defi.store.tsDAppHoldings/ protocol types compatible with new holdings pipeline.Portfolio UI
src/app/portfolio/_components/portfolio-asset-selector.tsxportfolioAssetSymbolAtomandlstConfigAtomsrc/app/portfolio/_components/portfolio-page.tsxlstSymbol+chartFilter; abort in-flight requests on asset/range change; protocol table filtered viaisProtocolAllowedForAsset; usescreatePortfolioColumns; snapshot-driven layoutuserAddressAtomfor consistency with rest of app.src/app/portfolio/_components/stats.tsxportfolioSnapshotAtom(USD, per-category, selected LST balance)src/app/portfolio/_components/defi-holding.tsxportfolioDefiPieSlices(snapshot.byLst[...])instead of individual Jotai protocol atomslstSymbol.src/app/portfolio/_components/chart.tsxstrkfarm/trovesHyperstack keys; merges Nostra into singlenostraseriessrc/app/portfolio/_components/table/columns.tsxcreatePortfolioColumns(lstSymbol);PortfolioDAppAmountCellpicks correct token column and decimals; removed debugconsole.logsrc/app/portfolio/_components/table/data-table.tsxportfolio-page.Chart time-range fix (commit
c66cd95)src/lib/portfolio-holdings-history.tsnDayssrc/app/portfolio/_components/portfolio-page.tsxholdingsTimeRange/ coordinates fetch withchartFiltersrc/app/portfolio/_components/chart.tsxavailableDaysvsrequestedDayshint (“Showing N days available”)Previous bug: Chart filtered with
referenceDate = new Date("2024-11-25"), so after that calendar window, 7d/30d filters often returned[]even when the API had data.Navigation and polish
src/components/sidebar-menu-items.tsxsrc/components/mobile-nav.tsxsrc/components/ui/user.tsxsrc/components/ui/chart.tsxvalueSuffixandvalueDecimalsArchitecture (data flow)