feat: hide soft-launch gemstone tokens from production retail UI (RUBY, EMBER, …) #562
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#562
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 soft-launch gemstone / faucet tokens (EMBER, CORAL, JADE, ONYX, RUBY, TOPAZ, QUARTZ, PEARL, plus LocalTerra extras OPAL, COBALT, SLATE, AMBER, IRON) from the production retail dApp (
https://dex.cl8y.com) so Swap / Trade / Pool / Charts / Create look like a finished economic DEX, not a test faucet.Bundle into one issue. Do not split “Swap picker” vs “Trade pairs” vs “Pool table” vs “Create Pair gems” vs “Mint nav” vs “route hops through gems” vs “Coolify faucet env.”
This is retail discovery + quoting, not on-chain deletion. Pairs and CW20s stay on columbus-5.
Related: #534 (economic first, gems last — still listed), #473 (Mint faucet), #542 (Create Pair appends mintable gems), #547 (
/poolcatalog still shows gems last), #489 (retail copy), #501 (hybridGET /route/solvemay hop gem pools).Current codebase
Soft-launch deployed eight noneconomic CW20s and ten gem pairs on columbus-5 (
deployments/mainnet-soft-launch/deploy-trace.md). They were a routing/UI sandbox before wrap + UST1 hubs existed. Economic markets (cLUNC, cUSTC, UST1, USTR, CL8Y, vFDUSD, …) are live; the gems are still in the factory graph.terra1dmuruhht32x8f47nvm73pwp6q7uf2jtfhdt3nxcql4mmqkyfsraqn2dt94terra1k6cqupylk0wp4pj273pntwhv9py0q5guyqye8ssvukn9xq7mes7sdlmenaterra1ejq3mjjgnklpa3pg4jterlfwsny055gpmcjf3fz0ev3ueajnzeysz6xxgrterra178fgrfzv7njtmdp9vghyf2dx77sah8u8jluzs7ym562chaxnmj2s6mn6m9terra1fga508hzx8dd7x8q4uhm6mdhkqv6fxrtsea3r27smdqmv5k2jgxq5zk9fcterra12k67cvfs7y7g8lca3qr4g4py6s6j69fu24gze5pjfamfpckv8mps7cymmeterra17dpnjlpgsnm8muu4msfjra4f2hrptnjp2jdpkka4p0e3px42ayxq0pmc2zterra18fzufz8cs7ez49xjwgs248x85za5v50yug55fj7lyxp9hapxyr7qnh3czsPairs: EMBER/CORAL, EMBER/JADE, EMBER/ONYX, CORAL/RUBY, JADE/TOPAZ, ONYX/QUARTZ, RUBY/PEARL, EMBER/QUARTZ, CORAL/PEARL, JADE/ONYX.
getAllTokens(pairs)unions every factory leg + wrap natives.filterTokensByLocalSearchonly ranks gems last (P534-7). Empty browse still lists RUBY / ONYX / PEARL / … with blockies identicons (not intokenlist/tokenlist.json). Defaultfrom/toistokens[0]/[1]— often a gem. Livedex.cl8y.comSwap picker shows this.PairSearchSelectempty browse usessortPairInfosByCatalogthen a Test pairs divider (#534 P534-1). Gems remain selectable. TypedRUBYstill hits./tradeauto-pickfirstCatalogPairAddressprefers economic pairs when any exist (P534-5), but gem pairs stay in the menu./pooltableCOUNT(*)over all indexed pairs).getCreatePairCw20OptionsappendsSOFT_LAUNCH_MINTABLE_TOKENSwhenever Coolify bakesVITE_TOKEN_{EMBER,…}_ADDRESS(C542-11). QUARTZ/PEARL are not in that env list but still appear on Swap via factory.frontend.env.examplesetsVITE_FAUCET_ADDRESS+ six mintable addresses. Nav Mint appears when faucet env is set (F11 / #473).GEM_SYMBOLS/isGemTokenId/isTestPair. Address match uses env mintables only — QUARTZ/PEARL rely on symbol aftertoken_info.findRouteand indexerGET /api/v1/route/solve(#501) walk the full factory graph. A production UST1↔cUSTC quote can still hop EMBER/RUBY and show that on the Route row.deploy-dex-local.shseeds gems; swarm (#293), Playwright wrap/swap, limit-order E2E, andVITE_NETWORK=localall depend on EMBER/CORAL/… remaining visible.There is no production gate that omits gems from pickers.
#534only reordered them.Why this is needed
Retail users on
dex.cl8y.comare spending real LUNC / UST1 / wrap assets. A Swap picker full of RUBY / ONYX / PEARL / QUARTZ (pixel blockies, faucet tickers) reads as unfinished / testnet, even when economic pairs work. Ranking gems last (#534) does not fix first-open browse: they are still in the list, searchable, and can be the default pay token.Product ask: remove them from the finished article now that wrap / UST1 / economic markets are the real product. Keep them where they still earn their keep: LocalTerra, swarm, contract tests, and residual holder exit.
Constraints / guardrails
UpdateMintertheatre, no factory pair delete (none exists), no forced LP withdraw, no indexer DB wipe of gem pairs. Soft-launch CW20s and pairs remain valid columbus-5 contracts.VITE_NETWORK=local(and an explicit opt-in for a mainnet-RPC QA build) still lists gems, Test pairs, faucet Mint, Create Pair gem append, swarm OE-1 pairs, and Playwright token names (EMBER, CORAL, IRON, …). Do not rewrite E2E onto UST1 as a substitute unless a test has no gem fixture./portfolio,/trader, wallet history, and LP rows must still show gem positions a user already holds so they can exit. Deep link/trade/<gem-pair-addr>and a pasted gem CW20 on Swap may still resolve for that exit path; they must not appear in empty browse or typed catalog search on production.symbol=UST1on a gem contract must stay hidden; a future listed token must not vanish because someone named it RUBY. Production hide list =GEM_SYMBOLSand the eight columbus-5 addresses (plus LocalTerra extras by symbol). Do not rely only on CoolifyVITE_TOKEN_*(QUARTZ/PEARL are absent there; unsetting env must not re-show gems).isGemSymbol('UST1')stays false.findRoute/ hybridroute/solve/ Swap Route row must not show EMBER→JADE→RUBY-class intermediates. Prefer “no route” over a gem bridge. LocalTerra may still use gem hops (existing E2E).VITE_FAUCET_ADDRESS(nav hides via F11). Recommend operatorPauseon the faucet + optionalRemoveMinterper F9 so UI hide is not the only control. Do not add KYC. Do not fold UST1 mint into/mint(#506).RUBY/EMBER/quartzreturns no gem options. Do not leak them under Test pairs on mainnet builds. LocalTerra keeps the divider (P534-1).exclude_test_pairsonroute/solveis in scope if frontend filtering is not enough to stop gem hops. Do not changevolume_quote_24hwire format.Relevant files
frontend-dapp/src/utils/pairCatalogRank.tsGEM_SYMBOLS,isGemTokenId,isTestPair, catalog sortfrontend-dapp/src/utils/constants.tsSOFT_LAUNCH_MINTABLE_TOKENS,VITE_TOKEN_*,DEFAULT_NETWORK, faucetfrontend-dapp/src/utils/tokenSearchQuery.tsfrontend-dapp/src/services/terraclassic/router.tsgetAllTokens,findRoutefrontend-dapp/src/pages/SwapPage.tsxgetAllTokensfrontend-dapp/src/components/trade/TokenSearchSelect.tsxfrontend-dapp/src/components/trade/PairSearchSelect.tsxfrontend-dapp/src/pages/TradePage.tsxfrontend-dapp/src/pages/PoolPage.tsx+ pool tablefrontend-dapp/src/pages/ChartsPage.tsxfrontend-dapp/src/utils/createPairTokenCatalog.tsfrontend-dapp/src/components/common/navItems.ts+Layout.tsxfrontend-dapp/src/pages/MintPage.tsxdeployments/mainnet-soft-launch/frontend.env.exampleVITE_FAUCET_*/VITE_TOKEN_*route/solvedocs/frontend.md§ Pair catalog rank, token searchskills/AGENTS_FRONTEND_PAIR_CATALOG_RANK.mddocs/runbooks/soft-launch-faucet.mdRecommended direction
isGemTokenId):retailExposeTestTokens()=VITE_NETWORK !== 'mainnet'orVITE_SHOW_TEST_TOKENS=true. Production Coolify staysVITE_NETWORK=mainnetwith the override unset. LocalTerraVITE_NETWORK=localunchanged.GEM_SYMBOLS) so hide-by-address works even if Coolify dropsVITE_TOKEN_*. QUARTZ/PEARL must be in that address set.getAllTokens(or a wrapper used by picker + defaultfrom/to) so empty browse, typed search, and initial selection are economic-only on production.factoryPairs/ indexer rows fed toPairSearchSelect,/pooldefault+search, Charts menu. User column sort on/poolmust also omit gems on production (otherwise Vol sort resurfaces RUBY/PEARL).gems:append when!retailExposeTestTokens(). Custom paste of a gem CW20 still hits factory whitelist (honest) — do not special-case a “blocked” toast that teaches attackers the list; paste remains #382 checksum + code-id check.VITE_FAUCET_ADDRESS;/mintstays the existing unavailable path.!retailExposeTestTokens(), drop test pairs from the BFS graph and rejectroute/solvepaths whose hops include a gem asset (client-side sufficient if the indexer cannot filter yet; add query flag if tests show leaks).dex.cl8y.com; operator F9 pause.make verify-issue-<iid>for the new gate.Acceptance criteria
VITE_NETWORK=mainnet, override unset): Swap empty YOU PAY / YOU RECEIVE lists no EMBER, CORAL, JADE, ONYX, RUBY, TOPAZ, QUARTZ, PEARL (or LocalTerra extras). Economic + wrap tokens remain.RUBY,onyx,pearl,quartz,emberon that build returns no gem rows. Address paste of a gem CW20 is not offered as a browse hit; deep-link/exit hatch documented./poolempty browse and production search do not list the ten gem pairs. Test pairs divider is absent on production (nothing to group)./tradeauto-pick are economic (UST1 hub / wrap), nevertokens[0]gem.VITE_FAUCET_ADDRESSunset). Runbook records faucet Pause (or explicit risk acceptance if left running).VITE_NETWORK=local): gems still appear; Test pairs still shows; Mint still works when faucet env set; existing Playwright / swarm fixtures still find EMBER/CORAL.VITE_SHOW_TEST_TOKENS=trueon a mainnet-network build restores gem browse (QA). Default production image does not set it.make verify-issue-<iid>cover A1–A11. No always-on “test tokens removed” banner.Test plan — functional paths
Unit / RTL
retailExposeTestTokensmainnet+ unset override → false;local→ true;mainnet+VITE_SHOW_TEST_TOKENS=true→ trueisGemTokenId; UST1 / wrap addrs are notfilterTokensByLocalSearchproductionRUBYempty; economic ids remain sortedcontract_addr; no Test pairs label/poolproductionsort=volume_24hmock pages omit gem pairsgems: []equivalent; local still appends env gemsfrom/toare non-gemfindRouteproduction graph#534/#542/#547/#481tests still pass on local vitest env (VITE_NETWORK=local)Playwright (5 workers; LocalTerra for local path; production path = unit + optional mainnet-env Vite)
VITE_NETWORK=mainnetagainst LocalTerra factory or fixture)Manual QA (
dex.cl8y.comafter Coolify rebuild)RUBY,EMBER,quartz: no hits.make dev: gems still there; faucet still drips.Test plan — attack, hack, and abuse vectors
RUBYis not hidden; hide requires gem address (or known gem symbol and not in economic hub/tokenlist). Document the rule; unit-test UST1 vssymbol=RUBYgem addr.symbol=cLUNCbut gem code-id / unknown provenance stays subject to existing #311 / whitelist — this issue must not mark it as a “real” hub just to fill the picker. Do not invert the gem set into an allowlist of tickers.onChangestill only emits ids in the gated options list (#481). Pastingterra1…rubyinto search does not select a hidden gem unless the exit-hatch path is explicitly open. NodangerouslySetInnerHTML./trade/<gem-pair>must not look like UST1/USTR (identity copy = contracts, T541-4). Invalid /javascript:/ HTML params still rejected (T541-2). Optional legacy notice must be static React text.getPairs(q=RUBY)may still return gem rows; UI must drop them on production after factory intersection. Do not trust indexeris_testif absent — client gem set is source of truth.VITE_TOKEN_*must not re-list gems (hardcoded columbus-5 addrs). Unsetting faucet must hide Mint even if token envs remain.VITE_SHOW_TEST_TOKENSis a build-arg, not a runtime query (?showGems=1is forbidden — phishing / support-scam vector).Dripvia LCD. Acceptance A6 requires Pause or documented acceptance. Pause does not clear cooldowns (F9).useCodeIdCheckor pair-creation fee. Do not add a client-only “blocked token” that can be bypassed by LCDCreatePair.Verification criteria
Must prove:
dex.cl8y.comSwap screenshot no longer shows the gemstone list.Issue is done when a mainnet Coolify rebuild matches A1–A12 and LocalTerra regression (
make test-frontend+ relevant E2E) is green. Not done if gems are only moved below a divider, or if chain contracts were deleted “to be sure.”marked as related to #534
marked as related to #473
marked as related to #542
marked as related to #547
marked as related to #489
marked as related to #501
mentioned in commit
db6911f5bcmentioned in merge request !1094
mentioned in commit
8515da1925Post-merge note (!1094 on main). P562 retail gem filter + gem-bridge quote reject is on
main.Remaining ops / QA (issue not done until these):
VITE_NETWORK=mainnet,VITE_SHOW_TEST_TOKENSunset,VITE_FAUCET_ADDRESSunset (Mint nav).mentioned in commit
4f24107fa7Sanity after stack: unit/docs 8/8 PASS. Playwright P1 failed because
e2e/issue-158-swap-route.spec.tsand the grep"pay picker lists every factory token including gems"are not in the tree.verify-issue-562.shnow SKIPs that rung instead of failing. Track adding the LocalTerra EMBER pay-list spec on the post-merge ops issue.mentioned in issue #573
marked as related to #573
mentioned in issue #583
mentioned in issue #587
mentioned in issue #593
mentioned in issue #595
mentioned in issue #597
mentioned in issue #599
mentioned in issue #619
mentioned in issue #630
mentioned in issue #631
marked as related to #631
mentioned in issue #632
mentioned in issue #639
mentioned in issue #655
marked as related to #655
mentioned in issue #657
mentioned in issue #660
mentioned in issue #661
mentioned in issue #662
mentioned in issue #664
mentioned in issue #682
mentioned in issue #683
mentioned in issue #687
mentioned in issue #690
mentioned in issue #692
mentioned in issue #711
marked as related to #711
mentioned in issue #713
mentioned in issue #715