Skip to content

feat(full-stack): implement live XLM/USD price oracle — multi-source backend cache with staleness guard, REST endpoint, frontend live rate ticker, and XlmAmount component across all escrow UIs #1548

Description

@Chibey-max

Overview

Users currently see escrow amounts only in XLM with no reference to their fiat value. Adding a live XLM/USD price oracle bridges this gap: the backend caches price data with staleness guards, exposes a REST endpoint, and the frontend renders a live rate ticker alongside all XLM amounts in the UI.

What to build

Backend:

  • GET /api/v1/market/xlm-usd — returns { price_usd: number, source: string, timestamp: ISO8601, stale: bool }
  • Price source priority: 1) Stellar Anchor's SEP-38 quote server (if configured), 2) Coingecko free API (/simple/price?ids=stellar&vs_currencies=usd), 3) Binance REST API (/api/v3/ticker/price?symbol=XLMUSDT)
  • Redis cache: key market:xlm_usd, TTL 60s; if fetch fails and cache is older than 5 min, return cached data with stale: true; if cache is absent and all sources fail, return 503
  • Background job (every 60s): proactively refreshes cache so the REST endpoint never waits on an external call; logs source and latency
  • Source failover: if source 1 fails, try source 2; if source 2 fails, try source 3; record which source succeeded in the response

Frontend:

  • useLiveXlmRate() hook: polls GET /api/v1/market/xlm-usd every 60s; returns { rate_usd, stale, loading }
  • XlmAmount component: displays {xlm_amount} XLM with a ≈ $USD sub-label; USD computed client-side from the live rate; grayed-out if stale=true
  • RateTicker widget (header bar): shows 1 XLM = $0.XXX USD with a green/red delta arrow vs the previous rate; clicking links to Stellar Expert market data
  • Replace all bare XLM amount displays across the app with the XlmAmount component
  • Stale rate indicator: if stale=true, show a small ⚠ Rate may be outdated tooltip on the USD sub-label

Acceptance Criteria

  • Backend never makes an external call on the hot path — background job always pre-warms the cache
  • Source failover is tested: mock source 1 failure → source 2 used; mock 1+2 failure → source 3 used
  • 503 only returned when cache is absent AND all three sources fail in sequence
  • Frontend polling uses setInterval with cleanup on unmount; no polling when tab is hidden
  • XlmAmount with amount=0 displays 0 XLM and ≈ $0.00 USD (not NaN or blank)
  • RateTicker delta arrow: green up-arrow if rate increased since last poll, red down-arrow if decreased, no arrow if unchanged
  • All existing XLM amount displays migrated to XlmAmount component (grep for raw {amount} XLM patterns)
  • Tests: source failover logic, cache staleness, XlmAmount USD calculation, RateTicker delta

Technical notes

  • Coingecko free API has a 10–50 req/min rate limit; the 60s background job stays well within this
  • USD conversion: usd_value = xlm_amount * rate_usd; displayed with toLocaleString('en-US', { style: 'currency', currency: 'USD' })

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions