UI: Add searchable token picker on Swap (mirror PairSearchSelect) #481
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#481
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
User feedback (mainnet Swap): the YOU PAY / YOU RECEIVE token dropdowns are scroll-only lists (EMBER, JADE, ONYX, RUBY, …) with no search field. With more factory tokens this becomes hard to use. Trade and Limits already have a searchable pair combobox (
PairSearchSelect); Swap should get an equivalent token search.Current codebase
Swap token picker (no search)
frontend-dapp/src/components/ui/TokenSelect.tsx— button trigger + portaledrole="listbox". Options aretokens.filter(t => t !== excludeToken)with no query filter. Keyboard typeahead on the closed button exists (#244) viausePortalListboxKeyboard, but there is no visible search input.frontend-dapp/src/pages/SwapPage.tsxfor both legs (aria-label“Select token you pay” / receive). Token universe =getAllTokens(pairs)from factory LCD pairs (frontend-dapp/src/services/terraclassic/router.ts), not an indexer token list. Order isSetinsertion order (not alphabetical). Wallet balance does not filter the list.MintPagefor a small faucet list (scroll is fine there).PortalListbox.tsx,.token-select-*inindex.css. E2E:frontend-dapp/e2e/helpers/token-select.ts(triggers viagetByRole('button')).Pair search (reference UX)
frontend-dapp/src/components/trade/PairSearchSelect.tsx—role="combobox"text input, 300ms debounce, indexerGET /api/v1/pairs?q=…with factory gate, offline fallback viapairSearchQuery.ts(#314, #301, #328, #350).docs/frontend.md§ “Pair search combobox”.Indexer gap (optional later)
GET /api/v1/tokens(indexer/src/api/tokens.rs) is paginated metadata only — noq/ relevance sort. Pair search already matches single-token symbols via pairq, but that returns pairs, not unique tokens for the Swap picker.Why needed
max-h-60list; scroll-only discovery is slow on mobile in-wallet browsers.Constraints / guardrails
getAllTokens(pairs)(plus existing native-wrap enrichment). Do not introduce an arbitrary external token list that could select non-routable assets.PairSearchSelectdegraded mode. Do not block Swap on indexer availability.terra1…address), result cap for long lists, preserveexcludeTokenso pay/receive cannot pick the same asset.aria-autocomplete="list",aria-activedescendant, Escape/arrows/Enter) likePairSearchSelect, not only a button+typeahead.TokenSelectfor Mint (small list) unless search is trivial to share; avoid forcing Mint into a combobox if unnecessary.getPairs(q)(duplicates / pair-shaped results).ILIKEwildcards if any server-side token search is added later; client filter must not execute or render untrusted HTML from token metadata (symbols/names are text only). Logo URLs stay behind existing allowlist (resolveTrustedTokenLogoUrl).w-fullon small viewports); portaled listbox must still flip when space is tight.Relevant files
frontend-dapp/src/components/ui/TokenSelect.tsxfrontend-dapp/src/pages/SwapPage.tsxfrontend-dapp/src/services/terraclassic/router.ts(getAllTokens)frontend-dapp/src/components/trade/PairSearchSelect.tsxfrontend-dapp/src/utils/pairSearchQuery.tsfrontend-dapp/src/utils/tokenDisplay.ts,tokenRegistry.ts,hooks/useTokenDisplayInfo.tsfrontend-dapp/src/components/ui/index.tsfrontend-dapp/e2e/helpers/token-select.tsdocs/frontend.mdindexer/src/api/tokens.rs,frontend-dapp/src/services/indexer/client.tsRecommended direction
TokenSearchSelect(new component undercomponents/ui/orcomponents/trade/) modeled onPairSearchSelect: visible search input + portaled listbox withTokenLogo+ symbol rows.tokenSearchQuery.tsreusing haystack ideas fromlegSearchTokens/buildPairLocalSearchHaystack(id, denom, cached symbol/name,tokenRegistry). Empty query shows allowed tokens (optionally sort by symbol; optionally cap + “type to filter” if list is large).TokenSelectusages onSwapPageonly; keep Mint onTokenSelect.PairSearchSelect.*.test.tsx; update E2E helpers to targetcombobox/ labeled search and cover type-to-filter + select.docs/frontend.mdbeside pair search.GET /api/v1/tokens?q=relevance API — only if factory token count outgrows comfortable client filtering.Acceptance criteria
excludeTokenstill removes the other leg; selecting a token updates Swap state exactly as today (quotes, wrap/unwrap, impact confirm reset behavior preserved).docs/frontend.mdupdated.Test plan (all paths)
Unit
excludeToken)."EM"/"ember"filters to EMBER (and similar); no match → empty state / helper text.uluna, CW20terra1…).tokensshows loading or disabled state.Manual / LocalTerra
E2E
token-selecthelpers; Swap smoke path: search → select from + to → quote visible.Test plan (attack / hack / abuse)
<script>, HTML, or markdown must render as plain text only (nodangerouslySetInnerHTML).logoURIin cache/registry must still go through allowlist; blockjavascript:/ untrusted hosts.onChangemust only emit ids present in thetokensprop (no free-typed arbitrary address that bypasses factory graph).qadded later: escape SQL/ILIKEwildcards (%,_) per #459; enforce min query length; rate-limit / cap results; never return tokens outside factory-allowed set to Swap.Verification criteria
References
mentioned in commit
c438038171mentioned in merge request !1018
Implemented on !1018 (
feat/481-token-search-select): TokenSearchSelect + client-side tokenSearchQuery for Swap; Mint stays on TokenSelect. Docs: docs/frontend.md#token-search-combobox, skills/AGENTS_FRONTEND_TOKEN_SEARCH.md.mentioned in commit
b72c960123mentioned in issue #498
mentioned in issue #542
marked as related to #542
mentioned in issue #562
mentioned in issue #630
mentioned in issue #632
marked as related to #632
mentioned in merge request !1144
mentioned in issue #711
marked as related to #711
mentioned in issue #713