bug: indexer oracle reports CEX FDUSD as vFDUSD/USD in logs and API #580

Closed
opened 2026-08-19 12:11:26 +00:00 by PlasticDigits · 20 comments
PlasticDigits commented 2026-08-19 12:11:26 +00:00 (Migrated from gitlab.com)

Summary

Indexer logs and GET /api/v1/oracle/price/vfdusd present CEX FDUSD/USD (~$0.998, typically within 1% of $1) as vFDUSD/USD. On-chain vFDUSD is a distinct asset (Venus FDUSD bridged CW20), not First Digital USD. This issue is the indexer identity / operator-API slice. Protocol tab copy + Venus redeem rate is already specified in #571 / #572 — do not re-implement that product surface here.

Observed indexer lines (columbus-5). Same ticks also show CoinGecko HTTP 403 “add a descriptive User-Agent” for lunc / vfdusd — companion #579.

INFO Oracle: vFDUSD/USD avg $0.99800000 from 1/2 sources
INFO Oracle: vFDUSD/USD avg $0.99800000 from 1/1 sources

Related: #515 (ticker-scoped CEX oracle), #550 (added vfdusd tab and P550-9), #522 / #556 (P522-Q / hub USD — must not start pricing vFDUSD from this CEX print).

Current codebase

#550 / P550-9 intentionally wired the third external ticker as “wrapped FDUSD CEX reference”: poll MEXC FDUSDUSDT + CoinGecko first-digital-usd, skip KuCoin, expose path vfdusd, and label the print vFDUSD/USD. Tests and docs lock that mapping in as correct. Tokenlist identity disagrees.

Layer Behavior today
Poll symbols indexer/src/indexer/oracle.rs OracleTicker::Vfdusd: mexc_symbol() = "FDUSDUSDT", coingecko_id() = "first-digital-usd", kucoin_symbol() = None.
Log / display name display_name() returns vFDUSD/USD. poll_ticker logs Oracle: {} avg ${:.8} from {}/{} sources with that string. Operators therefore see FDUSD ~$0.998 as vFDUSD.
API catalog indexer/src/api/oracle.rs ORACLE_CATALOG_METADATA: vfdusd = wrapped FDUSD CEX/USD reference (polls FDUSD, not a $1 peg). GET /api/v1/oracle/price/vfdusd JSON ticker is "vfdusd"; price_usd is the CEX FDUSD average. Path fdusd is 400 (no alias).
DB oracle_prices.ticker = 'vfdusd' stores CEX FDUSD samples + average.
Token identity tokenlist/tokenlist.json: symbol vFDUSD, name Venus FDUSD (bridged), 6 decimals, terra1mnl9azefrqpmu888ar2u6zrcwr80hxlt3avf4300r576cw5ar7esvxsvj3. Not CEX FDUSD.
Protocol UI ProtocolOracleCard.tsx heading `${label} / USD` → vFDUSD / USD. Product fix (FDUSD reference + Venus 1 vFDUSD rate) is #571 / #572, not this issue.
P522-Q / hub pair_price_usd.rs quote_usd_kind has UST1 / USTC / LUNC / USTR only. vFDUSD is unknown → price_usd / volume_usd NULL for vFDUSD-quoted swaps. X4 / P550-10 forbid converting DEX volume with vFDUSD/FDUSD. Hub tickers are custc | ust1 | ustr only.
UST1 window ust1Window.ts uses on-chain window effective_swap — not this CEX feed.
Tests that encode the lie vfdusd_polls_fdusd_not_ustc_or_lunc_and_not_hardcoded_peg asserts MEXC/CG FDUSD ids on OracleTicker::Vfdusd. fetch_coingecko_parses_vfdusd_id parses first-digital-usd. api_oracle.rs + Protocol RTL/e2e assert ticker vfdusd and heading vFDUSD / USD. make verify-issue-515 / verify-issue-550 document P550-9.
Docs / skills docs/runbooks/indexer-external-oracle.md, docs/indexer-invariants.md X2/X3, skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md, docs/frontend.md P550-9.

Downstream today: Protocol retail copy (misleading), integrator JSON (ticker: "vfdusd" + ~$1), operator logs. Charts/tape USD for vFDUSD quotes is missing, not silently $1 — unless a later change wires this ticker into P522-Q.

Why this is needed

  1. vFDUSD ≠ FDUSD. CEX FDUSD is a dollar-ish stablecoin. Bridged Venus vFDUSD is a vToken whose redeem value vs FDUSD is Venus exchangeRateStored, not 1:1, and is not “usually within 1% of $1”. Logging $0.998 as vFDUSD/USD is the same class of mix-up #515 fixed for USTC vs LUNC.
  2. Operators debug from logs. display_name() is the only signal in indexer::oracle info lines. A 1-source MEXC FDUSD print looks like a healthy vFDUSD oracle.
  3. API ticker field is an identity. Integrators that treat /price/vfdusd as USD of 1 human CW20 vFDUSD will be off by the Venus exchange-rate factor (and any Terra vs BSC decimal mismatch). Catalog metadata currently teaches that lie.
  4. Do not “fix” NULLs by trusting this print. If someone later adds vFDUSD to P522-Q / hub USD using OracleTicker::Vfdusd, Charts Price (USD), overview volume_usd, trader volume, and portfolio USD would systematically mis-mark vFDUSD legs. This issue must close that footgun in code comments, skills, and tests.

Constraints / guardrails

  1. Scope. This issue: indexer log strings, catalog metadata, optional additive JSON honesty (quote_asset / display_name), tests/docs that claim CEX FDUSD is vFDUSD/USD. Out of scope: Venus BSC poller, Protocol 1 vFDUSD Price section, CSP — those stay on #571 / #572.
  2. Keep path vfdusd unless a breaking-change ADR says otherwise. #571 keeps /api/v1/oracle/price/vfdusd for the CEX FDUSD snapshot (no silent /price/fdusd alias — still 400). Identity fix is labels and metadata, not a surprise ticker rename that breaks getOraclePrice('vfdusd').
  3. X1–X6 stay. Bare /price and /history remain catalogs. Unknown ticker → 400. Distinct CEX ids (USTC ≠ LUNC ≠ FDUSD). X4: do not convert volume_usd with FDUSD or vFDUSD. Overview ustc_price_usd stays USTC. Advisory only (X5). Non-finite f64 still safe-default (X6).
  4. Do not hardcode $1 for FDUSD or vFDUSD. Do not invent Venus FDUSD-out from the CEX print. Do not paste UST1 window oracle.rate into this feed.
  5. Do not add vFDUSD to P522-Q / hub as part of this issue. Unknown quote → NULL remains correct until a separate hub/Venus USD design exists. Never use CEX FDUSD as usd_per_human for symbol VFDUSD.
  6. Allowlist / injection. OracleTicker::parse stays ASCII allowlist. Homoglyphs, ../, javascript:, fdusd → None / 400. Parameterized SQL. No interpolating raw path segments into CEX URLs beyond the enum’s fixed symbols.
  7. Docs/skills. Rewrite P550-9 / X3 examples so CEX print is FDUSD reference stored under ticker vfdusd. Coordinate wording with #571 so verify-issue-550 heading asserts do not fight this change.
  8. Do not change USTC/LUNC symbols, hub USD, /ust1 math, swap settlement, or factory/router audit rows.

Relevant files

File Role
indexer/src/indexer/oracle.rs display_name(), comments, tests that assert FDUSD ids and the vFDUSD/USD log label
indexer/src/api/oracle.rs ORACLE_CATALOG_METADATA; optional quote_asset / display_name on snapshot
indexer/src/db/queries/oracle.rs Ticker column stays vfdusd unless ADR
indexer/tests/api_oracle.rs Catalog text + /price/fdusd 400 + snapshot ticker
docs/runbooks/indexer-external-oracle.md Ticker table: CEX FDUSD, not “vFDUSD/USD”
docs/indexer-invariants.md External oracle row
skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md Do/don’t: logs and JSON must not call CEX FDUSD “vFDUSD/USD”
skills/AGENTS_INDEXER_PAIR_PRICE_USD.md Guardrail: do not catalog vFDUSD off this feed
docs/frontend.md / skills/AGENTS_FRONTEND_PROTOCOL_STATS.md P550-9 wording (coordinate with #571)
scripts/qa/verify-issue-515.sh Regression
new scripts/qa/verify-issue-<iid>.sh This issue’s verify
  1. Logs: Change OracleTicker::Vfdusd.display_name() to FDUSD/USD (or FDUSD/USD (CEX)). Info lines must not say vFDUSD/USD avg $0.998. Keep as_str() = "vfdusd" for DB/path.
  2. Catalog metadata: State explicitly: ticker vfdusd returns CEX FDUSD/USD (MEXC FDUSDUSDT, CoinGecko first-digital-usd); it is not USD of Terra CW20 vFDUSD. Point Protocol Venus UI to #571.
  3. Optional additive JSON on /price/vfdusd: e.g. quote_asset: "FDUSD", display_name: "FDUSD/USD" so clients do not infer identity from the path alone. USTC/LUNC get matching fields. Do not add path fdusd.
  4. Tests: Split “polls FDUSD CEX ids” (keep) from “display_name is vFDUSD/USD” (fail). Assert display_name contains FDUSD and does not equal vFDUSD/USD. Catalog metadata must not claim the print is vFDUSD/USD.
  5. Skills: Add don’t: never use OracleTicker::Vfdusd as usd_per_human for VFDUSD in quote_usd_kind. Keep X4.
  6. make verify-issue-<iid> greps display_name / catalog / skill lines. Do not require live CEX.

Acceptance criteria

  • AC1. Indexer info log for this ticker is FDUSD/USD (CEX), never vFDUSD/USD avg $…. Path/DB ticker remains vfdusd unless an approved breaking change.
  • AC2. GET /api/v1/oracle/price metadata states the vfdusd snapshot is CEX FDUSD, not Terra vFDUSD USD. /price/fdusd remains 400.
  • AC3. Unit tests still prove MEXC/CG symbols are FDUSD ids (not USTC/LUNC) and not a $1 hardcode; they must not require display_name() == "vFDUSD/USD".
  • AC4. P522-Q / volume_usd / hub USD unchanged: vFDUSD quotes stay unpriced (NULL), not CEX FDUSD × size.
  • AC5. Docs/skills (P550-9, X3, external-oracle runbook) match AC1–AC2. Coordinate with #571 so Protocol verify scripts do not require the old heading if that issue removed it.
  • AC6. make verify-issue-<iid> plus make verify-issue-515 (and #550 if still applicable) pass without live CEX.

Test plan (all paths)

# Path Steps Expected
T1 Unit display_name OracleTicker::Vfdusd.display_name() Contains FDUSD; ≠ vFDUSD/USD
T2 Unit symbols Existing distinct-id tests MEXC FDUSDUSDT, CG first-digital-usd, KuCoin skipped
T3 Unit parse parse("vfdusd") / parse("fdusd") / ../ / javascript: Some(Vfdusd) / None / None / None
T4 Catalog HTTP GET /api/v1/oracle/price Catalog only; metadata mentions CEX FDUSD; tickers still include vfdusd
T5 Snapshot HTTP GET /api/v1/oracle/price/vfdusd ticker = vfdusd; price_usd is CEX cache (or null); optional quote_asset = FDUSD
T6 Unknown GET /price/fdusd, /price/btc 400
T7 History GET /history/vfdusd Same ticker identity; samples are CEX averages
T8 USTC/LUNC logs Poll those tickers Still USTC/USD / LUNC/USD; no FDUSD copy
T9 Soft-fail CG CG 403/429 MEXC-only average still logs FDUSD/USD, not vFDUSD
T10 $1 hardcode Depeg fixture 0.87 Stored/served 0.87, not 1.0
T11 P522-Q quote_usd_kind("VFDUSD", None) / volume helper None; no FDUSD oracle multiply
T12 Hub parse HubTicker::parse("vfdusd") / "fdusd" None
T13 Docs grep verify script Runbook/skill do not say the CEX print is vFDUSD/USD
T14 #515 / #550 regression make verify-issue-515 (and 550 after copy sync) Pass
T15 LocalTerra/Postgres make setup-indexer-postgres; no live MEXC required Lib + api_oracle tests pass

Test plan (attack, hack, abuse)

# Vector Steps Expected
A1 Path injection /price/../../vfdusd, /price/%00vfdusd 400 or allowlist miss; no file read
A2 XSS ticker /price/<script>, query javascript: 400; JSON error is plain text
A3 Silent alias Client calls /price/fdusd hoping for vFDUSD 400 — no alias that hides the identity bug
A4 SQL ticker Insert vfdusd'; DROP Parameterized; enum only
A5 Cross-wire USD Mock CEX 0.998; assert volume_usd_for_swap on a VFDUSD quote NULL, not 0.998 × amount
A6 Fake peg All sources down Last-known retained or null; never invent $1 vFDUSD
A7 Integrator confusion Snapshot JSON Must be possible to tell the number is FDUSD (metadata and/or quote_asset); must not only say ticker: vfdusd
A8 Window oracle mix-up Compare /ust1 rate vs this snapshot Different subsystems; this change must not paste window rate into CEX cache
A9 Homoglyph ticker vfdusd lookalikes parse → None
A10 Log injection Malicious CEX body Price parse fail; log must not treat CEX payload as the display_name

Verification criteria

make verify-issue-<iid>
# expected:
# - display_name / catalog metadata: CEX FDUSD, not vFDUSD/USD
# - parse('fdusd') still None; P522-Q still ignores VFDUSD
# - cargo test --lib oracle + cargo test --test api_oracle
make verify-issue-515
# coordinate: make verify-issue-550 after P550-9 copy sync with #571

Ship is done when AC1–AC6 pass, T1–T15 and A1–A10 are recorded (or waived with reason), and no code path multiplies CEX FDUSD into vFDUSD volume_usd / price_usd.

## Summary Indexer logs and `GET /api/v1/oracle/price/vfdusd` present **CEX FDUSD/USD** (~$0.998, typically within 1% of $1) as **vFDUSD/USD**. On-chain **vFDUSD** is a distinct asset (Venus FDUSD bridged CW20), not First Digital USD. This issue is the **indexer identity / operator-API** slice. Protocol tab copy + Venus redeem rate is already specified in [#571](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/571) / [#572](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/572) — do not re-implement that product surface here. Observed indexer lines (columbus-5). Same ticks also show CoinGecko HTTP 403 “add a descriptive User-Agent” for `lunc` / `vfdusd` — companion [#579](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/579). ``` INFO Oracle: vFDUSD/USD avg $0.99800000 from 1/2 sources INFO Oracle: vFDUSD/USD avg $0.99800000 from 1/1 sources ``` Related: [#515](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/515) (ticker-scoped CEX oracle), [#550](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/550) (added `vfdusd` tab and **P550-9**), [#522](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/522) / [#556](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/556) (P522-Q / hub USD — must **not** start pricing vFDUSD from this CEX print). ## Current codebase `#550` / **P550-9** intentionally wired the third external ticker as “wrapped FDUSD CEX reference”: poll MEXC `FDUSDUSDT` + CoinGecko `first-digital-usd`, skip KuCoin, expose path `vfdusd`, and **label** the print **vFDUSD/USD**. Tests and docs lock that mapping in as correct. Tokenlist identity disagrees. | Layer | Behavior today | |-------|----------------| | **Poll symbols** | [`indexer/src/indexer/oracle.rs`](indexer/src/indexer/oracle.rs) `OracleTicker::Vfdusd`: `mexc_symbol() = "FDUSDUSDT"`, `coingecko_id() = "first-digital-usd"`, `kucoin_symbol() = None`. | | **Log / display name** | `display_name()` returns **`vFDUSD/USD`**. `poll_ticker` logs `Oracle: {} avg ${:.8} from {}/{} sources` with that string. Operators therefore see FDUSD ~$0.998 as vFDUSD. | | **API catalog** | [`indexer/src/api/oracle.rs`](indexer/src/api/oracle.rs) `ORACLE_CATALOG_METADATA`: `vfdusd = wrapped FDUSD CEX/USD reference (polls FDUSD, not a $1 peg)`. `GET /api/v1/oracle/price/vfdusd` JSON `ticker` is `"vfdusd"`; `price_usd` is the CEX FDUSD average. Path `fdusd` is **400** (no alias). | | **DB** | `oracle_prices.ticker = 'vfdusd'` stores CEX FDUSD samples + `average`. | | **Token identity** | [`tokenlist/tokenlist.json`](tokenlist/tokenlist.json): symbol `vFDUSD`, name **Venus FDUSD (bridged)**, 6 decimals, `terra1mnl9azefrqpmu888ar2u6zrcwr80hxlt3avf4300r576cw5ar7esvxsvj3`. Not CEX FDUSD. | | **Protocol UI** | [`ProtocolOracleCard.tsx`](frontend-dapp/src/components/protocol/ProtocolOracleCard.tsx) heading `` `${label} / USD` `` → **vFDUSD / USD**. Product fix (FDUSD reference + Venus 1 vFDUSD rate) is **#571 / #572**, not this issue. | | **P522-Q / hub** | [`pair_price_usd.rs`](indexer/src/indexer/pair_price_usd.rs) `quote_usd_kind` has UST1 / USTC / LUNC / USTR only. **vFDUSD is unknown** → `price_usd` / `volume_usd` NULL for vFDUSD-quoted swaps. **X4** / **P550-10** forbid converting DEX volume with vFDUSD/FDUSD. Hub tickers are `custc` \| `ust1` \| `ustr` only. | | **UST1 window** | [`ust1Window.ts`](frontend-dapp/src/services/terraclassic/ust1Window.ts) uses on-chain window `effective_swap` — **not** this CEX feed. | | **Tests that encode the lie** | `vfdusd_polls_fdusd_not_ustc_or_lunc_and_not_hardcoded_peg` asserts MEXC/CG FDUSD ids on `OracleTicker::Vfdusd`. `fetch_coingecko_parses_vfdusd_id` parses `first-digital-usd`. `api_oracle.rs` + Protocol RTL/e2e assert ticker `vfdusd` and heading `vFDUSD / USD`. `make verify-issue-515` / `verify-issue-550` document **P550-9**. | | **Docs / skills** | [`docs/runbooks/indexer-external-oracle.md`](docs/runbooks/indexer-external-oracle.md), [`docs/indexer-invariants.md`](docs/indexer-invariants.md) **X2/X3**, [`skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md`](skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md), [`docs/frontend.md`](docs/frontend.md) **P550-9**. | Downstream today: Protocol retail copy (misleading), integrator JSON (`ticker: "vfdusd"` + ~$1), operator logs. Charts/tape USD for vFDUSD quotes is **missing**, not silently `$1` — unless a later change wires this ticker into P522-Q. ## Why this is needed 1. **vFDUSD ≠ FDUSD.** CEX FDUSD is a dollar-ish stablecoin. Bridged Venus **vFDUSD** is a vToken whose redeem value vs FDUSD is Venus `exchangeRateStored`, not 1:1, and is **not** “usually within 1% of $1”. Logging `$0.998` as **vFDUSD/USD** is the same class of mix-up [#515](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/515) fixed for USTC vs LUNC. 2. **Operators debug from logs.** `display_name()` is the only signal in `indexer::oracle` info lines. A 1-source MEXC FDUSD print looks like a healthy vFDUSD oracle. 3. **API `ticker` field is an identity.** Integrators that treat `/price/vfdusd` as USD of 1 human CW20 vFDUSD will be off by the Venus exchange-rate factor (and any Terra vs BSC decimal mismatch). Catalog metadata currently teaches that lie. 4. **Do not “fix” NULLs by trusting this print.** If someone later adds vFDUSD to P522-Q / hub USD using `OracleTicker::Vfdusd`, Charts Price (USD), overview `volume_usd`, trader volume, and portfolio USD would systematically mis-mark vFDUSD legs. This issue must close that footgun in code comments, skills, and tests. ## Constraints / guardrails 1. **Scope.** This issue: indexer **log strings**, catalog **metadata**, optional additive JSON honesty (`quote_asset` / `display_name`), tests/docs that claim CEX FDUSD **is** vFDUSD/USD. **Out of scope:** Venus BSC poller, Protocol **1 vFDUSD Price** section, CSP — those stay on **#571 / #572**. 2. **Keep path `vfdusd` unless a breaking-change ADR says otherwise.** `#571` keeps `/api/v1/oracle/price/vfdusd` for the CEX FDUSD snapshot (no silent `/price/fdusd` alias — still **400**). Identity fix is **labels and metadata**, not a surprise ticker rename that breaks `getOraclePrice('vfdusd')`. 3. **X1–X6 stay.** Bare `/price` and `/history` remain catalogs. Unknown ticker → 400. Distinct CEX ids (USTC ≠ LUNC ≠ FDUSD). **X4:** do **not** convert `volume_usd` with FDUSD or vFDUSD. Overview `ustc_price_usd` stays USTC. Advisory only (**X5**). Non-finite f64 still safe-default (**X6**). 4. **Do not hardcode $1** for FDUSD or vFDUSD. Do not invent Venus FDUSD-out from the CEX print. Do not paste UST1 window `oracle.rate` into this feed. 5. **Do not add vFDUSD to P522-Q / hub** as part of this issue. Unknown quote → NULL remains correct until a **separate** hub/Venus USD design exists. Never use CEX FDUSD as `usd_per_human` for symbol `VFDUSD`. 6. **Allowlist / injection.** `OracleTicker::parse` stays ASCII allowlist. Homoglyphs, `../`, `javascript:`, `fdusd` → `None` / 400. Parameterized SQL. No interpolating raw path segments into CEX URLs beyond the enum’s fixed symbols. 7. **Docs/skills.** Rewrite **P550-9** / X3 examples so CEX print is **FDUSD reference** stored under ticker `vfdusd`. Coordinate wording with #571 so verify-issue-550 heading asserts do not fight this change. 8. **Do not change** USTC/LUNC symbols, hub USD, `/ust1` math, swap settlement, or factory/router audit rows. ## Relevant files | File | Role | |------|------| | [`indexer/src/indexer/oracle.rs`](indexer/src/indexer/oracle.rs) | `display_name()`, comments, tests that assert FDUSD ids **and** the vFDUSD/USD log label | | [`indexer/src/api/oracle.rs`](indexer/src/api/oracle.rs) | `ORACLE_CATALOG_METADATA`; optional `quote_asset` / `display_name` on snapshot | | [`indexer/src/db/queries/oracle.rs`](indexer/src/db/queries/oracle.rs) | Ticker column stays `vfdusd` unless ADR | | [`indexer/tests/api_oracle.rs`](indexer/tests/api_oracle.rs) | Catalog text + `/price/fdusd` 400 + snapshot ticker | | [`docs/runbooks/indexer-external-oracle.md`](docs/runbooks/indexer-external-oracle.md) | Ticker table: CEX FDUSD, not “vFDUSD/USD” | | [`docs/indexer-invariants.md`](docs/indexer-invariants.md) | External oracle row | | [`skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md`](skills/AGENTS_INDEXER_EXTERNAL_ORACLE.md) | Do/don’t: logs and JSON must not call CEX FDUSD “vFDUSD/USD” | | [`skills/AGENTS_INDEXER_PAIR_PRICE_USD.md`](skills/AGENTS_INDEXER_PAIR_PRICE_USD.md) | Guardrail: do not catalog vFDUSD off this feed | | [`docs/frontend.md`](docs/frontend.md) / [`skills/AGENTS_FRONTEND_PROTOCOL_STATS.md`](skills/AGENTS_FRONTEND_PROTOCOL_STATS.md) | **P550-9** wording (coordinate with #571) | | [`scripts/qa/verify-issue-515.sh`](scripts/qa/verify-issue-515.sh) | Regression | | new `scripts/qa/verify-issue-<iid>.sh` | This issue’s verify | ## Recommended direction 1. **Logs:** Change `OracleTicker::Vfdusd.display_name()` to **`FDUSD/USD`** (or `FDUSD/USD (CEX)`). Info lines must not say `vFDUSD/USD avg $0.998`. Keep `as_str() = "vfdusd"` for DB/path. 2. **Catalog metadata:** State explicitly: ticker `vfdusd` returns **CEX FDUSD/USD** (MEXC `FDUSDUSDT`, CoinGecko `first-digital-usd`); it is **not** USD of Terra CW20 vFDUSD. Point Protocol Venus UI to #571. 3. **Optional additive JSON** on `/price/vfdusd`: e.g. `quote_asset: "FDUSD"`, `display_name: "FDUSD/USD"` so clients do not infer identity from the path alone. USTC/LUNC get matching fields. Do not add path `fdusd`. 4. **Tests:** Split “polls FDUSD CEX ids” (keep) from “display_name is vFDUSD/USD” (fail). Assert `display_name` contains `FDUSD` and does **not** equal `vFDUSD/USD`. Catalog metadata must not claim the print is vFDUSD/USD. 5. **Skills:** Add don’t: never use `OracleTicker::Vfdusd` as `usd_per_human` for `VFDUSD` in `quote_usd_kind`. Keep X4. 6. **`make verify-issue-<iid>`** greps display_name / catalog / skill lines. Do not require live CEX. ## Acceptance criteria - [ ] **AC1.** Indexer info log for this ticker is **FDUSD/USD** (CEX), never `vFDUSD/USD avg $…`. Path/DB ticker remains `vfdusd` unless an approved breaking change. - [ ] **AC2.** `GET /api/v1/oracle/price` metadata states the `vfdusd` snapshot is CEX **FDUSD**, not Terra vFDUSD USD. `/price/fdusd` remains **400**. - [ ] **AC3.** Unit tests still prove MEXC/CG symbols are FDUSD ids (not USTC/LUNC) and **not** a `$1` hardcode; they must **not** require `display_name() == "vFDUSD/USD"`. - [ ] **AC4.** P522-Q / `volume_usd` / hub USD **unchanged**: vFDUSD quotes stay unpriced (NULL), not CEX FDUSD × size. - [ ] **AC5.** Docs/skills (**P550-9**, X3, external-oracle runbook) match AC1–AC2. Coordinate with #571 so Protocol verify scripts do not require the old heading if that issue removed it. - [ ] **AC6.** `make verify-issue-<iid>` plus `make verify-issue-515` (and #550 if still applicable) pass without live CEX. ## Test plan (all paths) | # | Path | Steps | Expected | |---|------|-------|----------| | T1 | Unit display_name | `OracleTicker::Vfdusd.display_name()` | Contains FDUSD; ≠ `vFDUSD/USD` | | T2 | Unit symbols | Existing distinct-id tests | MEXC `FDUSDUSDT`, CG `first-digital-usd`, KuCoin skipped | | T3 | Unit parse | `parse("vfdusd")` / `parse("fdusd")` / `../` / `javascript:` | `Some(Vfdusd)` / `None` / `None` / `None` | | T4 | Catalog HTTP | `GET /api/v1/oracle/price` | Catalog only; metadata mentions CEX FDUSD; tickers still include `vfdusd` | | T5 | Snapshot HTTP | `GET /api/v1/oracle/price/vfdusd` | `ticker` = `vfdusd`; `price_usd` is CEX cache (or null); optional `quote_asset` = FDUSD | | T6 | Unknown | `GET /price/fdusd`, `/price/btc` | **400** | | T7 | History | `GET /history/vfdusd` | Same ticker identity; samples are CEX averages | | T8 | USTC/LUNC logs | Poll those tickers | Still `USTC/USD` / `LUNC/USD`; no FDUSD copy | | T9 | Soft-fail CG | CG 403/429 | MEXC-only average still logs **FDUSD/USD**, not vFDUSD | | T10 | `$1` hardcode | Depeg fixture `0.87` | Stored/served 0.87, not 1.0 | | T11 | P522-Q | `quote_usd_kind("VFDUSD", None)` / volume helper | `None`; no FDUSD oracle multiply | | T12 | Hub parse | `HubTicker::parse("vfdusd")` / `"fdusd"` | `None` | | T13 | Docs grep | verify script | Runbook/skill do not say the CEX print **is** vFDUSD/USD | | T14 | #515 / #550 regression | `make verify-issue-515` (and 550 after copy sync) | Pass | | T15 | LocalTerra/Postgres | `make setup-indexer-postgres`; no live MEXC required | Lib + `api_oracle` tests pass | ## Test plan (attack, hack, abuse) | # | Vector | Steps | Expected | |---|--------|-------|----------| | A1 | Path injection | `/price/../../vfdusd`, `/price/%00vfdusd` | 400 or allowlist miss; no file read | | A2 | XSS ticker | `/price/<script>`, query `javascript:` | 400; JSON error is plain text | | A3 | Silent alias | Client calls `/price/fdusd` hoping for vFDUSD | **400** — no alias that hides the identity bug | | A4 | SQL ticker | Insert `vfdusd'; DROP` | Parameterized; enum only | | A5 | Cross-wire USD | Mock CEX 0.998; assert `volume_usd_for_swap` on a VFDUSD quote | NULL, not 0.998 × amount | | A6 | Fake peg | All sources down | Last-known retained or null; never invent `$1` vFDUSD | | A7 | Integrator confusion | Snapshot JSON | Must be possible to tell the number is FDUSD (metadata and/or `quote_asset`); must not only say `ticker: vfdusd` | | A8 | Window oracle mix-up | Compare `/ust1` rate vs this snapshot | Different subsystems; this change must not paste window rate into CEX cache | | A9 | Homoglyph ticker | `vfdusd` lookalikes | `parse` → None | | A10 | Log injection | Malicious CEX body | Price parse fail; log must not treat CEX payload as the display_name | ## Verification criteria ```bash make verify-issue-<iid> # expected: # - display_name / catalog metadata: CEX FDUSD, not vFDUSD/USD # - parse('fdusd') still None; P522-Q still ignores VFDUSD # - cargo test --lib oracle + cargo test --test api_oracle make verify-issue-515 # coordinate: make verify-issue-550 after P550-9 copy sync with #571 ``` Ship is done when AC1–AC6 pass, T1–T15 and A1–A10 are recorded (or waived with reason), and no code path multiplies CEX FDUSD into vFDUSD `volume_usd` / `price_usd`.
PlasticDigits commented 2026-08-19 12:11:28 +00:00 (Migrated from gitlab.com)

marked as related to #571

marked as related to #571
PlasticDigits commented 2026-08-19 12:11:29 +00:00 (Migrated from gitlab.com)

marked as related to #572

marked as related to #572
PlasticDigits commented 2026-08-19 12:11:29 +00:00 (Migrated from gitlab.com)

marked as related to #579

marked as related to #579
PlasticDigits commented 2026-08-19 12:11:30 +00:00 (Migrated from gitlab.com)

marked as related to #515

marked as related to #515
PlasticDigits commented 2026-08-19 12:11:31 +00:00 (Migrated from gitlab.com)

marked as related to #550

marked as related to #550
PlasticDigits commented 2026-08-19 12:11:32 +00:00 (Migrated from gitlab.com)

marked as related to #522

marked as related to #522
PlasticDigits commented 2026-08-19 12:11:33 +00:00 (Migrated from gitlab.com)

marked as related to #556

marked as related to #556
PlasticDigits commented 2026-08-20 02:16:46 +00:00 (Migrated from gitlab.com)

mentioned in commit 54fb99372c

mentioned in commit 54fb99372cac734056eae222ae1940c9ff4154e5
PlasticDigits commented 2026-08-20 02:19:42 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1103

mentioned in merge request !1103
PlasticDigits commented 2026-08-20 02:21:34 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1104

mentioned in merge request !1104
PlasticDigits commented 2026-08-20 03:33:11 +00:00 (Migrated from gitlab.com)

mentioned in commit b179d1c99b

mentioned in commit b179d1c99b12dbaab8eaa73fffdabd85cb75f2f0
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-20 03:33:11 +00:00
PlasticDigits commented 2026-08-20 03:54:26 +00:00 (Migrated from gitlab.com)

Merged via !1104 (before !1105).

make verify-issue-580 passed. Indexer path vfdusd logs/JSON FDUSD/USD / quote_asset=FDUSD; /price/fdusd stays 400. Protocol UI relabel (FDUSD reference + Venus) is #571 (!1105), not this issue.

Merged via !1104 (before !1105). `make verify-issue-580` passed. Indexer path `vfdusd` logs/JSON **FDUSD/USD** / `quote_asset=FDUSD`; `/price/fdusd` stays 400. Protocol UI relabel (FDUSD reference + Venus) is #571 (!1105), not this issue.
PlasticDigits commented 2026-08-20 03:54:51 +00:00 (Migrated from gitlab.com)

mentioned in issue #583

mentioned in issue #583
PlasticDigits commented 2026-08-20 03:55:05 +00:00 (Migrated from gitlab.com)

marked as related to #583

marked as related to #583
PlasticDigits commented 2026-08-22 03:10:05 +00:00 (Migrated from gitlab.com)

mentioned in issue #589

mentioned in issue #589
PlasticDigits commented 2026-08-24 00:30:20 +00:00 (Migrated from gitlab.com)

mentioned in issue #614

mentioned in issue #614
PlasticDigits commented 2026-08-26 01:11:06 +00:00 (Migrated from gitlab.com)

mentioned in issue #653

mentioned in issue #653
PlasticDigits commented 2026-08-27 01:00:19 +00:00 (Migrated from gitlab.com)

mentioned in issue #682

mentioned in issue #682
PlasticDigits commented 2026-08-27 01:00:29 +00:00 (Migrated from gitlab.com)

mentioned in issue #683

mentioned in issue #683
PlasticDigits commented 2026-08-27 11:52:44 +00:00 (Migrated from gitlab.com)

mentioned in issue #691

mentioned in issue #691
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#580
No description provided.