EVM support, global search, and account inspector - #91
Conversation
Profiles that define both an EVM RPC endpoint and EVM chain ID now use an injected EIP-1193 wallet (e.g. MetaMask) for connecting, native LUME balances, and transfers. Staking, governance signing, and transaction history stay on the legacy Cosmos flow and are hidden or disabled on EVM profiles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Global search dialog in the header accepting block height, tx hash, or account address (bech32 / ETH hex, case-insensitive), routing to the matching inspector - Account inspector page at /account/[address]: balances, delegations, both address formats, sent/received transaction history - Dashboard versions footer: network chain-id and node version, EVM chain ID, and Hub build version from git tag/commit - Extract shared portfolio aggregation helpers and TransactionHistory component from WalletScreen; parameterize account/tx fetchers by address - Fix wallet Unstaking total always reading zero (misread unbonding response) - EVM wallet provider refinements and named EVM network profiles - Add CHANGELOG.md and document Makefile targets in README Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds first-class EVM/MetaMask wallet support alongside the existing Cosmos/Interchain flow, a global header search that routes to block/tx/account inspectors, a new account inspector at /account/[address], and a dashboard "versions" footer (chain-id, node app version, EVM chain id, and a git-derived Hub version). It also refactors shared logic out of WalletScreen (a reusable TransactionHistory component and portfolio aggregation helpers), parameterizes fetchAccountInfo/useTransaction by address, adds staking-overview caching with a manual/auto refresh UI, surfaces the user's current governance vote, and fixes the wallet "Unstaking" total (reads unbonding_responses off the response object). Extensive Vitest suites and docs (README Quick Start, CHANGELOG v1.0.0) accompany the change.
Changes:
- EVM wallet support: named EVM network profiles, MetaMask account/data fixes, idempotent WalletConnect init, and EVM-aware balance/transfer/tx-history handling.
- New navigation/inspection features: global search parsing + dialog, account inspector screen, and a dashboard versions footer.
- Refactors and fixes: extracted
TransactionHistory/portfolio helpers, address-parameterized hooks, staking-overview cache + refresh UI, governance current-vote display, and the "Unstaking total = 0" bug fix.
Reviewed changes
Copilot reviewed 88 out of 94 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| turbo.json | Declares NEXT_PUBLIC_* build inputs for cache correctness (missing NEXT_PUBLIC_EVM_PROFILE_NAME). |
| README.md | Adds Make network targets and network-profile / EVM selection docs. |
| packages/ui/src/screens/StakingScreen/index.tsx | Memoized validator-uptime map, canDelegate/refresh wiring into child components. |
| packages/ui/src/screens/StakingScreen/components/RewardsCalculator.tsx | Gates the stake CTA on canDelegate with a Keplr prompt. |
| packages/ui/src/screens/StakingScreen/components/AllValidators.tsx | Adds refresh UI (progress/last-updated) and MetaMask "not supported" banner; safer sort. |
| packages/ui/src/screens/HomeScreen.tsx | Uses shared getPortfolioData, adds countdown + current-vote display in proposals/vote modal. |
| apps/web/src/contants/network.ts | Adds named EVM profiles and EVM_PROFILE_NAME/IS_EVM_NETWORK derivation. |
| apps/web/src/app/providers/evm-wallet-provider.tsx | New EIP-1193/MetaMask wallet context. |
| apps/web/src/utils/* (portfolio, search, account, evm, governance-votes, node-info, countdown, staking-overview-cache, transaction-history, …) | New helper modules with accompanying tests. |
| apps/web/src/hooks/* (useStaking, useAccountInfo, useTransaction, useProposals, useWalletConnect, …) | Address-parameterized fetching, staking cache/refresh, EVM-aware vote/tx queries. |
| packages/ui/src/screens/AccountScreen.tsx | Full account inspector (balances, delegations, tx tabs). |
| Makefile / CHANGELOG.md | Network run/build targets and v1.0.0 changelog. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
etapsc
left a comment
There was a problem hiding this comment.
Review
Verdict: merge with fixes. The EVM signing/address core is solid and well-tested; one user-facing vote-integrity bug and one silent-failure bug should be fixed first. Proposed fixes are attached as inline suggestions (validated locally: 130/130 pnpm --filter web test, tsc --noEmit clean with all suggestions applied).
Bugs
- HIGH — governance vote can sign a different option than the UI shows (
useProposalsstate vs uncontrolled radio; 3 inline suggestions). - MED — staking auto-refresh dies permanently after one failed refresh (
useStakingtimer only re-arms onlastUpdatedchange; 5 inline suggestions, with a 30s retry floor so failures don't tight-loop against the LCD). - LOW — portfolio pie tooltip shows raw micro-LUME (
HomeScreen.tsx:850-856feedsgetPortfolioDatamicro-units straight into the chart tooltip). - LOW —
search.ts:11-13routes degenerate heights ("0","007") to/block/0,/block/007. - LOW —
evm-wallet-provider.tsx:131-146: overlappingaccountsChanged/chainChangedhandlers race (last-async-write-wins), a transient RPC failure inassertEvmProviderMatchesRpcclears the connected address, and theerrorstate is rendered nowhere.
Security — clean
No key material handled (signing stays in MetaMask/Keplr). EVM send path re-verifies sender/recipient at send time, uses exact BigInt decimal math, and defeats chain-ID spoofing by comparing a 12-blocks-back block hash between wallet provider and configured RPC (evm.ts:229-265) — nice. No XSS surface (search returns fixed path shapes only; no dangerouslySetInnerHTML). Wallet-mode isolation (forced Keplr disconnect under MetaMask) prevents address/mode confusion. New deps benign; cosmjs 0.36→0.39 pinned by the ethsecp256k1 behavior test.
Deploy/ops notes
NEXT_PUBLIC_NETWORK_PROFILEdefaults to mainnet when unset; unknown values throw. Deployments must set it explicitly.- EVM UX auto-enables on testnet/devnet profiles and empty-string env overrides cannot disable it (all overrides use
||fallback) — the only off-switch is the mainnet profile. - Version baking runs
git describe --tagsat build: Docker builds need.gitwith tags + agitbinary or the footer shows a bare hash/unknown. turbo.jsonenv hash omitsNEXT_PUBLIC_EVM_PROFILE_NAMEand the baked git version — a tag-only rebuild can serve a stale cached bundle; skip turbo cache for release builds.- Dead config:
NEXT_PUBLIC_EVM_WS_ENDPOINTis read by no runtime code;NEXT_PUBLIC_COSMOS_EIP712_ENABLEDcan never take effect whileuseWalletConnect.ts:32hard-codeshasEvmCosmosSigner=false(intentional Phase-1 fail-closed, but the.env.examplecomment implies otherwise). .env.exampleoverride comment saysNEXT_PUBLIC_CHAIN_NAME=lumeratestnet; profile + Makefile uselumera-testnet.
Test gaps
No test pins the vote display/submit sync (bug 1) or the refresh scheduling loop (bug 2); transaction hooks' EVM wiring untested (only extracted helpers). The tests that exist are genuinely good — DI'd fetchers, upstream cosmjs behavior pins, negative address cases.
🤖 Generated with Claude Code
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
Co-authored-by: Kullat Nunu <ak@etapiscium.com>
…tion or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 88 out of 94 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (1)
packages/ui/src/screens/HomeScreen.tsx:852
- This change passes the raw micro-denom sums (
stacked,liquid) into the donut chart, whereas the previous code converted them to display LUME viaformatTokenDisplaybefore charting. The pie tooltip (trigger: 'item') renders the raw value, so on hover it will now show large micro amounts (e.g.1234500000) instead of the formatted LUME figure. The wedge proportions are unchanged, but the tooltip value is a regression. Consider converting to display units before passing them to the chart.
Applying the review suggestions for the staking auto-refresh retry left the branch unable to compile: the dependency-array suggestion was added without replacing the original line, and the follow-up autofix then dropped the STAKING_REFRESH_RETRY_DELAY_MS import that the new delay calculation needs. Remove the duplicated dependency array and restore the import, then add regression tests for the behaviour the fix was meant to deliver: a failed refresh re-arms the timer instead of dying, retries persist across repeated failures, and a success resets the attempt counter so the long cadence resumes. Verified by reverting each half of the fix in turn - dropping the refreshAttempt dependency fails all three tests, and dropping the retry floor fails the tight-loop assertion. These are the first React-rendering tests in the app, so jsdom is opted into per file via a docblock and the other suites keep the node environment. window.localStorage is stubbed because Node defines a localStorage global that stays undefined without --localstorage-file, which vitest's jsdom environment will not shadow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses the three low-severity review findings on this branch. The portfolio pie carries micro-denom totals, so the default tooltip printed raw micro-LUME beside figures that were already formatted. Format the tooltip value through the same helper the surrounding labels use, so the two cannot drift apart. Global search treated any digit string as a block height, routing "0" and "007" to /block/0 and /block/007. Strip leading zeros and reject an all-zero height instead. The EVM wallet sync had two defects. Overlapping accountsChanged and chainChanged events each started a multi-round-trip sync, so the slowest response won and could restore state a newer event had already superseded; a sequence counter now discards stale results, and a cancelled flag stops a sync that resolves after unmount. Separately, any verification failure cleared the connected address, so a transient RPC error made the wallet look disconnected. Distinguish the stages: a missing account or a genuine EvmNetworkMismatchError still clears, while an unverifiable network keeps the address and surfaces the reason. This does not weaken signing safety, because the send path re-runs the same check via ensureNetwork before broadcasting. The provider error state was also rendered nowhere, leaving the header simply looking disconnected with no explanation. Show it as an alert chip that stays visible whether or not an address survived, collapsing to icon-only on narrow screens. Verified by reverting the provider fix: the race test then reports the stale address winning, and the transient-failure test reports a cleared address. The mismatch and missing-account tests pass either way by design, pinning the behaviour the fix had to preserve. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The hub's deployed revision (39aedbc) sits on develop, which is not an ancestor of main. main has not moved since 2025-12-09 while develop accumulated 264 commits and serves both production hubs. evm-support was branched from the stale main, so it lacks that feature surface. Records the decision to move the smaller tested change onto the live line rather than the reverse, the file-by-file reconciliation plan for the 40 overlapping files, the wallet consolidation to Keplr + MetaMask, and a route-parity gate to stop a wrong HUB_VERSION re-pin from silently dropping routes from the public hub. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Summary
/account/[address]: balances (available/staking/rewards/unstaking), delegations with validator links, both address formats with copy, and sent/received transaction tabsnode_info, EVM chain ID (EVM networks only), and Hub version baked from git tag/commit at build timeTransactionHistorycomponent and portfolio aggregation helpers extracted from WalletScreen;fetchAccountInfo/useTransactionparameterized by addressunbonding_responsesoff the response object instead of.data)Test plan
pnpm --filter web test), including new suites for search parsing, account address parsing, tx query building, portfolio helpers, and node-info parsingtsc --noEmitclean🤖 Generated with Claude Code