design: IBC USDC as other-DEX routing intermediate (not USTC/LUNC) #691

Open
opened 2026-08-27 11:52:44 +00:00 by PlasticDigits · 2 comments
PlasticDigits commented 2026-08-27 11:52:44 +00:00 (Migrated from gitlab.com)

Summary

Follow-up / analysis-only until the parent ships. Decide whether IBC USDC on Terra Classic can become a common intermediate for other-DEX v2 hops in the CL8Y route solver (sibling of LUNC / USTC). Do not implement USDC intermediates on the parent ticket.

Parent: #690 — external DEX routing + 3% CMM fee + max-1 foreign hop.


Current codebase

  • CL8Y router and solver are CW20-only. AssetInfo::NativeToken in router ops is NativeTokenNotSupported. Wrap-mapper maps uluna ↔ cLUNC and **uusd ↔ cUSTC only** ([NATIVE_TOKEN_WRAPPING.md](NATIVE_TOKEN_WRAPPING.md), [#516](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/516)). There is **no** USDC wrap CW20 in [tokenlist/tokenlist.json`](tokenlist/tokenlist.json).
  • Indexer graph nodes are CW20 contract addresses (docs/route-solver.md). Native uluna / uusd are not routable except via wrap CW20s. Unknown natives / ibc/… display as raw denoms (#630 N630-5).
  • USD oracles are ticker-scoped USTC / LUNC / CEX-FDUSD (path vfdusd) (#515, #580). No USDC oracle. Hub USD is cUSTC / LUNC / UST1 / USTR (#556). Do not treat CEX FDUSD or vFDUSD as USDC.
  • Factory F6 whitelist is CW20 code_id. IBC denoms have no code_id. Parent admission rule “both tokens on a whitelisted code_id” does not apply as-is to a USDC native leg.
  • TerraSwap-family pairs do support AssetInfo::NativeToken including ibc/[A-F0-9]{64} (docs/classic-terraswap-upstream-source.md IBC_REX). Native IBC typically 0 Terra burn tax on send (Terraswap is_ibc_token()).
  • Classic USDC is not a single denom. Public Terra 2 docs list Axelar-bridged USDC as ibc/B3504E092456BA618CC28AC671A71FB08C6CA0FD0BE7C8A5B5A3E2DD933CC9E4 (verify columbus-5, not phoenix-1). Noble channel-113 native USDC is a different IBC denom. Axelar vs Noble vs wormhole-style assets must not be collapsed into one “USDC” node.
  • Parent product: foreign v2 pair eligible only if one leg is USTC or LUNC until this analysis lands.

Why this is needed

Many other-DEX pools are TOKEN / USDC rather than TOKEN / USTC or TOKEN / LUNC. Until USDC is an allowed intermediate, those pools stay out of the solver even when they are the deepest honest v2 LP. Shipping USDC on the parent ticket without denom identity, oracle USD, wrap policy, and burn-tax / hop-shape analysis would either mis-price $1k TVL, mix Axelar and Noble USDC, or break CW20-only router invariants.


Constraints / guardrails

ID Rule
U1 — parent first No USDC graph edges until parent external-DEX execute (max-1 hop, 300 bps CMM, balance-delta) is specified. This ticket may design in parallel; it must not expand parent admission to USDC.
U2 — one denom, explicitly pinned Pick one columbus-5 IBC denom (document hash, channel, counterparty, decimals). Do not treat Axelar uusdc and Noble USDC as fungible. A second denom = a new issue or an explicit multi-denom table with no implicit 1:1.
U3 — no fake wrap Do not invent a cUSDC wrap-mapper mapping without a real CMM wrap product. If the hop is native IBC in/out, use the parent skip wrap/unwrap pattern, not InstantWithdraw.
U4 — $1k TVL USD(USDC) must be a real feed (CEX USDC or documented stable oracle), fail closed if down — not $1 hardcode, not vFDUSD, not UST1 peg.
U5 — code_id Native USDC leg: no CW20 pin. The other leg still needs factory-whitelisted code_id if it is CW20. TOKEN/USDC where TOKEN is unlisted wasm stays ineligible.
U6 — burn tax / IBC Confirm columbus-5 tax on ibc/… bank sends (expect 0). Router must not route IBC through a path that applies USTC 1.5% tax by mistake (denom mix-up).
U7 — CL8Y factory This does not require listing IBC USDC as a CL8Y pair asset. Scope is foreign v2 intermediate only unless a later issue adds CL8Y USDC markets.
U8 — display Retail ticker USDC only for the pinned denom. Raw ibc/… for anything else (N630-5). Do not show “USDC” for vFDUSD / UST1.

Relevant files


Produce a short design note (issue comment or docs/) that answers, with LCD evidence:

  1. Which USDC denoms exist on columbus-5 with non-dust Terraport/TerraSwap TVL (query factory pairs whose asset_infos contain ibc/… and metadata symbol USDC).
  2. Channel + counterparty for each; Axelar vs Noble vs other.
  3. Decimals and oracle — CoinGecko/Binance USDC vs on-chain; stale-feed behavior.
  4. Hop shapes — TOKEN(CW20)/USDC(native) foreign hop in/out; whether CL8Y router must hold IBC coins (tax/trust) vs last/first hop skip pattern from the parent.
  5. $1k TVL — USD(USDC)×reserve + USD(other)×reserve; unpriced other token still ineligible.
  6. Go / no-go — ship as intermediate on parent follow-up MR, or no-go (fragmented denoms, no TVL, unsafe router-held IBC).
  7. If go: admission amendment to parent XDex-4 (“LUNC or USTC or pinned USDC denom”) and oracle work (#515-style ticker). If no-go: close this issue with the evidence, do not leave USDC as a silent maybe.

Acceptance criteria

  • LCD inventory of columbus-5 USDC IBC denoms used in Terraport and Classic TerraSwap pairs (table: denom, channel, TVL, pair count).
  • Written choice of at most one pin (or explicit multi-pin table) with non-fungibility warnings.
  • Oracle plan (feed, fail-closed, not $1, not vFDUSD).
  • Router/wrap plan: native IBC first/last hop vs “must wrap” (likely no wrap product).
  • Interaction with parent 300 bps skim (CMM can hold IBC USDC? bank send vs CW20).
  • Go / no-go recorded on this issue. Implementation (if go) is a follow-up MR after parent execute exists — not a drive-by on the parent MR.
  • Tokenlist / dApp ticker rules if go (USDC vs raw ibc).

Test plan (analysis / later impl)

Analysis (this issue):

  1. LCD: Terraport + TerraSwap pairs pages; collect native_token.denom matching ibc/[A-F0-9]{64}; resolve denom traces; sample reserve TVL.
  2. Confirm tax rate query for that denom (0 vs USTC).
  3. Confirm wrap-mapper config has no USDC mapping.

If implementation is approved later:

  1. Parent tests extended: TOKEN→USDC foreign last hop skip-unwrap analogue (native IBC out, no wrap-mapper).
  2. USDC→TOKEN foreign first hop skip-wrap (native in, no WrapDeposit).
  3. $1k gate uses USDC oracle; oracle down → pair dropped.
  4. Axelar denom must not fill a Noble USDC quote (and vice versa).
  5. Ranking still subtracts 300 bps; CL8Y USTC paths win when within the fee stack.
  6. CMM skim in IBC USDC: bank send succeeds; CMM address can receive IBC.

Test plan (attack, hack, abuse)

Vector Expect
Denom confusion (Axelar vs Noble vs fake ibc/…) Only the pinned denom is an intermediate; others ineligible.
Symbol spoof CW20 named USDC Still needs whitelisted code_id; not the IBC intermediate.
$1 peg on depeg Oracle fail-closed or live CEX; never hardcode $1.
vFDUSD / UST1 as USDC Forbidden (#580 / UST1 unstablecoin).
Router holds IBC, attacker donates wrong ibc denom minimum_receive / delta only on declared denom (parent XDex-5).
Burn tax surprise if denom is not tax-exempt Quote and execute must use the same tax; fail closed if unknown.
IBC timeout / not a swap issue Bridging is out of scope; only on-chain DEX hops.
Using USDC to skip 3% or whitelist 300 bps still applied; CW20 other-leg still F6-gated.
Phoenix-1 denom copied onto columbus-5 Pin from columbus-5 LCD only.

Verification criteria

Close analysis when the go/no-go comment has LCD tables and U1–U8 answers.

Close implementation (if split later) when parent verify target is extended with USDC hop fixtures and oracle fail-closed tests.

Out of scope: wrapping USDC in ustr-cmm; CL8Y factory USDC pairs; IBC transfer UI; treating all stables as one hub.

## Summary **Follow-up / analysis-only until the parent ships.** Decide whether **IBC USDC** on Terra Classic can become a **common intermediate** for other-DEX v2 hops in the CL8Y route solver (sibling of LUNC / USTC). **Do not implement** USDC intermediates on the parent ticket. Parent: [#690](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/690) — external DEX routing + 3% CMM fee + max-1 foreign hop. --- ## Current codebase - CL8Y router and solver are **CW20-only**. `AssetInfo::NativeToken` in router ops is `NativeTokenNotSupported`. Wrap-mapper maps **`uluna` ↔ cLUNC** and **`uusd` ↔ cUSTC` only** ([`NATIVE_TOKEN_WRAPPING.md`](NATIVE_TOKEN_WRAPPING.md), [#516](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/516)). There is **no** USDC wrap CW20 in [`tokenlist/tokenlist.json`](tokenlist/tokenlist.json). - Indexer graph nodes are CW20 contract addresses ([`docs/route-solver.md`](docs/route-solver.md)). Native `uluna` / `uusd` are not routable except via wrap CW20s. Unknown natives / `ibc/…` display as raw denoms ([#630](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/630) **N630-5**). - USD oracles are ticker-scoped **USTC / LUNC / CEX-FDUSD (path `vfdusd`)** ([#515](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/515), [#580](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/580)). **No USDC oracle.** Hub USD is cUSTC / LUNC / UST1 / USTR ([#556](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/556)). Do **not** treat CEX FDUSD or vFDUSD as USDC. - Factory **F6** whitelist is **CW20 `code_id`**. IBC denoms have **no** `code_id`. Parent admission rule “both tokens on a whitelisted code_id” **does not apply as-is** to a USDC native leg. - TerraSwap-family pairs **do** support `AssetInfo::NativeToken` including `ibc/[A-F0-9]{64}` ([`docs/classic-terraswap-upstream-source.md`](docs/classic-terraswap-upstream-source.md) `IBC_REX`). Native IBC typically **0 Terra burn tax** on send (Terraswap `is_ibc_token()`). - Classic USDC is **not a single denom**. Public Terra 2 docs list Axelar-bridged USDC as `ibc/B3504E092456BA618CC28AC671A71FB08C6CA0FD0BE7C8A5B5A3E2DD933CC9E4` (verify **columbus-5**, not phoenix-1). Noble `channel-113` native USDC is a **different** IBC denom. Axelar vs Noble vs wormhole-style assets must not be collapsed into one “USDC” node. - Parent product: foreign v2 pair eligible only if one leg is **USTC or LUNC** until this analysis lands. --- ## Why this is needed Many other-DEX pools are **TOKEN / USDC** rather than TOKEN / USTC or TOKEN / LUNC. Until USDC is an allowed intermediate, those pools stay out of the solver even when they are the deepest honest v2 LP. Shipping USDC on the parent ticket without denom identity, oracle USD, wrap policy, and burn-tax / hop-shape analysis would either **mis-price $1k TVL**, **mix Axelar and Noble USDC**, or **break CW20-only router invariants**. --- ## Constraints / guardrails | ID | Rule | |----|------| | **U1 — parent first** | No USDC graph edges until parent external-DEX execute (max-1 hop, 300 bps CMM, balance-delta) is specified. This ticket may **design** in parallel; it must not expand parent admission to USDC. | | **U2 — one denom, explicitly pinned** | Pick **one** columbus-5 IBC denom (document hash, channel, counterparty, decimals). Do not treat Axelar `uusdc` and Noble USDC as fungible. A second denom = a new issue or an explicit multi-denom table with **no** implicit 1:1. | | **U3 — no fake wrap** | Do not invent a cUSDC wrap-mapper mapping without a real CMM wrap product. If the hop is native IBC in/out, use the parent **skip wrap/unwrap** pattern, not InstantWithdraw. | | **U4 — $1k TVL** | USD(USDC) must be a **real** feed (CEX USDC or documented stable oracle), fail closed if down — **not** `$1` hardcode, **not** vFDUSD, **not** UST1 peg. | | **U5 — code_id** | Native USDC leg: no CW20 pin. The **other** leg still needs factory-whitelisted `code_id` if it is CW20. TOKEN/USDC where TOKEN is unlisted wasm stays ineligible. | | **U6 — burn tax / IBC** | Confirm columbus-5 tax on `ibc/…` bank sends (expect 0). Router must not route IBC through a path that applies USTC 1.5% tax by mistake (denom mix-up). | | **U7 — CL8Y factory** | This does **not** require listing IBC USDC as a CL8Y pair asset. Scope is **foreign v2 intermediate** only unless a later issue adds CL8Y USDC markets. | | **U8 — display** | Retail ticker **USDC** only for the pinned denom. Raw `ibc/…` for anything else (**N630-5**). Do not show “USDC” for vFDUSD / UST1. | --- ## Relevant files - Parent [#690](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/690) — contracts/indexer/frontend listed there - [`NATIVE_TOKEN_WRAPPING.md`](NATIVE_TOKEN_WRAPPING.md) · wrap-mapper config (uluna/uusd only) - [`tokenlist/tokenlist.json`](tokenlist/tokenlist.json) - [`indexer/src/indexer/asset_resolver.rs`](indexer/src/indexer/asset_resolver.rs) (`ibc/…` labels) - [`skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md`](skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md) (#515) - [`docs/classic-terraswap-upstream-source.md`](docs/classic-terraswap-upstream-source.md) (IBC native) - [`docs/terraport.md`](docs/terraport.md) --- ## Recommended direction (analysis deliverable) Produce a short design note (issue comment or `docs/`) that answers, with LCD evidence: 1. **Which USDC denoms exist on columbus-5** with non-dust Terraport/TerraSwap TVL (query factory pairs whose `asset_infos` contain `ibc/…` and metadata symbol USDC). 2. **Channel + counterparty** for each; Axelar vs Noble vs other. 3. **Decimals and oracle** — CoinGecko/Binance USDC vs on-chain; stale-feed behavior. 4. **Hop shapes** — TOKEN(CW20)/USDC(native) foreign hop in/out; whether CL8Y router must hold IBC coins (tax/trust) vs last/first hop skip pattern from the parent. 5. **$1k TVL** — USD(USDC)×reserve + USD(other)×reserve; unpriced other token still ineligible. 6. **Go / no-go** — ship as intermediate on parent follow-up MR, or **no-go** (fragmented denoms, no TVL, unsafe router-held IBC). 7. If go: admission amendment to parent **XDex-4** (“LUNC or USTC or pinned USDC denom”) and oracle work (#515-style ticker). If no-go: close this issue with the evidence, do not leave USDC as a silent maybe. --- ## Acceptance criteria - [ ] LCD inventory of columbus-5 USDC IBC denoms used in Terraport and Classic TerraSwap pairs (table: denom, channel, TVL, pair count). - [ ] Written choice of **at most one** pin (or explicit multi-pin table) with non-fungibility warnings. - [ ] Oracle plan (feed, fail-closed, not `$1`, not vFDUSD). - [ ] Router/wrap plan: native IBC first/last hop vs “must wrap” (likely **no wrap product**). - [ ] Interaction with parent 300 bps skim (CMM can hold IBC USDC? bank send vs CW20). - [ ] Go / no-go recorded on this issue. Implementation (if go) is a **follow-up MR** after parent execute exists — not a drive-by on the parent MR. - [ ] Tokenlist / dApp ticker rules if go (**USDC** vs raw ibc). --- ## Test plan (analysis / later impl) **Analysis (this issue):** 1. LCD: Terraport + TerraSwap `pairs` pages; collect `native_token.denom` matching `ibc/[A-F0-9]{64}`; resolve denom traces; sample reserve TVL. 2. Confirm tax rate query for that denom (0 vs USTC). 3. Confirm wrap-mapper `config` has no USDC mapping. **If implementation is approved later:** 4. Parent tests extended: TOKEN→USDC foreign last hop skip-unwrap analogue (native IBC out, no wrap-mapper). 5. USDC→TOKEN foreign first hop skip-wrap (native in, no WrapDeposit). 6. $1k gate uses USDC oracle; oracle down → pair dropped. 7. Axelar denom must not fill a Noble USDC quote (and vice versa). 8. Ranking still subtracts 300 bps; CL8Y USTC paths win when within the fee stack. 9. CMM skim in IBC USDC: bank send succeeds; CMM address can receive IBC. --- ## Test plan (attack, hack, abuse) | Vector | Expect | |--------|--------| | **Denom confusion** (Axelar vs Noble vs fake `ibc/…`) | Only the pinned denom is an intermediate; others ineligible. | | **Symbol spoof** CW20 named USDC | Still needs whitelisted `code_id`; not the IBC intermediate. | | **$1 peg** on depeg | Oracle fail-closed or live CEX; never hardcode `$1`. | | **vFDUSD / UST1 as USDC** | Forbidden (#580 / UST1 unstablecoin). | | **Router holds IBC, attacker donates wrong ibc denom** | `minimum_receive` / delta only on declared denom (parent XDex-5). | | **Burn tax surprise** if denom is not tax-exempt | Quote and execute must use the same tax; fail closed if unknown. | | **IBC timeout / not a swap issue** | Bridging is out of scope; only on-chain DEX hops. | | **Using USDC to skip 3% or whitelist** | 300 bps still applied; CW20 other-leg still F6-gated. | | **Phoenix-1 denom copied onto columbus-5** | Pin from columbus-5 LCD only. | --- ## Verification criteria Close **analysis** when the go/no-go comment has LCD tables and U1–U8 answers. Close **implementation** (if split later) when parent verify target is extended with USDC hop fixtures and oracle fail-closed tests. **Out of scope:** wrapping USDC in ustr-cmm; CL8Y factory USDC pairs; IBC transfer UI; treating all stables as one hub.
PlasticDigits commented 2026-08-27 11:52:45 +00:00 (Migrated from gitlab.com)

marked as related to #690

marked as related to #690
PlasticDigits commented 2026-08-27 11:52:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #690

mentioned in issue #690
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#691
No description provided.