fix(side-panel): receive tab loads offline + auto token discovery#50
Merged
BitHighlander merged 8 commits intodevelopfrom Apr 28, 2026
Merged
fix(side-panel): receive tab loads offline + auto token discovery#50BitHighlander merged 8 commits intodevelopfrom
BitHighlander merged 8 commits intodevelopfrom
Conversation
…n handoff) Adopts the Claude Design handoff's visual language for the side panel — darker base (#0b0d10), Inter + JetBrains Mono typography, gold-gradient primary buttons, and tokenized surfaces/lines. Shield badge on the left doubles as both "go home" button and device-status indicator (gold pulsing when transient, green when paired, red when errored), replacing the separate status icon. Bug fixes along the way: - "Add blockchain" picker now hides the dashboard block and is resettable from the home button (state lifted out of Balances into SidePanel). - Asset-detail drawer no longer renders a redundant "Ethereum" title bar; a minimal floating back chevron replaces it. - Header row heights unified at 32px so the Network/Account pills and shield all sit on the same baseline. - TronLink badge next to "Tron" on the asset page (passive, no link — TronLink is an unaffiliated wallet; we just signal protocol use). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Receive tab - Derive UTXO receive addresses locally from xpubs (BIP32 + script-type encoding) instead of round-tripping the device. Page now renders in view-only mode and survives hot-swaps. Adds @scure/bip32, @scure/base, @noble/hashes; new utxoDerive.ts covers BTC (legacy/segwit/native segwit), LTC, DOGE, DASH, BCH cashaddr. - Fix the account dropdown — it looked up pubkeys by .address|.master (both empty on UTXO) so clicks were silent no-ops. Keys by pubkey.note now and shows derived addresses, not truncated xpubs. - Labels read "Account 0 · Native Segwit" etc. so the three BTC entries that all share account index 0 are distinguishable. - Tighten GET_UTXO_ADDRESS match order so note wins over scriptType; multiple p2wpkh accounts can be disambiguated. - Center Receive + AssetDetail vertically; hide the Tokens tab on UTXO chains instead of rendering an empty state. Token auto-discovery - Replace the per-chain prefetch().then(fetch(true)) chains with a single Promise.allSettled([sol, tron, ton]).then(fetch(true)). The old shape raced three parallel fetches that the snapshot staleness guard then discarded — SPL/TRC-20 tokens silently never landed in cachedBalances, and users had to press "Discover Tokens" to see them. - Wire the Discover/Refresh button to REFRESH_ALL_BALANCES so it forces a Pioneer round-trip instead of re-reading the cache. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- script_type, not scriptType: raw pubkey objects use snake_case (matches chainConfig.ts and the SDK request shape in wallet.ts). Reading .scriptType was always undefined, so segwit / native-segwit accounts silently fell through to the p2pkh branch and rendered legacy "1..." addresses for "Account 0 · Native Segwit" entries. - Drop the chrome.storage.session derived-address cache. Local BIP32 + hash160 + encoding is microseconds; the cache only added a stale surface — it was keyed by (networkId, scriptType, note) without the xpub or device id, so a hot-swap in the same session could return the previous device's address for the same note. - Pick the UTXO Receive default address from pubkeyContext.note (the header's selected account) instead of ctxAsset.pubkeys[0]. After SET_ASSET_CONTEXT, the asset's pubkeys field carries every pubkey on the network, and idx===0 was the first configured chainConfig path (legacy BTC), not the user's selection. New effect resolves addressByNote[pubkeyContext.note] when both are present, falls back to pubkeys[0].note only if no context is set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The high-finding from the second review pass: header-selected UTXO accounts weren't reliably carried into Receive. Header fired SET_ASSET_CONTEXT with the selected pubkey, but the background overwrote asset.pubkeys with all network pubkeys, and GET_PUBKEY_CONTEXT only restored by accountIndex — UTXO accounts share an accountIndex (BTC's Legacy / Segwit / Native Segwit all live at account 0), so it always fell back to scoped[0] and Receive opened on the first configured chainConfig path. - NetworkAccountHeader.setAssetContext now puts script_type on the asset alongside accountIndex (snake_case to match raw pubkey shape). - GET_PUBKEY_CONTEXT prefers script_type for the scoped lookup, then falls through to accountIndex (multi-account EVM) and finally scoped[0]. Receive's existing pubkeyContext.note → addressByNote resolver picks up the right entry without further changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous fix carried script_type through SET_ASSET_CONTEXT, but multiple BTC paths share each script_type (chainConfig has account 0 + account 1 both at p2wpkh, plus several legacy accounts). The header also keyed rows on script_type alone, so account-1 Native Segwit collapsed onto account-0 in the dropdown, and even if surfaced separately GET_PUBKEY_CONTEXT would have matched the first p2wpkh (account 0). - AccountItem gains an optional `note` field. headerUtils' BTC and UTXO builders key rows on `pk.note` (unique per chainConfig path) and append "· Account N" to the label only when the script_type repeats. Only the first p2wpkh row is flagged isDefault now. - NetworkAccountHeader.setAssetContext sends `note` alongside `script_type` and `accountIndex`. - GET_PUBKEY_CONTEXT match priority is now note → script_type → accountIndex → scoped[0]. Note is the only identifier that's unique across every chainConfig path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Header dropdown was correct, but two entry points still desynced: - Global Receive / dashboard / asset list: SidePanel.handleGlobalReceive picks a default balance row that has no note or script_type, so the background's GET_PUBKEY_CONTEXT fell through to scoped[0] (legacy BTC). The header could read "Native SegWit" while Receive surfaced a "1..." address. Background SET_ASSET_CONTEXT now defaults a UTXO asset that arrived without note/script_type to the network's first p2wpkh pubkey (else scoped[0]) before storing. - Header auto-default: the useEffect that picks the default account on mount/network-change set local selectedAccountKey only — it never fired SET_ASSET_CONTEXT. Stored context could be stale or absent while the dropdown showed Native SegWit. Effect now calls setAssetContext for the chosen account when a network is selected. Reordered the effect to sit after setAssetContext's useCallback so it isn't referenced in the temporal dead zone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. Header restore by note (was: accountIndex only). UTXO accounts share an accountIndex, so a stored "BTC Native Segwit account 0" round-trip would resolve to whatever chainConfig path landed first at accountIndex 0 — usually legacy. fetchPubkeys now seeds desiredNote / desiredScriptType / desiredAccountIndex from the stored asset context. 2. ASSET_CONTEXT_UPDATED resync. Listener feeds the same desired* state, so within-network context changes now retarget the dropdown instead of leaving selectedAccountKey pinned to a stale row. The auto-select effect compares before firing setAssetContext, so the broadcast back from our own SET_ASSET_CONTEXT can't loop. 3. Per-chain UTXO fallback in background SET_ASSET_CONTEXT. Previously defaulted every bip122:* asset to first p2wpkh, which silently shifted LTC from p2pkh-default (header's items.length===0) to native segwit. Now BTC keeps p2wpkh-first, other UTXO uses scoped[0] — both match the header builders. 4. Auto-default no longer opens AssetDetail. setAssetContext is now a pure data setter (sends SET_ASSET_CONTEXT, returns the asset). The click handlers (handleNetworkSelect / handleAccountSelect) call onSelectNetwork explicitly, so the auto-default effect can sync stored context without an unprompted drawer-open on cold start. 5. AssetDetail derives UTXO addresses. asset.pubkeys[0].address is empty on UTXO and Pioneer's /portfolio stuffs the xpub into b.address (background/index.ts:439), so the address bar / copy / explorer link were either blank or showing an xpub. New UTXO branch in the address-fetch effect calls GET_UTXO_ADDRESS with the asset's note + script_type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The ASSET_CONTEXT_UPDATED listener fires for every SET_ASSET_CONTEXT the background receives — including the header auto-default sync we just added (cold-start restore + ASSET_CONTEXT_UPDATED resync) and dApp-driven chain switches. With onAssetDetailOpen() unconditional in the listener, the drawer would still pop up unprompted even after we made setAssetContext a pure data setter. Drop the auto-open. setSelectedAsset still runs, so an already-open drawer reflects the new context. Explicit opens already go through handleAssetSelect (asset list click, header click), which sets selectedAsset and calls onAssetDetailOpen together. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
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
Two related fixes for the side-panel UI revamp branch.
Receive tab — loads without a device round-trip
sdk.address.utxoGetAddress. Page renders in view-only mode and survives device hot-swaps. Adds@scure/bip32,@scure/base,@noble/hashes; newchrome-extension/src/background/utxoDerive.tscovers BTC (legacy / segwit / native segwit), LTC, DOGE, DASH, BCH cashaddr..address || .master, both empty on UTXO, so clicks were silent no-ops. Now keys bypubkey.noteand shows real derived addresses instead of truncated xpubs.Account 0 · Native Segwitetc. so the three BTC entries that all share account index 0 are distinguishable.GET_UTXO_ADDRESSmatch order tightened sonotewins overscriptType(multiplep2wpkhaccounts can be disambiguated).Asset detail layout
ReceiveandAssetDetailviews vertically (full-height wrappers + biased flex spacers).Tokenstab on UTXO chains entirely instead of showing an empty "No tokens for UTXO chains" panel.Token auto-discovery race
prefetch().then(fetch(true))chains (Solana / Tron / TON). Each fetch's snapshot missed pubkeys still being added by the other two prefetches, and the staleness guard at commit time discarded all but the slowest. Result: SPL / TRC-20 tokens silently never landed incachedBalances, and users had to press "Discover Tokens" to see them. Replaced with a singlePromise.allSettled([sol, tron, ton]).then(fetch(true))so the one committed fetch sees the full pubkey set.REFRESH_ALL_BALANCESso it actually round-trips Pioneer instead of re-reading the cache (it was a placebo before).Test plan
Account 0 · Legacy/Segwit/Native Segwit./portfoliorequest fires (network tab) instead of just re-reading cache.🤖 Generated with Claude Code