Skip to content

feat(wallet): surface multi-chain balances in Settings#2686

Open
obchain wants to merge 5 commits into
tinyhumansai:mainfrom
obchain:feat/wallet-balances-panel
Open

feat(wallet): surface multi-chain balances in Settings#2686
obchain wants to merge 5 commits into
tinyhumansai:mainfrom
obchain:feat/wallet-balances-panel

Conversation

@obchain
Copy link
Copy Markdown
Contributor

@obchain obchain commented May 26, 2026

Summary

  • Surfaces the existing wallet.balances RPC in the UI by adding a new Settings → Account → Wallet Balances panel.
  • Multi-chain rows (EVM, BTC, Solana, Tron) with chain badges, truncated addresses, copy-to-clipboard, formatted balances, provider-status chips, and a Refresh button.
  • No behaviour change in the core — backend was already complete; this fills the UI gap.

Problem

The wallet.balances RPC has been live in the Rust core for some time and is listed as a capability in about_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

  1. app/src/services/walletApi.ts — adds BalanceInfo type + fetchWalletBalances() calling wallet.balances via the existing coreRpcClient, mirroring the fetchWalletStatus pattern. New tests added in the same file.
  2. 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 one BalanceRow per 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.
  3. app/src/pages/Settings.tsx — registers the new entry under Account section, routed at /settings/wallet-balances.
  4. i18n — 13 new keys in app/src/lib/i18n/en.ts plus 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) so pnpm i18n:check parity passes and translators can fill non-English values later.
  5. Capability catalog (src/openhuman/about_app/catalog.rs) — single-line update to the skills.wallet_execution how_to field pointing users at the new panel.
  6. Coverage matrix — new row in docs/TEST-COVERAGE-MATRIX.md tracking the panel.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — 9 Vitest panel tests across loading / error+retry / empty / loaded / refresh states, plus 5 walletApi tests for the new fetchWalletBalances function (14/14 passing locally).
  • Diff coverage ≥ 80% — changed lines are largely the new panel + new RPC client function, both fully covered by the new test files.
  • Coverage matrix updated — added row 13.1.4 ("Wallet Balances Panel") under the wallet feature group; covered count bumped 69→70.
  • All affected feature IDs from the matrix are listed in the PR description under ## Relatedskills.wallet_execution (UI surfacing of an existing capability — no new feature ID needed).
  • No new external network dependencies introduced (mock backend used per Testing Strategy) — the new tests mock fetchWalletBalances directly; no network calls.
  • Manual smoke checklist updated if this touches release-cut surfaces (docs/RELEASE-MANUAL-SMOKE.md) — N/A: additive UI surface, no release-cut behaviour change.
  • Linked issue closed via Closes #NNN in the ## Related section — Closes #2682 below.

Impact

  • Runtime / platform: pure UI addition — desktop only, no behaviour change on existing surfaces.
  • Performance: negligible — one RPC call on panel mount + on user-triggered Refresh.
  • Security / privacy: unchanged — wallet.balances only uses the public address; the recovery phrase never leaves the local core.
  • Migration / compatibility: none.

Manual visual verification

Verified locally via pnpm dev:app:

  • Setting up a fresh wallet via Settings → Recovery Phrase and then opening Settings → Wallet Balances renders all four chain rows (EVM, BTC, SOL, TRX) with the expected zero-balance loaded state.
  • Sample output observed:
    EVM   0x9B99…2660      0.000000000000000000  ETH
    BTC   1BnHT1…77G3      0.00000000            BTC
    SOL   51xrwi…aWRF      0.000000000           SOL
    TRX   TAWYnc…q4q9      0.000000              TRX
    
  • Addresses are derived correctly per chain, decimals match the chain conventions (18/8/9/6), and chain badges colour-match the Tailwind palette tokens.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: N/A
  • Commit SHA: N/A

Validation Run

  • pnpm --filter openhuman-app format:check — passes (Prettier auto-fixes applied during quality-gate commit).
  • pnpm typecheck — clean.
  • Focused tests: pnpm test -- WalletBalancesPanel walletApi — 14/14 passed.
  • Rust fmt/check (if changed): cargo check --manifest-path Cargo.toml clean for the one-line catalog.rs touch.
  • Tauri fmt/check (if changed): pnpm rust:check clean.

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: Users can now view multi-chain wallet balances inside the app instead of having to issue raw RPC calls.
  • User-visible effect: New Settings → Account → Wallet Balances panel listing per-chain balance rows.

Parity Contract

  • Legacy behavior preserved: No existing surface touched. Backend RPC unchanged.
  • Guard/fallback/dispatch parity checks: N/A — additive UI only.

Summary by CodeRabbit

  • New Features

    • Added Wallet Balances settings panel with per-chain badges, truncated addresses + copy-to-clipboard (with “copied” feedback), provider-status badge, and manual refresh with loading/disabled state.
  • Bug Fixes / Stability

    • Retry flow and error messaging for failed balance loads; refresh updates list reliably.
  • Documentation

    • Added translations for the Wallet Balances UI across multiple languages and updated settings entry text.
  • Tests

    • Added behavioral and API tests covering loading, error/retry, empty, populated, and refresh states.

Review Change Stack

obchain added 4 commits May 26, 2026 13:05
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.
@obchain obchain requested a review from a team May 26, 2026 10:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bf6cedfc-da8d-4351-ad9d-36cbe43f7138

📥 Commits

Reviewing files that changed from the base of the PR and between 98d2a86 and 9096c6b.

📒 Files selected for processing (18)
  • app/src/components/settings/hooks/useSettingsNavigation.ts
  • app/src/components/settings/panels/WalletBalancesPanel.tsx
  • app/src/components/settings/panels/__tests__/WalletBalancesPanel.test.tsx
  • app/src/lib/i18n/chunks/ar-4.ts
  • app/src/lib/i18n/chunks/bn-4.ts
  • app/src/lib/i18n/chunks/de-4.ts
  • app/src/lib/i18n/chunks/en-4.ts
  • app/src/lib/i18n/chunks/es-4.ts
  • app/src/lib/i18n/chunks/fr-4.ts
  • app/src/lib/i18n/chunks/hi-4.ts
  • app/src/lib/i18n/chunks/id-4.ts
  • app/src/lib/i18n/chunks/it-4.ts
  • app/src/lib/i18n/chunks/ko-4.ts
  • app/src/lib/i18n/chunks/pt-4.ts
  • app/src/lib/i18n/chunks/ru-4.ts
  • app/src/lib/i18n/chunks/zh-CN-4.ts
  • app/src/lib/i18n/en.ts
  • app/src/services/walletApi.ts
✅ Files skipped from review due to trivial changes (7)
  • app/src/lib/i18n/chunks/it-4.ts
  • app/src/lib/i18n/chunks/de-4.ts
  • app/src/lib/i18n/chunks/hi-4.ts
  • app/src/lib/i18n/chunks/es-4.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/chunks/ar-4.ts
  • app/src/lib/i18n/chunks/fr-4.ts

📝 Walkthrough

Walkthrough

This PR implements a new Settings > Account > Wallet Balances panel displaying multi-chain wallet balances fetched via openhuman.wallet_balances RPC. The component handles loading, error (with retry), empty, and loaded states, renders chain badges and truncated addresses with copy-to-clipboard, shows formatted balances and asset symbols, and displays provider-health chips. Full test coverage, i18n translations for 14 languages, and Settings integration are included.

Changes

Wallet Balances Settings Panel

Layer / File(s) Summary
API Contract & Data Shape
app/src/services/walletApi.ts, app/src/services/walletApi.test.ts
BalanceInfo interface types native balance rows with chain, address, asset metadata, and provider status; fetchWalletBalances() calls openhuman.wallet_balances RPC and returns typed results; unit tests validate RPC invocation, error propagation, and empty arrays.
Panel Component Implementation
app/src/components/settings/panels/WalletBalancesPanel.tsx
WalletBalancesPanel manages loading/error/empty/loaded states with async loadBalances and request-sequencing guard; BalanceRow renders chain badge, truncated address with copy-to-clipboard (transient "copied" state), formatted balance + symbol, and conditional provider-unavailable chip; refresh button re-triggers fetch while disabled during loading.
Panel Behavioral Tests
app/src/components/settings/panels/__tests__/WalletBalancesPanel.test.tsx
Full test suite covering loading spinner, error message with Retry button, empty state with Recovery Phrase hint, loaded state with chain badges/formatted amounts/truncated addresses/provider chips, and refresh flow triggering new fetches.
Settings Navigation & Routing
app/src/pages/Settings.tsx
Imports WalletBalancesPanel, defines WalletIcon SVG, adds "Wallet Balances" entry to accountSettingsItems with localized title/description, and registers /settings/wallet-balances route within settings layout.
Navigation Hook Update
app/src/components/settings/hooks/useSettingsNavigation.ts
Adds 'wallet-balances' to SettingsRoute, recognizes /settings/wallet-balances in route matching, and reuses breadcrumbs behavior from recovery-phrase.
Multi-Language i18n Translations
app/src/lib/i18n/en.ts, app/src/lib/i18n/chunks/*-4.ts
Panel title/description, loading/retry/empty states, copy-address workflow, provider-missing messages, and raw balance display keys added across language chunks (English, Arabic, Bengali, German, Spanish, French, Hindi, Indonesian, Italian, Korean, Portuguese, Russian, Chinese).
Documentation & Catalog Updates
docs/TEST-COVERAGE-MATRIX.md, src/openhuman/about_app/catalog.rs
TEST-COVERAGE-MATRIX.md adds Wallet Balances Panel as feature 13.1.4 (✅ covered), incrementing covered count from 69 to 70; catalog.rs documents Settings > Wallet Balances as alternative surface for skills.wallet_execution RPC capability.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through settings fair,
Wallet balances gleam with care,
Badges, truncated addresses, and a copy click,
Refresh the view — the numbers stick!
Hooray for tiny coins on display.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(wallet): surface multi-chain balances in Settings' directly and clearly describes the primary change: exposing wallet balances across multiple chains in the Settings UI.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #2682: Settings entry with navigation, multi-chain balance rows with badges/addresses, provider health indicators, refresh functionality, UI state handling (loading/error/empty/loaded), i18n translations, and unit tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the wallet balances feature: new component, tests, translations, routing, and API layer. No unrelated modifications to existing functionality were introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. working A PR that is being worked on by the team. labels May 26, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 844a26c and 98d2a86.

📒 Files selected for processing (21)
  • app/src/components/settings/panels/WalletBalancesPanel.tsx
  • app/src/components/settings/panels/__tests__/WalletBalancesPanel.test.tsx
  • app/src/lib/i18n/chunks/ar-4.ts
  • app/src/lib/i18n/chunks/bn-4.ts
  • app/src/lib/i18n/chunks/de-4.ts
  • app/src/lib/i18n/chunks/en-4.ts
  • app/src/lib/i18n/chunks/es-4.ts
  • app/src/lib/i18n/chunks/fr-4.ts
  • app/src/lib/i18n/chunks/hi-4.ts
  • app/src/lib/i18n/chunks/id-4.ts
  • app/src/lib/i18n/chunks/it-4.ts
  • app/src/lib/i18n/chunks/ko-4.ts
  • app/src/lib/i18n/chunks/pt-4.ts
  • app/src/lib/i18n/chunks/ru-4.ts
  • app/src/lib/i18n/chunks/zh-CN-4.ts
  • app/src/lib/i18n/en.ts
  • app/src/pages/Settings.tsx
  • app/src/services/walletApi.test.ts
  • app/src/services/walletApi.ts
  • docs/TEST-COVERAGE-MATRIX.md
  • src/openhuman/about_app/catalog.rs

Comment thread app/src/components/settings/panels/WalletBalancesPanel.tsx
Comment thread app/src/components/settings/panels/WalletBalancesPanel.tsx Outdated
Comment thread app/src/pages/Settings.tsx
Comment thread app/src/services/walletApi.ts
- 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.
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.addressCopied i18n 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show multi-chain wallet balances in Settings

2 participants