feat: /protocol vFDUSD tab — FDUSD reference price + Venus 1 vFDUSD rate #571
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#571
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
On
/protocol, the vFDUSD oracle tab currently presents the indexer CEX FDUSD/USD snapshot as if it were a vFDUSD price (vFDUSD / USDheading + generic Reference price). Bundle both product fixes into one issue:exchangeRateStored).Do not split “rename the StatBox” vs “Venus exchange-rate poller” vs “docs/P550-9”. Related: #550 (unified oracle card + vFDUSD tab), #515 (ticker-scoped CEX oracle), #506 (UST1 window — different oracle), #489 (short retail copy).
Current codebase
/protocolalready has a single CEX oracle card with USTC / LUNC / vFDUSD tabs (#550). The vFDUSD tab is a label lie: the indexer polls CEX FDUSD, then the UI titles it vFDUSD / USD. There is no Venus redeem rate anywhere on Protocol.ProtocolPage.tsx: Global stats → DEX hub prices → oneProtocolOracleCard→ audit rows → hooks.?ticker=allowlisted (ustc|lunc|vfdusd).ProtocolOracleCard.tsx: heading`${label} / USD`so vFDUSD → vFDUSD / USD. First StatBox label is always Reference price (formatProtocolOracleUsd(oracle.price_usd)). Sources table + recent history stay in the same card.protocolOracleTicker.ts: tab textvfdusd: 'vFDUSD'. Unknown /fdusd/javascript:/../→ustc.indexer/src/indexer/oracle.rsOracleTicker::Vfdusd: MEXCFDUSDUSDT, CoinGeckofirst-digital-usd; KuCoin skipped (unlisted). Path/api/v1/oracle/price/vfdusd. X4: this feed is not used for DEXvolume_usd. Invariant P550-9: labeled vFDUSD / USD, not$1, not the/ust1window.terra1mnl9azefrqpmu888ar2u6zrcwr80hxlt3avf4300r576cw5ar7esvxsvj3, registry name Venus FDUSD (bridged), 6 decimals (tokenRegistry.ts). This is a bridged Venus vToken, not naked FDUSD.0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba. Underlying FDUSD:0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409.exchangeRateStored()is a view (selector0x182df0cd) returning mantissa 1e18;underlying_out = vToken_raw * exchangeRate / 1e18.exchangeRateCurrent()accrues interest (state-changing if sent as a tx) — do not send it from the indexer.ust1Window.tseffective_swap.oracle.rateis UST1 per vFDUSD (1e18 scale,ust1WindowMath.ts) for mint/redeem on/ust1. It is not a Protocol FDUSD redeem quote and includes window fee semantics. Ops already treat Venus/BSC integrity as the ust1-oracle pause trigger (ust1-wrap-production-ops.md).connect-srcis env LCD/RPC/indexer + WC (viteCsp.ts). Browser must not call BSC RPC / BscScan / Venus HTTP.ProtocolPage.test.tsxasserts heading/vFDUSD \/ USD/iandgetOraclePrice('vfdusd').e2e/protocol-page.spec.tsP4 same heading.make verify-issue-550greps those strings.docs/frontend.mdP550-9;skills/AGENTS_FRONTEND_PROTOCOL_STATS.md;docs/runbooks/indexer-external-oracle.md.Why this is needed
exchangeRateStoredanswers the actual question: how much FDUSD does 1 vFDUSD redeem for. Neither number is the UST1 window rate.Constraints / guardrails
ticker === 'vfdusd'. USTC/LUNC keep today’s Reference price label and must not grow a Venus row.$. Tab chip stays vFDUSD. Short “reference / Venus / not settlement” line is OK; no TWAP vs CEX vs window essay.#515/#550feed. Venus rate = BSC vToken redeem. UST1 window =/ust1only. Pair TWAP = Charts. Do not substituteeffective_swap.oracle.rate(UST1 units, window fees) for Venus FDUSD-out. Do not multiply CEX FDUSD/USD × Venus rate and headline it as the CEX “Reference price.”/api/v1/oracle/priceremains a catalog. Nofdusdpath alias (still 400). Do not convert DEXvolume_usdwith FDUSD or Venus. Overviewustc_price_usdstays USTC.eth_calltoexchangeRateStored()on the pinned vFDUSD market. Soft-fail RPC errors (same pattern as CoinGecko 429). Cache + persist; do not live-scan BSC on every Protocol GET. Do not open BSC from the browser (CSP, RPC key leak, abuse). Pin the Core Pool address in code/docs; do not scrape Venus app HTML.decimals()of vToken and underlying. Terra CW20 is 6dp; Venus vFDUSD is typically 8dp — display is 1 human vFDUSD, never raw-unit identity. Non-finite / zero / overflow → em-dash, neverInfinity.exchangeRateStored(view). Do not sendexchangeRateCurrent()as a transaction. If a call-static current rate is used, it must be read-only and documented.parseProtocolOracleTicker. Never interpolate raw query strings into fetch URLs oreth_callpayloads. Venus payload fields are numbers/strings — do notdangerouslySetInnerHTML.—. Market-data banner (#215) may include Venus outage on the vFDUSD tab only if it does not leak RPC URLs./ust1math, or swap settlement.Relevant files
frontend-dapp/src/components/protocol/ProtocolOracleCard.tsxfrontend-dapp/src/components/protocol/useProtocolOracleQueries.tsfrontend-dapp/src/pages/ProtocolPage.tsxfrontend-dapp/src/pages/ProtocolPage.test.tsxfrontend-dapp/e2e/protocol-page.spec.tsfrontend-dapp/src/services/indexer/client.tsfrontend-dapp/src/types/index.tsfrontend-dapp/src/utils/protocolOracleTicker.tsfrontend-dapp/src/utils/formatProtocolStats.tsindexer/src/indexer/oracle.rsvfdusdindexer/src/api/oracle.rsindexer/src/indexer/venus_vfdusd.rs)eth_call+ decimal conversion + cacheindexer/tests/api_oracle.rsdocs/frontend.mddocs/runbooks/indexer-external-oracle.mdskills/AGENTS_FRONTEND_PROTOCOL_STATS.mdskills/AGENTS_INDEXER_EXTERNAL_ORACLE.mdscripts/qa/verify-issue-550.shvFDUSD / USDheading; add this issue’s verify scriptRecommended direction
/ USDfrom the heading if it fights the new FDUSD-out section). First StatBox label FDUSD reference price bound to existinggetOraclePrice('vfdusd'). Below sources (or beside the CEX row), a newdata-testid="protocol-oracle-vfdusd-venus"section titled 1 vFDUSD Price showingformat…(fdusd_per_vfdusd)+FDUSD, with source Venus and fetched time.GET /api/v1/oracle/price/vfdusd(e.g.venus: { fdusd_per_vfdusd, source, fetched_at, vtoken }) so the frontend does not grow a second ticker allowlist. USTC/LUNC responses omitvenusor sendnull. Alternative:GET /api/v1/oracle/price/vfdusd/venus— still allowlisted, still 400 for unknown. Do not add pathfdusd.eth_callexchangeRateStored()(+decimals/underlyingonce, cache). Config: BSC RPC URL(s) with failover; pin vToken0xC4eF4229FEc74Ccfe17B2bdeF7715fAC740BA0ba. Soft-fail; persist last good sample like CEX oracle rows (distinctsource, e.g.venus_bsc). LocalTerra/Postgres tests mock the RPC — do not require live BSC in CI.$1, not UST1 window.” Addmake verify-issue-<iid>(and a short skill if the poller is non-trivial).Acceptance criteria
/protocol?ticker=vfdusd(or vFDUSD tab): CEX StatBox label is FDUSD reference price. Value is still indexer CEX FDUSD/USD (not hardcoded$1, not Venus).protocol-oracle-vfdusd-venus) shows human FDUSD per 1 vFDUSD from VenusexchangeRateStored, with Venus source + time (or—+ retry when missing).?ticker=allowlist unchanged (fdusd/ XSS /../→ustc). No BSC hosts in production CSPconnect-src.volume_usdconversion via FDUSD/Venus; nofdusdalias.make verify-issue-<iid>updated.#550verify no longer asserts headingvFDUSD / USDif that string is removed.#489: labels ≤ ~5 words; no architecture lecture on the card.Test plan (all paths)
/protocolgetOraclePrice('ustc')onlyvfdusd; Venus payload used/protocol?ticker=vfdusd?ticker=fdusd,btc,javascript:…,../vfdusdgetOraclePrice('vfdusd')—; CEX can still resolve—; CEX intactexchangeRateStored+ decimalsGET /api/v1/oracle/price{ tickers: [ustc,lunc,vfdusd] }; not a numeric body/price/fdusdmake setup-indexer-postgres; mock BSCe2e/protocol-page.spec.ts+ 5 workers#550regressionmake verify-issue-550Test plan (attack, hack, abuse)
?ticker=../../oracle/price/vfdusdustc; client never interpolates raw ticker into URL?ticker=<img src=x onerror=alert(1)>ustc; no HTML in DOM from tickersource/vtokenwith<script>javascript:alert(1)ustc; no navigationeth_call/ BscScan / publicnode BSC from the originVITE_*eth_callamplification$1peg/ust1quote vs Protocol Venusoracle.rateas FDUSD-out0.87, Venus0.023exchangeRateCurrenttxeth_callticker=vfdusd'; DROPvolume_usdingesthttps:blanket; no BSC host added “for convenience”Verification criteria
Ship is done when AC1–AC8 pass, T1–T20 and A1–A15 are recorded (or waived with reason),
make verify-issue-<iid>is green on Cloud Agent (make setup-indexer-postgres; live BSC not required), and production CSP still does not call BSC from the browser.marked as related to #550
marked as related to #515
marked as related to #506
mentioned in issue #574
mentioned in issue #575
mentioned in issue #580
marked as related to #580
mentioned in issue #572
mentioned in merge request !1103
mentioned in merge request !1104
mentioned in merge request !1105
mentioned in commit
9c802c1aacMerged via !1105.
make verify-issue-571passed (docs, Venus lib +api_oracle, Protocol RTL, Playwright protocol-page 5 workers). Remaining production:BSC_RPC_URLS(neverVITE_*); confirm Protocol vFDUSD tab shows CEX FDUSD reference and Venus 1 vFDUSD PriceexchangeRateCurrenttransaction and no/price/fdusdaliasmentioned in issue #570
mentioned in issue #583
marked as related to #583
mentioned in issue #588
mentioned in issue #509
mentioned in issue #511
mentioned in issue #614
mentioned in issue #653