@@ -136,6 +136,10 @@ export const DEFAULT_ETH_TOKEN: Token = {
136136
137137const SEARCH_DEBOUNCE_MS = 150
138138const BARTER_SEARCH_CAP = 50
139+ /** Hide held tokens worth less than this (USD) from the default "Your tokens"
140+ * list so the section isn't crowded with dust. Unpriced tokens are also hidden
141+ * — if we can't confirm value ≥ threshold, treat it as dust. */
142+ const DUST_USD_THRESHOLD = 0.1
139143
140144const TokenSelectorModalComponent = ( {
141145 open,
@@ -210,13 +214,23 @@ const TokenSelectorModalComponent = ({
210214 }
211215 } , [ open ] )
212216
217+ // Held tokens minus dust: applied to the default "Your tokens" section so it
218+ // doesn't fill up with sub-$0.10 balances or unpriced tokens (airdrop junk).
219+ // Require a known USD price AND value ≥ threshold. Search results still use
220+ // the full heldTokens list so users can find anything by name or address.
221+ const visibleHeld = useMemo (
222+ ( ) => ( heldTokens ?? [ ] ) . filter ( ( h ) => h . usdPrice != null && h . usdValue >= DUST_USD_THRESHOLD ) ,
223+ [ heldTokens ]
224+ )
225+
213226 // Default view: short curated blue-chip list minus anything in Your tokens.
227+ // Dedup against the visible held list so a dust-hidden token (e.g. tiny USDC
228+ // balance) still shows up in Popular rather than disappearing entirely.
214229 const defaultPopular = useMemo ( ( ) => {
215- const held = heldTokens ?? [ ]
216- if ( held . length === 0 ) return popularTokens
217- const heldSet = new Set ( held . map ( ( h ) => h . token . address . toLowerCase ( ) ) )
230+ if ( visibleHeld . length === 0 ) return popularTokens
231+ const heldSet = new Set ( visibleHeld . map ( ( h ) => h . token . address . toLowerCase ( ) ) )
218232 return popularTokens . filter ( ( t ) => ! heldSet . has ( t . address . toLowerCase ( ) ) )
219- } , [ popularTokens , heldTokens ] )
233+ } , [ popularTokens , visibleHeld ] )
220234
221235 // Search view: full 344-entry curated list minus held, still filtered below.
222236 const searchCurated = useMemo ( ( ) => {
@@ -242,6 +256,12 @@ const TokenSelectorModalComponent = ({
242256 [ heldTokens , q , excludeAddrLower ]
243257 )
244258
259+ // Default "Your tokens" list: held minus dust and minus the excluded side.
260+ const defaultHeld : HeldToken [ ] = useMemo (
261+ ( ) => visibleHeld . filter ( ( h ) => h . token . address . toLowerCase ( ) !== excludeAddrLower ) ,
262+ [ visibleHeld , excludeAddrLower ]
263+ )
264+
245265 const filteredCurated = useMemo (
246266 ( ) => searchCurated . filter ( matchesQuery ) ,
247267 // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -329,7 +349,7 @@ const TokenSelectorModalComponent = ({
329349 filteredCurated . length > 0 ||
330350 barterMatches . length > 0 ||
331351 pastedBarterToken !== null
332- : filteredHeld . length > 0 || filteredRecent . length > 0 || defaultPopular . length > 0
352+ : defaultHeld . length > 0 || filteredRecent . length > 0 || defaultPopular . length > 0
333353
334354 return (
335355 < Dialog open = { open } onOpenChange = { onOpenChange } >
@@ -450,14 +470,14 @@ const TokenSelectorModalComponent = ({
450470 ) : (
451471 // Default view: Your tokens + Recent searches + Popular tokens.
452472 < >
453- { filteredHeld . length > 0 && (
473+ { defaultHeld . length > 0 && (
454474 < section >
455475 < SectionHeader
456476 icon = { < Coins className = "h-4 w-4" /> }
457477 label = "Your tokens"
458- count = { filteredHeld . length }
478+ count = { defaultHeld . length }
459479 />
460- { filteredHeld . map ( ( h ) => (
480+ { defaultHeld . map ( ( h ) => (
461481 < TokenRow
462482 key = { `held-${ h . token . address } ` }
463483 token = { h . token }
@@ -515,7 +535,7 @@ const TokenSelectorModalComponent = ({
515535 ) ) }
516536 </ section >
517537 ) }
518- { isLoadingHeld && filteredHeld . length === 0 && address && (
538+ { isLoadingHeld && defaultHeld . length === 0 && address && (
519539 < p className = "px-3 py-2 text-xs text-muted-foreground" > Loading your balances…</ p >
520540 ) }
521541 </ >
0 commit comments