feat: Create Pair picker for listed CW20s (token list + custom paste) #542
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#542
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
/create(CreatePairPage) only accepts two rawterra1…contract fields. Operators and retail users who want a market for a listed CW20 (cLUNC, cUSTC, CL8Y, USTR, UST1, vFDUSD, ALPHA, …) must copy-paste long addresses by hand.Ship a listed-CW20 picker on Create Pair, sourced from the repo token list / dApp registry (not Swap’s factory graph), while keeping a custom-address paste path for tokens that are not listed yet. Factory
CreatePairstays the security boundary.Related: #382 (bech32 checksum), #376 (code-ID whitelist ≠ token safety), #378 (logo host allowlist), #481 (Swap
TokenSearchSelect— wrong universe for this page), #508 (UST1 AMM ≠ oracle mint), #518 (digit LP ticker), #534 (economic-first catalog).Current codebase
/createUIToken A/B Contract Address),placeholder="terra1...". Submit enabled only when both passgetTerraAddressInputErrorandtokenA !== tokenB(#382). Wallet disconnected → CTA Connect Wallet To Create.useCodeIdCheckqueries factoryget_whitelisted_code_ids+ LCDgetChainContractInfo. Non-whitelisted → amber warning + “transaction will likely fail”. Does not disable submit. Query disabled until the field looks liketerra1and length> 5.CreatePairNativeTokenNotSupported). Both code IDs must be onWHITELISTED_CODE_IDS. Pair must not exist. Decimals ≤MAX_PAIR_ASSET_DECIMALS_BOOTSTRAP(18). One create per block. Uluna pair-creation fee whenpair_creation_fee_uluna > 0(#276).createPair(wallet, tokenA, tokenB)→tokenAssetInfo(terra1…→ CW20; anything else → native denom). A picker that emitsuluna/uusdwould submit a tx the factory rejects.tokenlist/tokenlist.json+tokenlist/README.md. Community MR process. CW20 entries haveaddress,symbol,name,decimals,logoURI. Natives LUNC/USTC havedenomonly. Not consumed by Create Pair.tokenRegistry.tsis a display map (logos / symbols).CW20_MAPhas hardcoded columbus-5 addresses for CL8Y, USTR, ALPHA, USTRIX, SpaceUSD, plus env overlays for cLUNC / cUSTC / UST1 / vFDUSD.TOKENS[]entries themselves have noaddressfield.TokenSearchSelectis gated togetAllTokens(pairs)(already-listed factory markets). MintTokenSelectis the faucet set. Neither is the create-pair universe: Swap omits tokens that do not yet have a pair; Mint is gems only.VITE_LUNC_C_TOKEN_ADDRESS,VITE_USTC_C_TOKEN_ADDRESS,VITE_UST1_TOKEN_ADDRESS,VITE_VFDUSD_TOKEN_ADDRESS,VITE_CL8Y_TOKEN_ADDRESS,VITE_TOKEN_*_ADDRESS(EMBER…). Mainnet addresses intokenlist.jsonare wrong on LocalTerra unless env overlays win./ust1mint/redeem.tokenlist.jsonlists CL8Ydecimals: 6;tokenRegistry/ on-chain CL8Y is 18 (#476). Create Pair must not trust tokenlist decimals for any on-chain amount.Why this is needed
Constraints / guardrails
uluna/uusd/ LUNC / USTC. Factory rejects natives;tokenAssetInfowould encode them asnative_token.TokenInforemain authoritative (#376,docs/runbooks/cw20-code-id-ops.md). Do not skip or weakenuseCodeIdCheck. Do not copy that implies a listed ticker is “safe” or audited.tokenlist.json) must still be enterable. Paste still runs #382 format + checksum gates before submit.GET /api/v1/tokens). Bundle / import the repo list (or a generated TS module from it). Remote fetch is a supply-chain and availability risk and would violate Swap’s “no arbitrary external list” rule if copied there.getAllTokens(pairs)+ wrap enrichment) —skills/AGENTS_FRONTEND_TOKEN_SEARCH.md.VITE_*wrap / UST1 / CL8Y / faucet token addresses are set, the picker must use those addresses, not columbus-5tokenlist.jsonvalues. Empty env → mainnet published address (same pattern asCW20_MAP).onChangeonly emits addresses from the gated CW20 catalog (or the validated paste field). Search tricks must not select the other leg (excludeToken). Same-address A/B stays a hard disable.resolveTrustedTokenLogoUrl/resolveAllowedTokenLogoUri(#378). Symbols/names render as text only (noinnerHTML). Search input truncated (TOKEN_SEARCH_MAX_QUERY_LENGTH= 128).useId()+htmlFor/aria-labelon both legs (#143). Combobox keyboard parity with Swap (#350 / #481) if a search control is used.Token A,Token B, orSelect token). No “this is not an AMM essay”. Keep the existing UST1 one-liner (#508 U1). Do not add a permanent “token list verified by governance” banner.CreatePair, whitelist, pair-creation fee, or LP ticker rules (#518).decimalsfor any amount / fee math. Create Pair does not send amounts; do not start.Relevant files
frontend-dapp/src/pages/CreatePairPage.tsxfrontend-dapp/src/pages/CreatePairPage.test.tsxfrontend-dapp/src/services/terraclassic/factory.tscreatePair,getWhitelistedCodeIdsfrontend-dapp/src/utils/terraAddressValidation.tsfrontend-dapp/src/utils/tokenRegistry.tsCW20_MAPfrontend-dapp/src/utils/constants.tsVITE_*wrap / UST1 / faucet gem addressestokenlist/tokenlist.jsontokenlist/README.mdfrontend-dapp/src/components/trade/TokenSearchSelect.tsxfrontend-dapp/src/components/ui/TokenSelect.tsxfrontend-dapp/src/utils/tokenSearchQuery.tsfrontend-dapp/src/utils/tokenLogoAllowlist.tsfrontend-dapp/src/types/index.tstokenAssetInfo(native vs CW20)frontend-dapp/src/utils/ust1SecondaryMarket.tssmartcontracts/contracts/factory/src/contract.rsexecute_create_pair(CW20 + whitelist) — read-only for this issuedocs/frontend.md§ Create pairdocs/security-model.md§ Code ID whitelistRecommended direction
Frontend-only. One catalog helper + Create Pair UI. No factory / indexer API change.
Catalog (new helper, e.g.
createPairTokenCatalog.ts)Single function
getCreatePairCw20Options():tokenlist.jsonbundled, ortokenRegistryCW20_MAP+TOKENS— pick one source of truth and generate/test the other so they cannot drift on address).VITE_LUNC_C_*/VITE_USTC_C_*/VITE_UST1_*/VITE_VFDUSD_*/VITE_CL8Y_*when set.SOFT_LAUNCH_MINTABLE_TOKENSwhen those env addresses are present (LocalTerra gems).Do not import
getAllTokens(pairs).UI
Each leg: searchable listed-token control (reuse
TokenSearchSelectwith the catalog ids, orTokenSelectif the list stays ~15) plus a progressive-disclosure Custom contract text field (closed by default when a listed token is selected).terra1…is the unlisted path; picker may show “Custom” / raw address.excludeTokenomits the other leg’s address from options.Docs
Extend
docs/frontend.md§ Create pair with catalog invariants. Short agent playbook only if the helper is non-obvious. Do not rewritetokenlist/README.mdlisting process except a one-line “Create Pair picker reads this list (CW20 only)”.Acceptance criteria
/createcan select Token A and Token B from listed CW20s (at least cLUNC, cUSTC, CL8Y, USTR, UST1, vFDUSD when addresses resolve) without pasting.uluna/uusdnever appear as selectable create-pair tokens.terra1…still enables Create Pair; #382 format/checksum errors still disable submit.tokenlist.jsonvalues.createPairwith two CW20AssetInfos. No native encoding. No factory/indexer API change.docs/frontend.md§ Create pair documents the catalog + paste dual path.make verify-issue-<iid>covers unit + (when stack is up) Playwright smoke.Test plan (all paths)
Unit / RTL (no chain)
uluna/uusdVITE_LUNC_C_TOKEN_ADDRESS=terra1local…→ catalog uses localCW20_MAPfallbacksVITE_TOKEN_*setcreatePaircalled with those addrsulunainto custom fieldexcludeTokenTokenSearchSelecttests still passPlaywright / LocalTerra (when
make has-localterra)/create, pick two LocalTerra listed/env CW20s that have no pair yet.env.local; Create Pair enabledOn-chain create (optional, one-per-block + fee): only if the pair does not exist and fee uluna is available. Do not spam
CreatePairin CI on a shared LocalTerra.Test plan — attack, hack, and abuse
CL8YvsCL8Y/U5T1)logoURI(javascript:,http://, non-allowlisted host)resolveAllowedTokenLogoUridrops it; blockie fallback; no script loadsymbol/name(<img onerror>)TokenInfo)getAllTokens”)uluna)native_tokenon this pageILIKEonChangewith id not in options (tampered handler / extra option)terra1checksum fail,terravaloper, other HRP)/create?a=&b=)VITE_LUNC_C_TOKEN_ADDRESSis the local wrap token.Verification criteria
Close only when all of the following are true:
make test-frontend(or scoped Vitest for catalog +CreatePairPage+ existing #382/#508 cases) passes.make verify-issue-<iid>exists and covers T1–T18 (and P1–P5 when LocalTerra is up — provision withmake setup-cloud-localterra/make has-localterra, do not SKIP)./createon LocalTerra — pick two env CW20s; confirm filled addresses match.env.local; paste path still works; natives absent; Swap pay/receive list unchanged.docs/frontend.md§ Create pair updated; no factory/indexer behavior change in the diff.marked as related to #382
marked as related to #376
marked as related to #378
marked as related to #481
marked as related to #508
mentioned in merge request !1071
mentioned in commit
d7aa1c07e1mentioned in commit
7fdc3a44adMerged via !1071 onto
main(7fdc3a44) after landing !1069 + !1070. Conflicts were Makefile.PHONY/help unions plus both #541 and #542 rows inAGENTS_FRONTEND_COPY_COGNITIVE_LOAD.md.Sanity check on
main:make verify-issue-542— 9/9 pass (unit T1–T18, catalog isolation, C542-1–C542-11 docs/skills). Playwright P1–P5 on LocalTerra all passed (listed pick, custom paste, no false success on existing pair, keyboard, phone width, natives absent).Remaining (not a merge blocker): no on-chain Create Pair
e2e-txfor a novel pair (one-per-block + fee). P3 only asserts no false success, not a specific factory duplicate-pair error string. Tracked in the post-merge follow-up.mentioned in issue #545
marked as related to #545
mentioned in merge request !1073
mentioned in issue #562
marked as related to #562
mentioned in issue #593
mentioned in merge request !1120
mentioned in issue #602
mentioned in issue #630
mentioned in merge request !1141
mentioned in merge request !1142
mentioned in issue #711
mentioned in issue #713
mentioned in issue #715
marked as related to #715