feat(wallet): surface multi-chain balances in Settings#2686
Conversation
Extend walletApi.ts with BalanceInfo type and fetchWalletBalances() calling openhuman.wallet_balances. Add 3 Vitest tests (happy path, error propagation, empty array). Add walletBalances.* and pages.settings.account.walletBalances* keys to en.ts, en-4.ts, and all 12 non-English chunk-4 locale files. Update wallet.execution how_to in catalog.rs to reference Settings > Wallet Balances.
New panel under Settings > Account with loading/error/empty/loaded states. Chain badges (EVM/BTC/SOL/TRX) with color-coded design tokens; truncated address (first 6 + last 4) with copy-to-clipboard; formatted balance + symbol; providerStatus chip for missing providers; Refresh button. Vitest+RTL tests cover all five UI state describe-blocks including Retry re-invocation.
Add wallet-balances route and accountSettingsItems entry in Settings.tsx so the panel is reachable at Settings > Account > Wallet Balances. Update TEST-COVERAGE-MATRIX.md row 13.1.4 and bump covered/total counts.
Apply Prettier auto-format, collapse duplicate-named imports into a single type+value import, switch the panel test to a static import + JSX render (no dynamic import per the app/src ban), and correct the truncated-address expectation to match the implementation's first-6 + last-4 character form. Vitest 14/14 pass; pnpm compile, lint, rust:check all green.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (18)
✅ Files skipped from review due to trivial changes (7)
📝 WalkthroughWalkthroughThis PR implements a new Settings > Account > Wallet Balances panel displaying multi-chain wallet balances fetched via ChangesWallet Balances Settings Panel
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/components/settings/panels/WalletBalancesPanel.tsx`:
- Around line 139-151: loadBalances currently allows out-of-order async
responses to overwrite newer state; add a sequencing guard (e.g., a local
incrementing requestId stored in a ref or closure) so each call to loadBalances
increments the id and only commits results (setBalances/setError/setLoading) if
the response id matches the latest id; implement the same pattern for the other
async calls noted (the other balance/fetch handlers around the later
occurrences) to ignore stale responses and ensure only the freshest fetch
updates state.
- Line 199: The UI currently renders the raw backend error variable `error`
directly in WalletBalancesPanel, which bypasses localization and may expose
backend phrasing; replace this with a localized user-facing string via the
`useT()` hook (e.g., `t('wallet.balance_error')` or an appropriate key), and
stop rendering `{error}` verbatim in the paragraph; if you need to surface
details for diagnostics, add a sanitized/non-user-facing place such as a
console.warn, a data-attribute (e.g., `data-error-detail`) or an internal log
entry rather than visible text; ensure `useT` is imported and used in the
`WalletBalancesPanel` component and keep the displayed message generic and
localized while preserving sanitized error detail only for diagnostics.
In `@app/src/pages/Settings.tsx`:
- Around line 239-245: The route id 'wallet-balances' is registered in the
settings menu but missing from the SettingsRoute union and getCurrentRoute()
mapping in useSettingsNavigation, causing the page to fall back to 'home';
update the SettingsRoute type (in useSettingsNavigation.ts) to include
'wallet-balances' and add a corresponding case/branch in getCurrentRoute() that
returns 'wallet-balances' when the path or route id matches, ensuring
breadcrumbs/back behavior resolves to the new route; confirm any related
switch/lookup tables (e.g., routeToTitle or routeToPath helpers) also include
'wallet-balances' so navigation and labels render correctly.
In `@app/src/services/walletApi.ts`:
- Around line 66-71: The doc for fetchWalletBalances() is contradictory about
handling an unconfigured wallet—update the comment to match the actual
implementation: either (A) state that fetchWalletBalances calls wallet.balances
via the core RPC relay and will surface the core RPC error when the wallet is
not configured (do not claim it returns an empty array), or (B) change the
implementation to catch the specific core error and return an empty array, and
then document that behavior; refer to fetchWalletBalances and the
wallet.balances/core RPC relay in the comment so callers know whether they
should expect an error or an empty array.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a0ec19da-97e2-4299-9033-8768354a8413
📒 Files selected for processing (21)
app/src/components/settings/panels/WalletBalancesPanel.tsxapp/src/components/settings/panels/__tests__/WalletBalancesPanel.test.tsxapp/src/lib/i18n/chunks/ar-4.tsapp/src/lib/i18n/chunks/bn-4.tsapp/src/lib/i18n/chunks/de-4.tsapp/src/lib/i18n/chunks/en-4.tsapp/src/lib/i18n/chunks/es-4.tsapp/src/lib/i18n/chunks/fr-4.tsapp/src/lib/i18n/chunks/hi-4.tsapp/src/lib/i18n/chunks/id-4.tsapp/src/lib/i18n/chunks/it-4.tsapp/src/lib/i18n/chunks/ko-4.tsapp/src/lib/i18n/chunks/pt-4.tsapp/src/lib/i18n/chunks/ru-4.tsapp/src/lib/i18n/chunks/zh-CN-4.tsapp/src/lib/i18n/en.tsapp/src/pages/Settings.tsxapp/src/services/walletApi.test.tsapp/src/services/walletApi.tsdocs/TEST-COVERAGE-MATRIX.mdsrc/openhuman/about_app/catalog.rs
- Guard `loadBalances` with a monotonic request id so a slow earlier fetch can no longer overwrite a newer Refresh/Retry result. - Render a translated, user-facing error string (`walletBalances.errorGeneric`) instead of leaking raw backend phrasing to the UI; raw error stays in `console.debug` for diagnostics. - Wire `wallet-balances` into `useSettingsNavigation` (route union, path matcher, breadcrumb mapping) so breadcrumbs + back navigation behave. - Clarify the `fetchWalletBalances` contract — empty array vs reject when the wallet is not configured.
graycyrus
left a comment
There was a problem hiding this comment.
Good work here. Clean additive feature that fills a real UI gap — the wallet.balances RPC was already live but had no frontend caller, and this fixes that.
What I checked
| Area | Files | Verdict |
|---|---|---|
| Frontend panel | WalletBalancesPanel.tsx |
Request-sequencing guard handles concurrent fetches correctly. Four UI states (loading, error, empty, loaded) all covered. Translated error message instead of raw backend text. |
| Service layer | walletApi.ts |
fetchWalletBalances follows the existing callCoreRpc pattern. BalanceInfo type matches the Rust serde output. JSDoc is accurate. |
| Navigation | useSettingsNavigation.ts, Settings.tsx |
Route, breadcrumbs, and SettingsRoute union all wired up. |
| i18n | en.ts + 13 locale chunks |
All 13 keys present in every chunk with English fallback values. Parity check passes. |
| Rust core | catalog.rs |
Single-line docs pointer. No logic change. |
| Tests | Panel tests (9) + API tests (5) | Comprehensive coverage across all states, retry/refresh flows, address truncation, and provider chip rendering. |
| Coverage matrix | TEST-COVERAGE-MATRIX.md |
Row 13.1.4 added, count bumped 69→70. |
CodeRabbit findings — all addressed
All four CodeRabbit findings (race condition guard, raw error text, missing navigation route, contradictory JSDoc) were fixed in 9096c6b. Nothing left to flag there.
Minor observations (non-blocking)
walletBalances.addressCopiedi18n key is defined but unused — the component shows a checkmark SVG instead of text for the copied state. Harmless, but dead keys accumulate.- Copy-address timer doesn't clear on rapid re-clicks (multiple
setTimeouts stack), so "Copied" could disappear based on the first click's timer rather than the latest. Cosmetic edge case.
Neither warrants blocking. Ship it.
Summary
wallet.balancesRPC in the UI by adding a new Settings → Account → Wallet Balances panel.Problem
The
wallet.balancesRPC has been live in the Rust core for some time and is listed as a capability inabout_app/catalog.rs, but the frontend never had a caller. The only way to read multi-chain balances today is to issue a raw JSON-RPC request — inaccessible to normal users, which breaks the obvious "I set up a wallet → what is in it?" mental model and blocks the broader crypto / portfolio workflows tracked in #1394.Solution
app/src/services/walletApi.ts— addsBalanceInfotype +fetchWalletBalances()callingwallet.balancesvia the existingcoreRpcClient, mirroring thefetchWalletStatuspattern. New tests added in the same file.app/src/components/settings/panels/WalletBalancesPanel.tsx— new self-contained panel that renders four states (loading, error with Retry, empty with Recovery Phrase hint, loaded). Loaded state renders oneBalanceRowper balance with chain badge (color-coded per Tailwind palette), truncated address (first 6 + last 4 chars), copy-to-clipboard button, formatted amount + symbol, optional "provider unavailable" chip, and a Refresh button.app/src/pages/Settings.tsx— registers the new entry under Account section, routed at/settings/wallet-balances.app/src/lib/i18n/en.tsplus matching English-value entries in every locale chunk (chunks/{ar,bn,de,en,es,fr,hi,id,it,ko,pt,ru,zh-CN}-4.ts) sopnpm i18n:checkparity passes and translators can fill non-English values later.src/openhuman/about_app/catalog.rs) — single-line update to theskills.wallet_executionhow_tofield pointing users at the new panel.docs/TEST-COVERAGE-MATRIX.mdtracking the panel.Submission Checklist
fetchWalletBalancesfunction (14/14 passing locally).## Related—skills.wallet_execution(UI surfacing of an existing capability — no new feature ID needed).fetchWalletBalancesdirectly; no network calls.docs/RELEASE-MANUAL-SMOKE.md) — N/A: additive UI surface, no release-cut behaviour change.Closes #NNNin the## Relatedsection —Closes #2682below.Impact
wallet.balancesonly uses the public address; the recovery phrase never leaves the local core.Manual visual verification
Verified locally via
pnpm dev:app:Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:check— passes (Prettier auto-fixes applied during quality-gate commit).pnpm typecheck— clean.pnpm test -- WalletBalancesPanel walletApi— 14/14 passed.cargo check --manifest-path Cargo.tomlclean for the one-linecatalog.rstouch.pnpm rust:checkclean.Validation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
Parity Contract
Summary by CodeRabbit
New Features
Bug Fixes / Stability
Documentation
Tests