fix: show Solana balance and SPL tokens in side panel#42
Merged
BitHighlander merged 3 commits intodevelopfrom Apr 21, 2026
Merged
fix: show Solana balance and SPL tokens in side panel#42BitHighlander merged 3 commits intodevelopfrom
BitHighlander merged 3 commits intodevelopfrom
Conversation
…rows Replace the plain centered spinner + "Loading balances…" text with a multi-layer animated hero spinner (triple counter-rotating rings, pulsing glow, breathing center dot) above skeleton cards that mirror the real asset row layout. Adds a subtle kk-logo watermark behind everything and a teal shimmer sweep that travels across each skeleton card staggered by 150ms. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Three layered bugs were each sufficient to zero out Solana balance display;
all three are fixed together because fixing any one alone leaves the chain
still broken:
1. Wrong CAIP in shortListSymbolToCaip['SOL'] / shortListNameToCaip.solana.
Previously pointed at wrapped-SOL SPL token
(solana:.../solana:so111…, all lowercase). Now points at native SOL
(solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501), matching
pioneer-caip's ChainToCaip and vault-v11's config.
2. Wrong Pioneer endpoint for Solana. /charts/portfolio returns empty
{balances:[], tokens:[]} for any Solana pubkey (verified via direct
curl). Vault-v11 uses /portfolio via pioneer.GetPortfolioBalances,
which returns natives + SPL tokens in one flat array. Route Solana
pubkeys to a third batch hitting /portfolio with the required
key:public-* Authorization header; EVM/UTXO still go through
/charts/portfolio for its richer Zapper/Unchained token data.
3. Response case mismatch. Pioneer echoes CAIP/networkId back in
lowercase regardless of request casing. The side-panel asset list
uses canonical mixed-case network IDs from ChainToNetworkId, so
strict b.networkId === asset.networkId comparisons in Balances.tsx
silently dropped every Solana entry. Rewrite Solana entries to
canonical casing before they enter the merged balances array.
Also eliminates a first-run race: the initial fetchBalancesFromPioneer()
fired before prefetchSolanaPubkey() persisted the Solana pubkey, so run 1
never included Solana at all. Chain a forced refetch on prefetch resolution
so the Solana entry lands in cachedBalances before the UI mounts.
Verified against the live Pioneer API: for the exact address the client
derives from the device at m/44'/501'/0'/0', /portfolio returns
{native SOL + 3 SPL tokens}.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…es to UI Addresses two PR review findings: 1. HIGH: stale earlier fetch could clobber a later fetch's result. The first cold-start fetchBalancesFromPioneer() starts before the Solana pubkey is persisted, and the chained forceRefresh fetch runs in parallel. If the forced fetch finishes first (correctly populating cachedBalances with SOL + SPL tokens) and the original slower fetch finishes later, the original unconditionally overwrote cachedBalances back to the pre-Solana snapshot. Fix: tag each fetch with a monotonic latestFetchId bumped only when real work starts (not for dedup-return paths), and only commit to cachedBalances if this fetch is still the most recent (myFetchId === latestFetchId). Superseded fetches log their discard and return their result to direct callers without touching the cache. 2. MEDIUM: UI never observed late cache updates. Balances.tsx and SidePanel.tsx each fetched GET_APP_BALANCES once and then stopped listening, so the cold-start forced refetch that lands Solana after the panel mounts was invisible to users. Fix: background now emits BALANCES_UPDATED via chrome.runtime.sendMessage every time cachedBalances is successfully written. Balances.tsx, SidePanel.tsx, and Tokens.tsx subscribe to that message and re-fetch so the UI reflects the latest cache without the user having to manually refresh. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
BitHighlander
added a commit
that referenced
this pull request
Apr 21, 2026
…back networkIdToIcon() was mapping Solana to the wrapped-SOL SPL CAIP (solana:.../solana:So111…), which returns 403 on keepkey.info/coins/<base64-caip>.png. The Avatar component fell back to rendering the first letter of the network name — a green "S" badge where the logo should be. Native SOL's slip44:501 CAIP has a real icon (200), matching pioneer-caip's ChainToCaip convention used elsewhere in the codebase after PR #42. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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
Three layered bugs were each sufficient to zero out Solana balance display; all three are fixed together because fixing any one alone leaves the chain still broken.
shortListSymbolToCaip['SOL']pointed at wrapped-SOL SPL token (…/solana:so111…, all lowercase). Now points at native SOLsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501, matching pioneer-caip'sChainToCaipand vault-v11's config./charts/portfolioreturns empty for any Solana pubkey (verified with direct curl against the live API). Vault-v11 uses/portfolioviapioneer.GetPortfolioBalances, which returns SOL + SPL tokens in one flat array. Solana pubkeys are now routed to a third batch hitting/portfolio(authenticated withkey:public-*); EVM/UTXO still use/charts/portfoliofor its Zapper/Unchained token data.ChainToNetworkId. Strictb.networkId === asset.networkIdmatches inBalances.tsxsilently dropped every Solana entry. Solana response entries are now rewritten to canonical casing before they enter the merged balances array.fetchBalancesFromPioneer()fired beforeprefetchSolanaPubkey()persisted the Solana pubkey, so run 1 never included Solana at all. A forced refetch is now chained on prefetch resolution.Ground-truth verification
For the exact address derived from the device at
m/44'/501'/0'/0'(CD9R61PMZFafFQ9QsPZATm74hFyEvYaNtEtwGvvHmRYH), Pioneer's/portfolioreturns:Test plan
[fetchBalances] solana batch: N natives, M tokensafter Solana pubkey is registered🤖 Generated with Claude Code