Skip to content

bug: ReflectorService fetches prices from CoinGecko only — advertised Reflector oracle integration is not implemented #8

Description

@Uchechukwu-Ekezie

Description

The project is named SentientFi and prominently features Reflector oracle integration as a core feature (README, architecture diagrams, smart contract code), but the ReflectorService class in backend/src/services/reflector.ts exclusively fetches prices from CoinGecko. No call to the Stellar Reflector oracle contract is made anywhere in the backend. The service name is misleading and the feature is not implemented.

Evidence

README claims Reflector integration

The README lists as a key feature:

"Reflector oracle integration for accurate on-chain price feeds"

Smart contract implements Reflector correctly

contracts/src/reflector.rs defines a proper ReflectorClient:

mod reflector_interface {
    use soroban_sdk::{contractclient, Env, Symbol};

    #[contractclient(name = "ReflectorClient")]
    pub trait ReflectorInterface {
        fn lastprice(env: Env, asset: soroban_sdk::Symbol) -> Option<PriceData>;
    }
}

contracts/src/lib.rs line 125 calls it during execute_rebalance:

let price_data = ReflectorClient::new(&env, &reflector_address)
    .lastprice(&asset_symbol);

Backend ReflectorService only uses CoinGecko

backend/src/services/reflector.ts:

// Line 1: unused import
import SorobanRpc from '@stellar/stellar-sdk/rpc'  // ← Imported but never used

// Line 105: actual price fetch
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${coinIds.join(',')}&vs_currencies=usd&include_24hr_change=true`
const response = await fetch(url, { ... })

There is no code path in the backend that ever calls a Soroban RPC endpoint to query the Reflector contract. The SorobanRpc import on line 1 being unused confirms this is a placeholder that was never implemented.

Fallback prices are hardcoded (not from cache)

When CoinGecko fails, the backend falls back to:

const fallbackPrices = {
    XLM: { price: 0.358878, ... },
    BTC: { price: 111150, ... },
    ETH: { price: 4384.56, ... },
    USDC: { price: 0.999781, ... },
}

These are hardcoded values, not a recent snapshot from Reflector.

Impact

  1. Single point of failure: All price data depends entirely on CoinGecko. If CoinGecko rate-limits the API key (or if the free tier is used), price data goes stale immediately
  2. Incorrect documentation: Users and auditors expect on-chain price feeds from the advertised Reflector integration
  3. Smart contract / backend mismatch: The contract uses on-chain Reflector prices for rebalancing decisions; the backend uses CoinGecko prices for drift detection. The two systems may use different prices for the same asset at the same moment, causing incorrect rebalancing triggers

Proposed Fix

Implement the Reflector oracle backend integration:

// backend/src/services/reflector.ts

import SorobanRpc from '@stellar/stellar-sdk/rpc'
import { Contract, scValToNative, xdr } from '@stellar/stellar-sdk'

const REFLECTOR_CONTRACT_ID = process.env.REFLECTOR_CONTRACT_ID!
const SOROBAN_RPC_URL = process.env.SOROBAN_RPC_URL || 'https://soroban-testnet.stellar.org'

async function fetchPriceFromReflector(asset: string): Promise<number | null> {
    const rpc = new SorobanRpc.Server(SOROBAN_RPC_URL)
    const contract = new Contract(REFLECTOR_CONTRACT_ID)

    try {
        const result = await rpc.simulateTransaction(
            contract.call('lastprice', /* asset Symbol */)
        )
        if (SorobanRpc.Api.isSimulationSuccess(result)) {
            const priceData = scValToNative(result.result!.retval)
            return priceData?.price ?? null
        }
    } catch (err) {
        logger.warn(`[Reflector] Price fetch failed for ${asset}:`, err)
    }
    return null
}

Then use Reflector as primary source and CoinGecko as fallback:

Price request → Reflector contract (on-chain) → CoinGecko fallback → hardcoded fallback

Add to .env.example:

REFLECTOR_CONTRACT_ID=CDOR33VMS...
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org

Files Affected

  • backend/src/services/reflector.ts — implement Reflector calls
  • backend/.env.example — add REFLECTOR_CONTRACT_ID, SOROBAN_RPC_URL
  • README.md — clarify current status (CoinGecko only) until fix is merged

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official Campaign

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions