feat(frontend): hide test tokens behind Advanced settings #193
Labels
No labels
agent:implement
agent:ready
backend
bug
cannot-reproduce
confirmed
desktop
docs
documentation
duplicate
enhancement
feature
frontend
good first issue
help wanted
high-risk
in-review
invalid
mobile
needs-triage
P0-critical
P1-high
P2-medium
P3-low
qa
QA
question
ready
report
responsive
security
security-escalate
smart-contract
solana
tablet
test-pass
ux
wallet-issue
wallet:keplr
wallet:metamask
wallet:station
wallet:walletconnect
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-bridge-monorepo#193
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
Hide known noneconomic faucet tokens from the default Transfer token picker. Keep them available for QA behind Settings → Advanced → Show test tokens (off by default). Ranking from #136 / PR #155 stays when the toggle is on.
This is not a reopen of #136. That issue shipped display order only and listed hiding as out of scope (
INV-FE-TOKEN-RANK-1“Do not hide test tokens”). This issue supersedes that one clause for the Transfer combobox.Bundle (do not split):
data-testid="token-select") omits closed-denylist noneconomic ids unless the Advanced toggle is on.INV-FE-TOKEN-RANK-1(or add a sibling INV) andskills/agent-frontend-token-rank.md.Current codebase
#136 is closed and merged. Test tokens still appear in the Transfer picker; they sort last.
buildTransferTokens()inpackages/frontend/src/services/transfer/buildTransferTokens.tsfilters by enabled registry + dest mapping, thenrankTransferTokensonly — no hidepackages/frontend/src/utils/tokenEconomicRank.ts(isNoneconomicBridgeToken) frompackages/frontend/src/utils/faucetTokens.tsTransferFormuseEffect(~377–392) callsdefaultTransferTokenId(transferTokens, selectedTokenId)— first ranked id when empty/invalid; keeps an explicit still-valid test idTokenSelectrenders given order;tokens.length === 0returnsnull(picker vanishes with no copy)SettingsPage.tsxtabs: Chains, Tokens, Bridge Config, Faucet. No Advanced tab.useUIStoreonly holds EVM wallet modalFaucetPanel.tsxstill lists testa / testb / tdec (mainnet) and TKNA/B/C / KDEC (local) via the shared catalogTokensPanel.tsx) including test CW20s — operator/read-only, not the Transfer pickerlocalStoragecl8y-theme. No preference store for token visibilitydocs/FRONTEND_BRIDGE_INVARIANTS.mdINV-FE-TOKEN-RANK-1 includes Do not hide test tokensskills/agent-frontend-token-rank.md— “Do not use this skill to hide test tokens”packages/frontend/e2e/token-selection.spec.tsassumes noneconomic rows are visible in the listboxNoneconomic ids (same catalog as ranking): mainnet testa / testb / tdec (Terra CW20 + EVM + SPL in
faucetTokens.ts); local TKNA / TKNB / TKNC / KDEC. Localuluna/ tLUNC and synthetic SOL withnoneconomic: falsestay economic and must remain in the default picker.Why this is needed
TokenSelectreturnnullon test-only routes.Constraints & guardrails
isNoneconomicBridgeToken/faucetTokens.ts. Do not classify by displaysymbol, substring"test", or “not intokenlist.json”. Unknown registered ids stay visible (economic).id/tokenId/evmTokenAddress, dest mappings, decimals, fees, hash encoding, or on-chain enablement. Hidden tokens remain fully bridgeable once shown.import.meta.env.DEVauto-show that would leak test tokens on a mis-built production bundle.defaultTransferTokenIdunchanged in spirit (first visible token; keep explicit still-valid choice among visible options).localStorage(suggested keycl8y-bridge-show-test-tokens, values'0'/'1'). Corrupt/missing → off. This is first-party UI preference, same class ascl8y-theme, not third-party SDK consent. Do not block on #165; if that later gates first-party keys, this key should be listed as functional preference.?token=/?showTestTokens=that silently injects a hidden mint into the default picker. Deep links are out of scope.symbol: 'CL8Y'still hidden by default; CL8Y CW20 withsymbol: 'testa'still shown.[](legacy #89), INV-UX1 amount/CTA, recipient validation, clickwrap, or faucet claim amounts.Relevant files
packages/frontend/src/utils/tokenEconomicRank.tsfilterTransferTokensForDisplay(tokens, showTestTokens)(or equivalent); keep rank helperpackages/frontend/src/utils/tokenEconomicRank.test.tspackages/frontend/src/services/transfer/buildTransferTokens.tsshowTestTokensthrough or filter at the call sitepackages/frontend/src/services/transfer/buildTransferTokens.test.tspackages/frontend/src/components/transfer/TransferForm.tsxpackages/frontend/src/components/transfer/TokenSelect.tsx/AmountInput.tsxtokens.length === 0after hide (do not leave a blank Amount row)packages/frontend/src/pages/SettingsPage.tsxpackages/frontend/src/components/settings/AdvancedPanel.tsx(+ test)packages/frontend/src/hooks/useShowTestTokens.tslocalStorageread/write,storageevent so Settings and Transfer stay in sync across tabspackages/frontend/src/pages/SettingsPage.test.tsxpackages/frontend/e2e/token-selection.spec.tsdocs/FRONTEND_BRIDGE_INVARIANTS.mdskills/agent-frontend-token-rank.mdpackages/frontend/src/utils/faucetTokens.tsRecommended solution direction
Single visible-set helper next to ranking:
visible = showTestTokens ? ranked : ranked.filter(t => !isNoneconomicBridgeToken(t))Rank the full mapped set first (stable groups), then drop noneconomic when the flag is off — or filter then rank; both must be unit-tested. Prefer filter then rank on the list actually rendered so
tokens[0]is always a valid default.Preference module with:
getShowTestTokens(): booleandefaultfalsesetShowTestTokens(next: boolean)window→falseSettings → Advanced tab containing only this control for this MR (do not dump RPC URLs, private keys, or operator config here).
Empty state in the Transfer amount/token cluster when
visible.length === 0while mappings have finished: short message + link/button to/settingsAdvanced. Distinguish from EVM mapping-load (still[]with no “enable test tokens” copy).E2E: set the storage key in a fixture helper before tests that need TKNA/testa. Default-path tests must not enable it.
Keep
TokenSelectfrom applying a second sort.Definition of Ready
code/cl8y-bridge-monorepodefault branchfaucetTokens.ts,isNoneconomicBridgeToken)Acceptance criteria
localStoragekey): Transfer combobox contains zero noneconomic catalog ids when economic tokens exist for the route; default is economic.ulunaremain in the default picker (noneconomic: false).?token=/ query flag added.data-tokenid; with storage key set, ranking + select-testa specs pass.make test-frontend/ package unit tests green.Given / When / Then
Given a Transfer route whose mapped set includes at least one economic token and at least one noneconomic faucet token, and Show test tokens is off (default)
When the user opens the Amount token combobox
Then noneconomic ids are not in
[role="option"], the default selection is economic, and a later Bridge submit cannot use a hidden id.Given the same route and the user enables Settings → Advanced → Show test tokens
When they return to Transfer and open the combobox
Then noneconomic tokens appear in the bottom group (INV-FE-TOKEN-RANK-1) and selecting testa keeps testa through amount entry.
Given testa is selected with the toggle on
When the user turns the toggle off
Then the selected id becomes the first remaining visible token (economic if any); the hidden testa id is not submitted.
Given a route whose mapped tokens are all noneconomic and the toggle is off
When mappings have finished loading
Then the UI shows an empty-token message pointing at Advanced settings, not a blank
TokenSelectand not an automatic unhide.Test plan — functional paths
Unit (rank helper + builder)
showTestTokens=false: output is only economic; order matches rank of that subset.showTestTokens=true: economic then test (existing #136 cases still pass).[].symbol: 'CL8Y'hidden when flag false; CL8Y CW20 +symbol: 'testa'still visible.defaultTransferTokenIdon the visible list; empty visible →undefined.sourceChainMappingspath, registryevm_token_addressfallback, and mapping-load[]all honor the flag (load-empty must not be confused with hide-empty).localStorage/ missing key →false.noneconomic: falsefaucet rows (tLUNC) never hidden by this filter.Settings / preference
ariareflects off by default.storageevent if implemented).TransferForm / TokenSelect
data-testid="token-select"may be absent, but the message is queryable (adddata-testide.g.token-select-empty).Playwright
data-tokenidfailsisNoneconomicBridgeToken.e2e/*.spec.tsfor first-token / TKNA / testa assumptions; those that need test tokens must set the preference in the same MR.Manual
Classification spoof, hidden-id submit, and empty-list clickjack belong in the unit/component cases above. This is not an auth/keys/wasm/deploy/wallet/2-of-3 change — no extra DeFi attack suite.
Verification criteria
showTestTokens=true. Frontend unit suite green.Out of scope
References
packages/frontend/src/utils/faucetTokens.tsdocs/FRONTEND_BRIDGE_INVARIANTS.mdINV-FE-TOKEN-RANK-1skills/agent-frontend-token-rank.mdFirst-pass model recommendation
Recommendation: grok-high
Rationale: Not a Composer-shaped first pass. Visibility is a cross-cutting UI preference (new Settings tab +
localStoragehook + Transfer builder + empty state + e2e/docs/skill). Expected production files exceed three in one subsystem (tokenEconomicRank/buildTransferTokens,TransferForm+ empty TokenSelect/Amount copy,SettingsPage+ new Advanced panel, preference hook). It revises a shipped invariant (INV-FE-TOKEN-RANK-1 “do not hide”) and can empty the picker or submit the wrong mint if filter andselectedTokenIddisagree. Comparable prior work is PR #155 (ranking: builder, catalog extract, TransferForm, TokenSelect tests, e2e, invariants, skill — far past a 1–3 file local edit). Verification is unit + Playwright + mixed-route manual, not a single deterministic helper test. Composer’s “at most three production files / no cross-cutting state” criteria fail; use Grok high.Labels
feature(type helper after create). Do not applyready/agent:implementvia intake labels.