Commit efc2eb1
feat(account-modal): tabbed Assets with token detail, history, staking cards (#612)
* feat(account-modal): tabbed assets with token detail, history, and staking cards
Refactor AccountModal Assets into a full portfolio view: balance header
with Send/Swap/History actions, Token/Staking tabs, per-token detail
screen with Swap/Send/Receive, aggregated transfer history backed by the
VeChain indexer, transaction detail screen, and protocol-specific staking
cards for Stargate, VeBetterDAO Navigators, and BetterSwap LP positions.
Adds VVET (wrapped VET) as a tracked token priced 1:1 with VET, and folds
staking positions into useTotalBalance so the portfolio total reflects
the user's full holdings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(account-modal): polish assets layout, icons, and staking cards
- Move Manage Custom Tokens button back inline with the search input;
shrink the search input height for a tighter Assets header.
- Trim AssetsTabs to just the tab labels.
- Use the bundled BetterSwapLogo for the BetterSwap LP card; switch
Stargate to the actual product logo; route VVET to the VeChain token
registry icon and rename the Navigators card to "VeBetter".
- Remove the redundant external-link icon next to the protocol name on
staking cards and tighten the "Go to platform" button styling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(transfer-history): use eventType=VET for native VET filter
The indexer doesn't accept the VET sentinel address as a tokenAddress
filter, so filtering by VET was fetching a mixed page and post-filtering
client-side, leaving only a handful of rows per page. Switch to the
indexer's eventType=VET parameter so the page contains only VET
transfers and pagination is meaningful.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(prices): 24h price change from oracle ValueUpdate events
Add useOraclePriceChanges24h: reads OracleVechainEnergy's current values
for each supported feed and scans ValueUpdate events in a ~5h window
centered on the block ~24h ago, taking the most recent observation per
feedId as the baseline. Filters by contract address only (the id field
is non-indexed) and decodes args client-side.
useTokenPrices exposes a priceChanges map keyed by token address (VOT3
and veDelegate inherit B3TR's change; VVET inherits VET's). Plumbs
priceChange24hPct through useTokensWithValues and a USD-weighted
portfolio-level change through useTotalBalance.
UI: new common PriceChangeBadge renders +/-X.XX% in semantic
green/red/muted with the "24h" suffix in neutral tertiary text.
Surfaces in AssetButton, the restyled card-shaped asset row,
TokenDetail header, AssetsHeader, and the main BalanceSection.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(history): resolve .vet domains and add copy buttons
- New AddressOrDomainLabel wraps useVechainDomain and renders the .vet
domain when available, falling back to humanAddress otherwise. Used
for the From/To subtitle on HistoryItemRow and the From/To rows on
TransactionDetailContent.
- New CopyIconButton wraps the SSR-safe copyToClipboard with a Copy ->
Check icon transition. Added next to From, To, and Hash on the
transaction detail screen so users can copy the full underlying
address / tx ID even when the label is truncated or a domain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(history): dedupe and Map-group transfers to avoid duplicate React keys
groupByDay used a sequential lastKey check, so any non-contiguous
same-day items (e.g. when paginating overlaps a day boundary) produced
multiple groups with the same label and React threw a duplicate-key
error on Load more. Switch to a Map keyed by day label and a Set keyed
by item.id so a transfer can never produce two groups or two rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(transfer-history): use page param instead of offset/limit
The VeChain indexer ignores offset/limit and only honors a 0-indexed
`page` parameter (returning ~20 items per page). Previously every Load
More click hit the same page-0 response, and the row dedupe quietly
swallowed the duplicate IDs, so the UI appeared frozen.
- Drop PAGE_SIZE and the offset/limit params.
- Send only `page=<n>`.
- getNextPageParam returns allPages.length (0 -> 1 -> 2 ...) when the
server reports hasNext.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(transfer-history): scope eventType=VET to explicit VET filter only
isVetTokenAddress treated 'undefined tokenAddress' the same as the VET
sentinel, so opening unfiltered history forced eventType=VET on the
indexer and the user only saw native VET rows. Trigger the VET filter
only when the caller explicitly passes the VET sentinel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(history): resolve unknown ERC-20 symbols and mirror sparkles icon
- useTransferHistory now collects every ERC-20 address per page that
isn't in the known token set (custom tokens + VET/VTHO/B3TR/VOT3/
veDelegate/VVET), fetches getTokenInfo for them in parallel, and uses
the real symbol + decimals. Fixes rows that previously displayed as
e.g. "+100 0x680f" (LP tokens from BetterSwap and other DeFi protocols)
and corrects the amount when the contract isn't 18 decimals.
- TransactionDetailContent now mirrors HistoryItemRow's behaviour: when
the transfer was received from 0x000...000 it renders the LuSparkles
placeholder circle instead of the token logo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(staking): add Juicy Finance card and fix history row overflow
- New useJuicyPosition hook reads the Juicy Finance pool (Aave v3 fork
at 0x00Bd212704A8816264607a7110cCabe70219D5aB on mainnet): enumerates
reserves, batches getReserveData + balanceOf on each aToken /
variableDebt / stableDebt token, and computes supplied + borrowed
per asset and net USD value. Health factor read from
getUserAccountData (null when there's no debt).
- New JuicyFinanceCard renders a supplied list, optional borrowed list,
and a colored health-rate tag; routes to www.juicyfinance.io.
- StakingTab gates on hasPosition; useTotalBalance folds the net Juicy
position into the portfolio total.
- Local JuicyPoolAbi added in staking/abis.ts (canonical Aave v3 subset
from vechain/b32).
- HistoryItemRow: split amount and token symbol onto two lines, with
flexShrink/minW/maxW constraints so long token names
(jVechainVTHO, variableDebtVechainVTHO, etc.) truncate with an
ellipsis instead of overflowing the row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(build): satisfy stricter tsc on staking + oracle hooks
CI's @vechain/vechain-kit:build runs tsc with stricter options than
`yarn typecheck` and surfaced four type errors:
- CustomTokenInfo.decimals is typed as string, not number — drop the
inline cast in useTransferHistory and useJuicyPosition and coerce
with Number(info.decimals) (fallback 18) instead.
- oracle.read.getLatestValue requires `0x\${string}` — cast the
PRICE_FEED_IDS value at the call site.
- getEventLogs requires nodeUrl in GetEventsProps — pass network.nodeUrl
in useOraclePriceChanges24h.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(i18n): translate remaining aria-labels and Copy prefix
- aria-label on the action buttons in AssetsHeader and TokenDetail now
uses the translated string (matches the visible text), not the raw
English key.
- Row's CopyIconButton aria-label uses t('Copy') instead of a hardcoded
English prefix; Row pulls its own useTranslation hook.
- Add the 'Copy' key to en.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* i18n: translate new assets/history/staking keys across all 16 locales
Add translations for the 37 keys introduced by the AccountModal refactor
(Token, Staking, History, Sent, Received, Supplied, Borrowed, Health
rate, Go to platform, Copy, etc.) to every supported locale: de, es,
fr, hi, it, ja, ko, nl, pt, ro, ru, sv, tr, tw, vi, zh.
Also alphabetises all language files (including en.json) case-
insensitively so the per-locale set of keys stays in sync going
forward.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(history): thread onBack through transaction-detail / history screens
Token Detail -> tap a history-preview row -> Transaction Detail. The
back button was hardcoded to navigate to the aggregated history screen
instead of returning to Token Detail. Add an optional onBack prop to
both TransactionHistoryContent and TransactionDetailContent, and pass:
- TokenDetail.handleHistoryItem -> onBack returns to Token Detail.
- TokenDetail.handleSeeAll -> onBack returns to Token Detail.
- TransactionHistory.handleItemClick -> onBack re-dispatches the same
history screen with its tokenFilter + parent onBack so further back
presses still chain correctly.
History screen's back button uses onBack if provided, else falls back
to the Assets list (existing behaviour).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(review): address valid CodeRabbit findings
- useTransferHistory: only pre-fill symbolByAddress for known 18-decimal
tokens (VET, VTHO, B3TR, VOT3, veDelegate, VVET). Custom tokens flow
through the existing lazy getTokenInfo path so their real decimals are
honoured.
- useBetterSwapLpPositions: validate factoryAddress / pairs with viem's
isAddress; build the queryKey from a pairs fingerprint (first + last
+ length) so different sets with equal length get distinct cache
entries; use the validated pair list throughout the queryFn.
- config: checksum the testnet stargate/stargate-NFT/navigator-registry
addresses and the mainnet navigator address (EIP-55) to match the
surrounding style.
- CopyIconButton: store the setTimeout id in a useRef and clear it on
rapid re-click and on unmount.
Other CodeRabbit suggestions were verified against current code and are
either already covered (oracle feedId cast, prices lowercase mirror,
ERC20 balance hook's internal address guard), pre-existing patterns
across the kit, or no-ops given the surrounding constraints — left
unchanged to keep this diff minimal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(charts): 24h sparklines from oracle ValueUpdate events
- Refactor the oracle hook into a single useOracleHistory24h query that
scans 24h of ValueUpdate events once and exposes:
* useOraclePriceChanges24h -> percent change per feed
* useTokenPriceHistory24h(token) -> sorted PricePoint[]
The current spot value is pinned as the closing point so the chart
always extends to "now" even between sparse oracle updates.
- New usePortfolioPriceHistory24h derives a portfolio sparkline by
walking the union of feed timestamps and weighting each feed by the
wallet's current liquid holdings (VVET counted as VET; VOT3 +
veDelegate counted on the B3TR feed). Cheap and accurate enough for
a 24h window where holdings rarely change.
- New PriceChart: dependency-free SVG sparkline with a
green/red/neutral tone, gradient fill, configurable opacity, and
vector-effect non-scaling-stroke so the line stays crisp regardless
of container width.
- TokenDetail screen shows the chart full-opacity between balance and
actions; VVET / VOT3 / veDelegate piggy-back on VET / B3TR.
- BalanceSection (main wallet view) overlays the portfolio chart at
~18% opacity behind the balance text, tinted by the day's direction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(chart): smooth sparkline with Catmull-Rom-to-Bezier curves
The straight-line polyline produced sharp corners between sparse oracle
points. Replace M/L segments with a Catmull-Rom-derived cubic-Bezier
spline: each segment's control points are derived from the surrounding
two points so the curve glides through every observation without
overshooting. The filled area + the existing strokeLinejoin/Linecap
=round still apply.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(chart): switch to monotone cubic interpolation to stop overshoot
The Catmull-Rom-to-Bezier smoothing produced control points that
overshot adjacent points whenever neighbouring oracle observations
jumped sharply, causing the line to backtrack on itself. Replace with
Fritsch-Carlson monotone cubic: tangents at each point are the average
of neighbouring slopes (zero at local extrema), then clamped so the
curve is provably monotone within each segment. The curve still passes
through every observation but is guaranteed not to overshoot or
reverse direction between points.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chart): show portfolio sparkline on Assets page
AssetsHeader now renders the same monotone-cubic portfolio sparkline
between the balance row and the Send/Swap/History actions, mirroring
the per-token chart on TokenDetail. Reuses usePortfolioPriceHistory24h
and PriceChart, tinted by the day's direction. Hidden when balances
are hidden (showAssets=false) or when there's only one observation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* revert(chart): drop the sparkline underlay from BalanceSection
The chart still ships on Assets and TokenDetail; on the main wallet
view (BalanceSection) it competed with the AssetIcons row and didn't
add enough signal at 18% opacity. Removes the underlay layer, the
usePortfolioPriceHistory24h call, the Box wrapper and the now-unused
imports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(chart): hover/touch tooltip + swap Assets header button order
- PriceChart: new interactive prop. When enabled the chart accepts
mouse/touch input, snaps to the nearest data point, and renders a
vertical guide line, a marker dot at the active point, and a
position-aware tooltip with the formatted value and timestamp. New
formatValue prop lets callers override the value formatter (defaults
to a 4-decimal USD price; AssetsHeader passes formatCompactCurrency
so portfolio values render as $4.30K-style).
- TokenDetail and AssetsHeader charts opt in via interactive.
- AssetsHeader actions reordered to [Swap, Send, History] so they match
the [Swap, Send, Receive] order on TokenDetail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(chart): fold staking positions into the portfolio sparkline
The chart was only summing liquid token balances, so for wallets where
most value sits in Stargate / Navigators / Juicy / BetterSwap LP, the
sparkline reported a value an order of magnitude smaller than
useTotalBalance's headline number.
usePortfolioPriceHistory24h now also reads useStargatePositions,
useNavigatorPosition, useJuicyPosition, useBetterSwapLpPositions and
maps each into the appropriate price feed:
- Stargate VET supplied -> VET feed amount.
- Navigator staked B3TR / delegated B3TR -> B3TR feed amount.
- Juicy supplied/borrowed -> per-asset net into VET (via VVET), VTHO,
or B3TR feeds.
- BetterSwap LP positions -> flat USD offset (their underlying split
varies per pair, so attributing amounts to specific feeds isn't
meaningful; the current USD value still contributes to the total).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent e6e9ae5 commit efc2eb1
69 files changed
Lines changed: 11973 additions & 7294 deletions
File tree
- packages/vechain-kit/src
- components
- AccountModal
- Components
- Contents
- Assets
- Components
- StakingCards
- Receive
- Swap
- TokenDetail
- Components
- TransactionHistory
- Components
- Types
- common
- config
- hooks/api
- staking
- transferHistory
- wallet
- languages
- utils
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 15 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| |||
74 | 77 | | |
75 | 78 | | |
76 | 79 | | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
77 | 90 | | |
78 | 91 | | |
| 92 | + | |
| 93 | + | |
79 | 94 | | |
80 | 95 | | |
81 | 96 | | |
| |||
Lines changed: 21 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
11 | 18 | | |
12 | 19 | | |
13 | 20 | | |
| |||
26 | 33 | | |
27 | 34 | | |
28 | 35 | | |
29 | | - | |
| 36 | + | |
30 | 37 | | |
31 | 38 | | |
32 | 39 | | |
| |||
109 | 116 | | |
110 | 117 | | |
111 | 118 | | |
112 | | - | |
113 | | - | |
114 | | - | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
115 | 131 | | |
116 | 132 | | |
117 | 133 | | |
| |||
Lines changed: 58 additions & 85 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | 3 | | |
9 | 4 | | |
10 | 5 | | |
11 | | - | |
12 | 6 | | |
13 | | - | |
14 | 7 | | |
15 | | - | |
| 8 | + | |
| 9 | + | |
16 | 10 | | |
17 | | - | |
18 | 11 | | |
19 | 12 | | |
20 | 13 | | |
21 | 14 | | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
31 | 25 | | |
32 | 26 | | |
33 | 27 | | |
| |||
36 | 30 | | |
37 | 31 | | |
38 | 32 | | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | 33 | | |
| 34 | + | |
46 | 35 | | |
47 | | - | |
| 36 | + | |
48 | 37 | | |
49 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
50 | 46 | | |
51 | 47 | | |
52 | 48 | | |
53 | 49 | | |
54 | | - | |
55 | 50 | | |
56 | 51 | | |
57 | 52 | | |
58 | 53 | | |
59 | 54 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
64 | 71 | | |
65 | 72 | | |
66 | 73 | | |
| |||
76 | 83 | | |
77 | 84 | | |
78 | 85 | | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
123 | 92 | | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
132 | 106 | | |
133 | | - | |
134 | | - | |
135 | 107 | | |
136 | | - | |
137 | | - | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
138 | 111 | | |
139 | 112 | | |
140 | 113 | | |
| |||
0 commit comments