feat(factory): allowlisted IBC natives starting with USDC #1220
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#1220
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
Add allowlisted IBC bank denoms as first-class CL8Y factory pair assets, starting with one pinned columbus-5 USDC denom. Retail should be able to create listed pairs, provide/withdraw, and swap CW20 ↔ that USDC (and later other allowlisted IBC natives) on this DEX — not only as a foreign-hop intermediate on other venues.
Today the stack is CW20-only: factory, pair, and router reject
AssetInfo::NativeToken. LUNC/USTC reach the graph only via wrap CW20s. Unknownibc/…strings display as raw denoms and Swap/Create query parsers treatibc/as hostile.This is not #691 (IBC USDC as other-DEX routing intermediate). #691 U7 explicitly leaves CL8Y factory USDC markets to a later ticket. This is that ticket. Do not expand #690/#691 admission here. Circle CCTP (#1215) is a different mint path and stays out of scope.
Definition of Ready (GWT)
Given a factory whose native-IBC allowlist contains exactly one pinned columbus-5 USDC denom (
ibc/+ 64 hex) and a CW20 whose livecode_idis factory-whitelistedWhen
CreatePairis submitted with those twoAssetInfos plus the pair-creation feeThen the pair instantiates, is queryable, and F6 pins the CW20
code_idonly (native leg has nocode_id)Given that pair has liquidity
When a user swaps the listed CW20 for the pinned USDC (CW20 send) and the reverse (native IBC attached to pair/router execute)
Then execute matches quote within
min_return/minimum_receive; the user receives the declared ask denom; unlistedibc/…denoms revert at create and at executeCurrent codebase
CL8Y pairs and routing are factory-local and CW20-only. Native LUNC/USTC never sit on a pair; they wrap to cLUNC/cUSTC first.
Factory / pair / router
smartcontracts/packages/dex-common/src/types.rs—AssetInfo::NativeTokenexists for wire compatibility.assert_is_token()errors: Native tokens are not supported; use CW20 wrapped tokens instead.smartcontracts/packages/dex-common/src/factory.rs—CreatePairdocs: onlyAssetInfo::Token; natives rejected.smartcontracts/contracts/factory/src/contract.rsexecute_create_pair— both legsassert_is_token()→NativeTokenNotSupported. Listing gate is F6WHITELISTED_CODE_IDS. IBC denoms have nocode_id.AddWhitelistedCodeId/RemoveWhitelistedCodeIdonly. No native-denom map.smartcontracts/contracts/pair/src/asset_code_id_guard.rs— both assets must be CW20; native →NativeTokenNotSupported. Write paths re-check pin + whitelist (#582).ExecuteMsg::Swaprejects native offer and tells CW20 users toSend+ hook. Pair wasm has noBankMsgnative in/out path.NativeTokenNotSupportedinrouter/src/contract.rsandblacklist_guard.rs. Wrap-mapper mapsuluna↔ cLUNC and **uusd↔ cUSTConly** ([NATIVE_TOKEN_WRAPPING.md`](NATIVE_TOKEN_WRAPPING.md)). No cUSDC product.Indexer / oracles / tokenlist
docs/route-solver.md). Nativeuluna/uusdare not routable except via wrap CW20s.indexer/src/indexer/asset_resolver.rs— retail labels onlyuluna→LUNC anduusd→USTC. Unknown natives includingibc/…insert as raw denom (N630-5). Native upsert hardcodes 6 decimals.vfdusd). No USDC feed. Do not treat vFDUSD, UST1, or$1as USDC (#580, #691 U4).tokenlist/tokenlist.json— natives are LUNC/USTC only. Duplicate tickers fail CI (#715 TL-1).Frontend
uluna/uusd(factory would reject). Catalog is bundled CW20s (skills/AGENTS_FRONTEND_CREATE_PAIR_PICKER.md).ibc/is hostile and ignored (swapQueryParams.ts,createPairQuery.ts).ibc/…stays raw unless indexer has a symbol. Do not invent USDC for vFDUSD / UST1 / a second IBC hash.Upstream contrast
Classic TerraSwap does support
AssetInfo::NativeTokenincludingibc/[A-F0-9]{64}and typically 0 burn tax on IBC bank sends (is_ibc_token()indocs/classic-terraswap-upstream-source.md). That is not CL8Y behavior today.Classic USDC is not one denom. Axelar-bridged
uusdcvs Noble native USDC are differentibc/…hashes. Phoenix-1 hashes must not be copied onto columbus-5.Why this is needed
Retail and LPs cannot hold or trade IBC USDC on CL8Y pools/books. Wrap cannot paper over this: there is no CMM USDC wrap. Other-DEX TOKEN/USDC liquidity (#691) does not create a CL8Y USDC market, fee, or TVL. Allowlisting one well-identified USDC denom unblocks honest USDC pairs without opening every IBC hash.
Constraints / guardrails
AddWhitelistedCodeId). Unlistedibc/…,uluna,uusd,factory/…, and junk strings revert atCreatePairand at pair/router execute.uluna/uusdstay on the existing wrap path; this ticket does not list LUNC/USTC as native pair legs.code_idstill must be whitelisted and pinned. Native IBC leg: nocode_idpin (skip or sentinel that cannot be confused with a real id).RefreshAssetCodeIds/ freeze still apply to the CW20 leg. TOKEN/USDC where TOKEN wasm is unlisted stays ineligible.^ibc/[A-F0-9]{64}$(case-normalize to uppercase hex as Terra bank does). Reject lowercase mix, truncated hashes,ibc/USDC, CW20terra1in the native slot.$1, not vFDUSD, not UST1 peg.ibc/…stays raw (N630-5). Unique-symbol CI (TL-1): oneUSDCrow.ibc/ignore remains for unlisted hashes. The pinned denom (and later allowlisted denoms) may appear in Swapfrom/toand Createa/bafter factory allowlist + tokenlist pin. Never echo raw hostile query into combobox/share.RegisterListedPairon foreign pair addrs.#276uluna fee still required. Native IBC does not skip the fee or F6 on the other leg.Relevant files
smartcontracts/packages/dex-common/src/types.rsassert_is_token()as the only gatesmartcontracts/packages/dex-common/src/factory.rsCreatePair+ new add/remove/query native-denom msgssmartcontracts/contracts/factory/src/contract.rs+state.rsCreatePair; instantiate pair with native legsmartcontracts/contracts/pair/src/contract.rsBankMsg)smartcontracts/contracts/pair/src/asset_code_id_guard.rssmartcontracts/contracts/pair/src/blacklist_guard.rssmartcontracts/contracts/router/src/contract.rs+msg.rssmartcontracts/contracts/router/src/blacklist_guard.rssmartcontracts/tests/indexer/src/indexer/asset_resolver.rsindexerroute-solver / pair ingesttokenlist/tokenlist.json+ logofrontend-dapp/src/utils/createPairTokenCatalog.tsfrontend-dapp/src/utils/swapQueryParams.ts+createPairQuery.tsibc/HASHis not hostilefrontend-dappSwap / Pool provide / Tradedocs/frontend.md+NATIVE_TOKEN_WRAPPING.mdskills/AGENTS_FRONTEND_CREATE_PAIR_PICKER.mdskills/AGENTS_FRONTEND_NATIVE_TICKERS.mddocs/runbooks/or factory listing policycode_idRecommended direction
AddWhitelistedNativeDenom/RemoveWhitelistedNativeDenom/IsNativeDenomWhitelisted(names TBD), governance-only. Regex-validate. Empty by default in tests unless the test inserts the pin.Reject two natives in v1 (no USDC/LUNC native-native pair; LUNC stays wrapped). Reject
uluna/uusdas the native leg.info.fundsfor IBC. Swap: existing CW20 hook when offer is CW20;ExecuteMsg::Swapwith funds when offer is the pinned native. Output native →BankMsg::Send. Hybrid/book: native rung amounts are bank denoms; do not wrap mid-hop.code_id+0(or a dedicatedNone) for the native slot. Guards that callassert_is_token()must branch. Unpinned native slot is not a freeze of the CW20 leg.SwapOperationnative IBC offer/ask only if factoryPair { asset_infos }returns that pair (same pin-not-caller-addr rule as foreign hops, but this is our factory). First hop native: execute with attached coins, not CW20 hook. Last hop native: bank send to recipient;unwrap_outputillegal. No wrap-mapper.uluna/uusd). Swap picker shows USDC. Deep links: allow only the pinned hash (or aUSDCsymbol that resolves uniquely — TL-1). Provide/withdraw UI must attach bank funds. No wrap toggle for USDC.Do not: permissionless IBC listing; treating all stables as one hub; IBC transfer/bridge UI; CCTP mint; copying TerraSwap router unbounded native support for
uluna.Acceptance criteria
ibc/…oruluna/uusdnative reverts.minimum_receive;unwrap_output+ USDC ask reverts or is ignored (pick one, test it).ibc/query ignored; pinned hash may prefill.$1USD TVL; fail closed for USD stats that need it.make verify-issue-<id>. Mainnet allowlist remains empty until governance; LocalTerra/tests inject the pin.Test plan (functional)
CreatePairCW20 + pinned USDCasset_infosmixedCreatePairCW20 + unlistedibc/…CreatePairtwo natives /uluna+CW20Swap+ funds)minimum_receive; no wrap-mapperibc/FOOdisplaynative_token; still charges uluna fee?from=<pin>&to=<cw20>ibc/still ignoredContracts: multitest with a fake IBC denom in the allowlist (need not be a live hash). Indexer: unit tests on
native_retail_meta+ pair ingest fixture. Frontend: Vitest catalog + query-parser tables; Playwright when LocalTerra can mint a native denom or bank-fund a testibc/….Test plan (attack, hack, abuse)
Wasm / listing / wallet surface — required.
ibc/code_id; not the native pin; no ticker collision in tokenlist (TL-1)Pairinfo.fundsminimum_receive$1peg / vFDUSD as USDCuusd)ibc/<script>BankMsgthen CW20 hookSwapInProgress/ no writable re-entryVerification criteria
Close when:
make verify-issue-<iid>covers factory allowlist + mixed CreatePair + pair swap both ways + router no-wrap + indexer labels/decimals + frontend catalog/parser tests.Blocked on: a verified columbus-5 USDC denom (LCD). Code may land with a test-only pin. Mainnet
AddWhitelistedNativeDenomis governance, not this MR's hidden side effect.Out of scope
uluna/uusdCL8Y pair legs (keep wrap).First-pass model recommendation
Recommendation: grok-high
Rationale: This breaks the CW20-only invariant across factory, pair, router, dex-common, indexer, tokenlist, and Swap/Create frontend (far more than three production files, multiple subsystems). It is founder-required wasm plus 2-of-3 listing of a bank denom, with burn-tax, F6 mixed pins, and oracle/ticker identity risk. There is no existing local pattern for native IBC on CL8Y pairs (wrap-mapper is LUNC/USTC only). Verification needs contract multitest, indexer ingest, and dApp fund-attachment paths — not a single-file deterministic edit. Composer criteria fail on file/subsystem count, protocol change, and contracts/listing surface.
cl8y-agent-control: queued
implementjob09e159ce-5239-4229-99a6-55bc31ceb0cf(not executed; no Hetzner VM).