Skip to content

feat(backend): integrate exchange rate oracle for real-time fiat value conversion #211

@0xDeon

Description

@0xDeon

Problem

Every page in the dApp that shows vault balances, deposit amounts, or settlement previews needs to display a fiat-equivalent value (USD, NGN, GHS, KES). Right now none of this is wired — the frontend either shows raw USDC amounts with no conversion or hardcodes a mock exchange rate. This becomes especially critical for the offramp flow where the user needs to see exactly how much local currency they will receive before confirming.

What's Needed

A lightweight exchange rate service baked into the Go API that can convert between the assets Nester uses (USDC, XLM) and the fiat currencies supported by the offramp (NGN, GHS, KES, USD).

Suggested Data Sources (in priority order)

  1. Stellar DEX / AMM — on-chain USDC/XLM rate via Horizon's /order_book endpoint. Most accurate for Stellar-native assets, no third-party dependency.
  2. DeFiLlama prices API — free, no key required. Good fallback.
  3. CoinGecko — secondary fallback. Rate-limited on free tier.

For fiat rates (XLM/NGN, USDC/NGN etc.), these should chain through USD as an intermediate: USDC → USD (effectively 1:1) and USD → NGN via a forex API (e.g. ExchangeRate-API, Open Exchange Rates, or a Central Bank API for NGN).

Implementation Plan

New package: apps/api/internal/oracle/

oracle/
  rate.go          // ExchangeRate type, Provider interface
  stellar.go       // Horizon order book source
  defillama.go     // DeFiLlama fallback
  fiat.go          // Forex feed for NGN/GHS/KES
  cache.go         // In-memory TTL cache (30s for crypto, 5min for fiat)
  service.go       // Aggregates sources with fallback logic

New endpoint:

GET /api/v1/rates?base=USDC&quote=NGN
GET /api/v1/rates?base=XLM&quote=USD

Response:

{
  "success": true,
  "data": {
    "base": "USDC",
    "quote": "NGN",
    "rate": 1647.50,
    "source": "horizon+forex",
    "fetched_at": "2026-04-02T12:00:00Z",
    "expires_at": "2026-04-02T12:05:00Z"
  }
}

Wire into existing flows:

  • Settlement service: include estimated_local_amount in settlement creation response
  • Vault handler: include usd_value on vault balance responses

Caching Strategy

  • Crypto rates: 30-second TTL (stale-while-revalidate acceptable)
  • Fiat rates: 5-minute TTL
  • On source failure: serve stale + set stale: true flag in response, never error the whole request

Acceptance Criteria

  • GET /api/v1/rates returns conversion rate for any supported pair
  • Rates are cached in-memory and not fetched on every request
  • Stale data is served with a warning flag if all sources fail (never a 5xx)
  • Settlement response includes estimated_local_amount pre-calculated
  • Unit tests for cache logic and fallback chain
  • Supported pairs at minimum: USDC→NGN, USDC→GHS, USDC→KES, USDC→USD, XLM→USD

Metadata

Metadata

Assignees

Labels

Stellar WaveIssues in the Stellar wave programbackendGo backend infrastructurefeatureNew feature or functionalitypriority:highHigh priority - foundational work

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions