Skip to content

fix: restore TON + Tron USD on dashboard#45

Merged
BitHighlander merged 2 commits intodevelopfrom
fix/ton-tron-usd-value
Apr 22, 2026
Merged

fix: restore TON + Tron USD on dashboard#45
BitHighlander merged 2 commits intodevelopfrom
fix/ton-tron-usd-value

Conversation

@BitHighlander
Copy link
Copy Markdown
Collaborator

Summary

Dashboard / Asset / Send pages showed `$0.00` USD for TON and Tron even with correct balance quantities (e.g. `15.7018 TON`). Vault's own UI showed proper USD (`$20.30` for the same 15 TON), so it wasn't a Pioneer data gap — it was an auth mismatch on our side.

Root cause

Pioneer's `/charts/portfolio` silently omits TON and Tron entries when called without a `?key=key:public-*` query param. EVM / UTXO / Cosmos / tokens come back unauthenticated, which made the omission non-obvious. Our previous workaround was per-address `/ton/accountInfo` + `/tron/accountInfo` calls, which return raw balance but no price — exactly what we saw on the dashboard.

Solana's `/portfolio` path already used the same key-based auth, which is why Solana USD always worked.

Fix

  • `chartsPortfolioUrl` now appends `?key=key:public-${Date.now()}`.
  • TON and Tron pubkeys flow through the main `fetchBatch` — dropped the dedicated `fetchTonBatch` / `fetchTronBatch` helpers (≈90 lines). Three concurrent batches → two.
  • Added per-entry log for TON / Tron rows coming back from Pioneer so future triage can see `price=1.37 value=20.14`.

Included bonus fixes

  • Icon flicker (`Failed to set icon './icon-128-online.png': Failed to fetch`): MV3 service-worker cold-start race. `updateIcon` now dedupes no-op calls (checkKeepKey fires every 5s, 99% are redundant) and retries once after 500ms on error.
  • Log-strip pass in `injected.ts` + `content/index.ts` (console.* noise removal, no behavior change).

Test plan

  • `make build` — 9 workspaces green.
  • Live: curl `/charts/portfolio?key=…` returns TON `priceUsd: 1.37, valueUsd: 20.14`.
  • Dashboard: TON row shows `~$20`, TRX row shows price > 0.
  • Icon no longer flickers on cold start.
  • Verify on a wallet with non-zero Tron balance (Pioneer may filter 0-balance Tron at the portfolio level).

🤖 Generated with Claude Code

BitHighlander and others added 2 commits April 21, 2026 19:07
Pioneer's /charts/portfolio endpoint silently omits TON and Tron
entries when called without a queryKey — the same call still serves
EVM / UTXO / Cosmos / tokens unauthenticated, so the omission wasn't
obvious. The previous workaround was per-address /ton/accountInfo +
/tron/accountInfo calls, which return raw balance but no price. Net
result: balance quantity rendered correctly once the earlier
unit-conversion bug was fixed, but valueUsd + priceUsd stayed at 0 —
dashboard showed "$0.00" on both chains even though the vault's own
UI displayed the correct USD (~$20 for 15 TON).

Curl confirmed: hitting /charts/portfolio with ?key=key:public-<ts>
returns TON with priceUsd: 1.37, valueUsd: 20.14, icon, etc. Solana's
/portfolio path already used the same key-based auth scheme (that's
why Solana USD always worked).

Change:
- Append ?key=key:public-${Date.now()} to chartsPortfolioUrl so
  Pioneer returns the full portfolio including TON + Tron with prices.
- Drop the dedicated fetchTonBatch and fetchTronBatch helpers (≈90
  lines); TON and Tron pubkeys flow through the main fetchBatch now.
  Three concurrent batches → two, fewer round-trips per fetch cycle.
- Added a per-entry log for TON/Tron rows coming back from Pioneer so
  future triage can see price/value arriving.

Unrelated fixes absorbed into this branch:
- Icon flicker / "Failed to set icon './icon-128-online.png': Failed
  to fetch": chrome.action.setIcon occasionally fails on MV3 service
  worker cold start. updateIcon now dedupes no-op calls (checkKeepKey
  was re-invoking every 5s) and retries once after 500ms on error.
- Log-strip pass in injected.ts + content/index.ts (console.* noise
  removal, no behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…icon

Addresses reviewer concerns on PR #45.

1. Partial-portfolio regression
   Previous commit dropped fetchTonBatch / fetchTronBatch entirely,
   trusting /charts/portfolio to always return those chains now that
   it's authed. But Pioneer has been observed to silently omit TON /
   TRON rows in edge cases (rate-limit, 0-balance, upstream hiccups)
   while returning the rest of the portfolio. That would leave
   rawBalances non-empty and commit it over cachedBalances without
   those rows — chain flickers off entirely on the dashboard.
   Re-introduce /api/v1/{ton,tron}/accountInfo as a TARGETED patch:
   only fires for specific pubkeys the portfolio call omitted, never
   duplicates coverage, and stays price-0 on the fallback row — a
   priceless row beats no row.

2. Stale-icon retry
   setIconWithRetry(iconPath, attempt) replayed the captured path after
   the 500ms backoff, so if KEEPKEY_STATE flipped during the wait the
   retry could paint the previous icon back. Flatten to a single
   updateIcon() that re-derives iconPath from CURRENT state on each
   call, and on error reschedule updateIcon() itself rather than
   replaying a stale captured path. An iconRetryPending guard stops
   runaway retries when the API stays unhappy.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@BitHighlander
Copy link
Copy Markdown
Collaborator Author

Both concerns addressed in `183b68d`.

#1 Partial-portfolio regression. Re-introduced the per-address fallback but as a targeted patch, not the old parallel batch. Flow:

  1. Send everything through authenticated `/charts/portfolio` (happy path, prices + values).
  2. Diff the response against the TON/TRON pubkeys we asked about — match by (lowercased networkId, lowercased pubkey/address).
  3. For any pubkey the portfolio silently dropped, fire `/api/v1/{ton,tron}/accountInfo` and push a priceless fallback row flagged with `_fallback: true`.

Degraded state is "TON row shows quantity, `$0.00` USD" instead of "TON row disappears". Price re-enters as soon as Pioneer's portfolio call covers that pubkey again on the next cycle.

#2 Stale-icon retry. Scrapped `setIconWithRetry(capturedPath)`. New flow:

  • `updateIcon()` derives the path from CURRENT `KEEPKEY_STATE` every call.
  • On error, it clears the dedupe key and reschedules `updateIcon()` itself — not the stale captured path.
  • An `iconRetryPending` guard prevents runaway retries when the API stays unhappy; the next legitimate state change overrides it.

So if state flips to disconnected during the 500ms backoff, the retry reads the new state and paints the offline icon instead of replaying a stale online icon.

Residual risk on regression coverage — still no e2e spec for either path, agreed. Happy to add one if the e2e suite grows TON/Tron fixtures; otherwise leave as a follow-up.

@BitHighlander BitHighlander merged commit fab0f75 into develop Apr 22, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant