Reflector — live oracle feeds (X1)
Goal: replace the testnet mock feed with a live Reflector
SEP-40 oracle in the oracle-adapter. Everything below was
researched from official sources and adversarially re-verified; each contract
ID was confirmed live via stellar-cli against soroban-testnet.stellar.org.
The vault's NAV now reads a live Reflector feed. The reflector-feed shim
(deployed on testnet) maps our LEOD symbol → Reflector's Asset::Other("USDC")
on the CEX/DEX feed — a stable ~$1 par-NAV proxy, since no RWA feed exists on
Reflector testnet (see below). oracle-adapter.get_nav(LEOD) returns the live
price (≈ $1.0008), self-refreshing every ~5 min, so it never goes stale. This
powers the live performance dashboard.
Version-volatile facts here are dated (2026-07-17) and cited, per the repo's
INTEGRATIONS/ rule. The canonical copy is INTEGRATIONS/reflector.md.
Contract IDs (verified)
Feeds published in Stellar's oracle providers list and confirmed on-chain:
| Feed | Network | Contract ID |
|---|---|---|
| Stellar DEX | testnet | CAVLP5DH2GJPZMVO7IJY4CVOD5MWEFTJFVPD2YY2FQXOQHRGHK4D6HLP |
| External CEX/DEX | testnet | CCYOZJCOPG34LLQQ7N24YXBM7LL62R7ONMZ3G6WZAAYPB5OYKOMJRN63 |
| Fiat / FX | testnet | CCSSOHTBL3LEWUCBBEB5NJFC2OKFRC74OWEIJIZLRJBGAAU4VMU5NV4W |
| Stellar DEX | mainnet | CALI2BYU2JE6WVRUFYTS6MSBNEHGJ35P4AVCZYF3B6QOE3QKOB2PLE6M |
| External CEX/DEX | mainnet | CAFJZQWSED6YAWZU3GWRTOCNPPCGBN32L7QV43XX5LZLFTK6JLN34DLN |
| Fiat / FX | mainnet | CBKGPWGKSKZF52CFHMTRR23TBWTPMRDIYZ4O2P5VS65BMHYH4DXMCJZC |
Interface
Reflector implements SEP-40. The asset selector is an enum:
enum Asset { Stellar(Address), Other(Symbol) } // Stellar assets vs external symbols
struct PriceData { price: i128, timestamp: u64 }
fn lastprice(asset: Asset) -> Option<PriceData>;
fn price(asset: Asset, timestamp: u64) -> Option<PriceData>;
fn decimals() -> u32; // = 14 on all three testnet feeds (live-queried)
fn resolution() -> u32; // = 300 (5-min heartbeat) on all three
fn base() -> Asset; // Other("USD") on CEX/DEX + FX; a Stellar-asset SAC on the DEX feed
- Decimals = 14, resolution = 300 s — confirmed live on all three testnet feeds.
- Pulse feeds are free, 5-minute cadence; history retention 24 h.
Adapter mapping
Leontief's adapter takes asset: Symbol; Reflector takes asset: Asset. The
integration maps our Symbol asset-id to Asset::Other(Symbol) (for symbol-priced
assets) or Asset::Stellar(Address) (for Stellar-asset-priced ones), reads the
i128 price + u64 timestamp, and normalizes from 14 decimals → SCALE (10¹²).
Recalibrate max_age_secs to cadence ×2.5 = 750 s.
This mapping is implemented by the reflector-feed shim contract — it exposes
the adapter's lastprice(Symbol) surface, forwards to the live Reflector
lastprice(Asset), and passes the XDR-identical PriceData through untouched, so
the fail-closed core is unchanged. One command wires it end-to-end:
source deploy.env && ./scripts/wire_reflector.sh.
The RWA-feed gap (the decisive finding)
The testnet CEX/DEX feed carries 16 crypto majors + EURC; the FX feed carries
23 fiat currencies plus XAU (gold). None of USDY / CETES / USTRY (or other
Etherfuse/Ondo RWAs) have a Reflector feed on testnet — nor were any identifiable
on mainnet. The FX feed's TRY is the Turkish Lira, not USTRY.
Consequence (as wired): the demo asset LEOD is mapped to Reflector's
USDC feed — a live, stable ~$1 par-NAV proxy — so the live-oracle path
runs end-to-end on real market data (a par bond NAV is ~$1, so USDC is a faithful
stand-in). USDC (or USDT) is used precisely because it's stable: a volatile crypto
feed would swing the NAV and crater collateral. The mainnet plan is to swap this
proxy for a dedicated RWA price source (issuer NAV feed or a Reflector
subscription feed) before any real asset is wrapped.
Drill
With a live feed configured, the deviation breaker is exercised by tightening
max_dev_bps, observing the PriceDeviation halt, and re-arming via the
prototype accept_override (which emits the loud override_accepted event). This
proves fail-closed behavior against real feed movement.