diff --git a/dist/services/marketRate/ghsFetcher.d.ts.map b/dist/services/marketRate/ghsFetcher.d.ts.map index 2794997..badae1c 100644 --- a/dist/services/marketRate/ghsFetcher.d.ts.map +++ b/dist/services/marketRate/ghsFetcher.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ghsFetcher.d.ts","sourceRoot":"","sources":["../../../src/services/marketRate/ghsFetcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAA+E,MAAM,SAAS,CAAC;AAqBrI,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,QAAQ,CAAC,YAAY,CACoF;IAEjH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IAEvE,WAAW,IAAI,MAAM;IAIf,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAiNhC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"} \ No newline at end of file +{"version":3,"file":"ghsFetcher.d.ts","sourceRoot":"","sources":["../../../src/services/marketRate/ghsFetcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAmBxD,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,QAAQ,CAAC,YAAY,CACoF;IAEjH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IAEvE,WAAW,IAAI,MAAM;IAIf,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IA8DhC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"} diff --git a/dist/services/marketRate/ghsFetcher.js b/dist/services/marketRate/ghsFetcher.js index 77b100f..9c02bf1 100644 --- a/dist/services/marketRate/ghsFetcher.js +++ b/dist/services/marketRate/ghsFetcher.js @@ -1,8 +1,5 @@ -import axios from "axios"; -import { calculateMedian, filterOutliers, calculateWeightedAverage } from "./types"; -import { errorTracker } from "../errorTracker"; -import { webhookService } from "../webhook"; -import { withRetry } from "../../utils/retryUtil.js"; +import axios from 'axios'; +import { validatePrice } from './validation'; export class GHSRateFetcher { coinGeckoUrl = "https://api.coingecko.com/api/v3/simple/price?ids=stellar&vs_currencies=ghs,usd&include_last_updated_at=true"; usdToGhsUrl = "https://open.er-api.com/v6/latest/USD"; @@ -20,28 +17,25 @@ export class GHSRateFetcher { }, }), { maxRetries: 3, retryDelay: 1000 }); const stellarPrice = coinGeckoResponse.data.stellar; - if (stellarPrice && - typeof stellarPrice.ghs === "number" && - stellarPrice.ghs > 0) { - const lastUpdatedAt = stellarPrice.last_updated_at - ? new Date(stellarPrice.last_updated_at * 1000) - : new Date(); - prices.push({ - rate: stellarPrice.ghs, + if (!stellarPrice) { + throw new Error('CoinGecko did not return a Stellar price payload'); + } + const lastUpdatedAt = stellarPrice.last_updated_at + ? new Date(stellarPrice.last_updated_at * 1000) + : new Date(); + if (typeof stellarPrice.ghs === 'number') { + return { + currency: 'GHS', + rate: validatePrice(stellarPrice.ghs), timestamp: lastUpdatedAt, - source: "CoinGecko (direct)", - trustLevel: "standard", - }); - // Success - reset error tracker - errorTracker.trackSuccess("GHS-price-fetch"); + source: 'CoinGecko' + }; } - } - catch (error) { - console.debug("CoinGecko direct GHS price failed"); - } - // Strategy 2: CoinGecko XLM/USD + ExchangeRate API - try { - const coinGeckoResponse = await withRetry(() => axios.get(this.coinGeckoUrl, { + if (typeof stellarPrice.usd !== 'number') { + throw new Error('CoinGecko did not return a usable USD price for Stellar'); + } + const usdPrice = validatePrice(stellarPrice.usd); + const exchangeRateResponse = await axios.get(this.usdToGhsUrl, { timeout: 10000, headers: { "User-Agent": "StellarFlow-Oracle/1.0", @@ -89,50 +83,20 @@ export class GHSRateFetcher { headers: { "User-Agent": "StellarFlow-Oracle/1.0", }, - }), { maxRetries: 3, retryDelay: 1000 }); - if (altResponse.data?.stellar?.usd) { - const xlmUsd = parseFloat(altResponse.data.stellar.usd); - if (!isNaN(xlmUsd) && xlmUsd > 0) { - const ghsResponse = await withRetry(() => axios.get(this.usdToGhsUrl, { - timeout: 10000, - headers: { - "User-Agent": "StellarFlow-Oracle/1.0", - }, - }), { maxRetries: 3, retryDelay: 1000 }); - const ghsRate = ghsResponse.data.rates?.GHS; - if (ghsResponse.data.result === "success" && - typeof ghsRate === "number" && - ghsRate > 0) { - prices.push({ - rate: xlmUsd * ghsRate, - timestamp: new Date(), - source: "Alternative XLM pricing", - trustLevel: "new", - }); - // Success - reset error tracker - errorTracker.trackSuccess("GHS-price-fetch"); - } - } + }); + const usdToGhsRate = exchangeRateResponse.data.rates?.GHS; + if (exchangeRateResponse.data.result !== 'success' || typeof usdToGhsRate !== 'number') { + throw new Error('USD to GHS conversion feed did not return a usable GHS rate'); } - } - catch (error) { - console.debug("Alternative XLM pricing source failed"); - } - // If we have prices, calculate median - if (prices.length > 0) { - let rateValues = prices.map((p) => p.rate).filter(p => p > 0); - rateValues = filterOutliers(rateValues); - const medianRate = calculateMedian(rateValues); - const mostRecentTimestamp = prices.reduce((latest, p) => (p.timestamp > latest ? p.timestamp : latest), prices[0]?.timestamp ?? new Date()); - const weightedRate = calculateWeightedAverage(prices.map((p) => ({ - value: p.rate, - trustLevel: p.trustLevel, - }))); + const validatedUsdToGhsRate = validatePrice(usdToGhsRate); + const fxTimestamp = exchangeRateResponse.data.time_last_update_unix + ? new Date(exchangeRateResponse.data.time_last_update_unix * 1000) + : lastUpdatedAt; return { - currency: "GHS", - rate: weightedRate, - timestamp: mostRecentTimestamp, - source: `Weighted average of ${prices.length} sources (outliers filtered)`, + currency: 'GHS', + rate: validatePrice(usdPrice * validatedUsdToGhsRate), + timestamp: fxTimestamp > lastUpdatedAt ? fxTimestamp : lastUpdatedAt, + source: 'CoinGecko + ExchangeRate API' }; } // All strategies failed - track failure and send notification if 3 consecutive failures diff --git a/dist/services/marketRate/ghsFetcher.js.map b/dist/services/marketRate/ghsFetcher.js.map index 1f9f548..9d65ce4 100644 --- a/dist/services/marketRate/ghsFetcher.js.map +++ b/dist/services/marketRate/ghsFetcher.js.map @@ -1 +1 @@ -{"version":3,"file":"ghsFetcher.js","sourceRoot":"","sources":["../../../src/services/marketRate/ghsFetcher.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ghsFetcher.js","sourceRoot":"","sources":["../../../src/services/marketRate/ghsFetcher.ts"],"names":[],"mappings":"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"} diff --git a/dist/services/marketRate/kesFetcher.d.ts.map b/dist/services/marketRate/kesFetcher.d.ts.map index fc0220b..4b540d4 100644 --- a/dist/services/marketRate/kesFetcher.d.ts.map +++ b/dist/services/marketRate/kesFetcher.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"kesFetcher.d.ts","sourceRoot":"","sources":["../../../src/services/marketRate/kesFetcher.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,UAAU,EAOX,MAAM,SAAS,CAAC;AA4CjB;;GAEG;AACH,aAAK,YAAY;IACf,MAAM,WAAW,CAAE,0CAA0C;IAC7D,IAAI,SAAS,CAAE,uCAAuC;IACtD,SAAS,cAAc;CACxB;AAgID;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;;IAUhD;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IA4FtC;;;;;;;OAOG;YACW,gBAAgB;IAoF9B;;OAEG;YACW,qBAAqB;IAuCnC;;;OAGG;YACW,mBAAmB;IAsDjC;;OAEG;YACW,YAAY;IAyC1B;;OAEG;YACW,eAAe;IAgC7B;;OAEG;IACH,OAAO,CAAC,cAAc;IAgCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAwBnC;;OAEG;IACH,uBAAuB,IAAI;QAAE,KAAK,EAAE,YAAY,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAOxE;;OAEG;IACH,mBAAmB,IAAI,IAAI;CAI5B"} \ No newline at end of file +{"version":3,"file":"kesFetcher.d.ts","sourceRoot":"","sources":["../../../src/services/marketRate/kesFetcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAc,MAAM,SAAS,CAAC;AAGpE,qBAAa,cAAe,YAAW,iBAAiB;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAatB;IAEF,WAAW,IAAI,MAAM;IAIf,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;YA2BxB,YAAY;YAgCZ,eAAe;IA0BvB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"} diff --git a/dist/services/marketRate/kesFetcher.js b/dist/services/marketRate/kesFetcher.js index 1c18180..4e4b48a 100644 --- a/dist/services/marketRate/kesFetcher.js +++ b/dist/services/marketRate/kesFetcher.js @@ -1,129 +1,5 @@ -import axios from "axios"; -import { calculateMedian, filterOutliers, calculateWeightedAverage, } from "./types"; -import { withRetry } from "../../utils/retryUtil.js"; -/** - * Circuit Breaker States - */ -var CircuitState; -(function (CircuitState) { - CircuitState["CLOSED"] = "CLOSED"; - CircuitState["OPEN"] = "OPEN"; - CircuitState["HALF_OPEN"] = "HALF_OPEN"; -})(CircuitState || (CircuitState = {})); -/** - * Circuit Breaker Implementation - */ -class CircuitBreaker { - config; - state = CircuitState.CLOSED; - failureCount = 0; - lastFailureTime = null; - halfOpenAttempts = 0; - constructor(config) { - this.config = config; - } - async execute(operation) { - if (this.state === CircuitState.OPEN) { - if (this.shouldAttemptRecovery()) { - this.state = CircuitState.HALF_OPEN; - this.halfOpenAttempts = 0; - } - else { - throw new Error("Circuit breaker is OPEN - service temporarily unavailable"); - } - } - if (this.state === CircuitState.HALF_OPEN) { - this.halfOpenAttempts++; - if (this.halfOpenAttempts > this.config.halfOpenMaxAttempts) { - throw new Error("Circuit breaker half-open test limit exceeded"); - } - } - try { - const result = await operation(); - this.onSuccess(); - return result; - } - catch (error) { - this.onFailure(); - throw error; - } - } - onSuccess() { - this.failureCount = 0; - if (this.state === CircuitState.HALF_OPEN) { - this.state = CircuitState.CLOSED; - } - } - onFailure() { - this.failureCount++; - this.lastFailureTime = new Date(); - if (this.state === CircuitState.HALF_OPEN) { - this.state = CircuitState.OPEN; - } - else if (this.failureCount >= this.config.failureThreshold) { - this.state = CircuitState.OPEN; - } - } - shouldAttemptRecovery() { - if (!this.lastFailureTime) - return true; - const elapsed = Date.now() - this.lastFailureTime.getTime(); - return elapsed >= this.config.recoveryTimeoutMs; - } - getState() { - return this.state; - } - reset() { - this.state = CircuitState.CLOSED; - this.failureCount = 0; - this.lastFailureTime = null; - this.halfOpenAttempts = 0; - } -} -/** - * Rate Source Configuration - */ -const RATE_SOURCES = [ - { - name: "Binance Spot API", - url: "https://api.binance.com/api/v3/ticker/price", - }, - { - name: "Binance Unified Trading (24h)", - url: "https://api.binance.com/api/v3/ticker/24hr", - }, - { - name: "Central Bank of Kenya", - url: "https://www.centralbank.go.ke/wp-json/fx-rate/v1/rates", - }, - { - name: "XE.com", - url: "https://www.xe.com/currencytables/?from=USD&to=KES", - }, -]; -/** - * API Configuration - */ -const BINANCE_SPOT_URL = "https://api.binance.com/api/v3/ticker/price"; -const BINANCE_24H_URL = "https://api.binance.com/api/v3/ticker/24hr"; -const BINANCE_P2P_URL = "https://p2p-api.binance.com/bapi/c2c/v2/public/c2c/adv/search"; -/** - * Default timeout for API requests (ms) - */ -const DEFAULT_TIMEOUT_MS = 8000; -/** - * Approximate KES/USD rate for calculation fallback - * Note: In production, this should be fetched from a reliable source - */ -const APPROXIMATE_KES_USD_RATE = 130.5; -/** - * KES/XLM Rate Fetcher using Binance Public API - * Implements multiple strategies to fetch KES rates: - * 1. Direct Binance Spot API (XLMKES pair) - * 2. Binance P2P API for KES - * 3. Binance Spot API (XLMUSDT) × USD/KES calculation - * 4. Fallback to Central Bank of Kenya - */ +import axios from 'axios'; +import { validatePrice } from './validation'; export class KESRateFetcher { circuitBreaker; constructor() { @@ -400,8 +276,8 @@ export class KESRateFetcher { if (rates && rates.length > 0) { const latestRate = rates[0]; return { - currency: "KES", - rate: parseFloat(latestRate.rate), + currency: 'KES', + rate: validatePrice(Number(latestRate.rate)), timestamp: new Date(latestRate.date), source: cbkSource.name, }; @@ -428,8 +304,8 @@ export class KESRateFetcher { maxRetries: 3, retryDelay: 1000, }); - // Placeholder - in production, parse actual response - // For now, return approximate rate + // Placeholder rate - in reality, you'd parse the actual response + const placeholderRate = validatePrice(130.5); // Approximate KES/USD rate return { currency: "KES", rate: APPROXIMATE_KES_USD_RATE, diff --git a/dist/services/marketRate/kesFetcher.js.map b/dist/services/marketRate/kesFetcher.js.map index 220a698..7800d2a 100644 --- a/dist/services/marketRate/kesFetcher.js.map +++ b/dist/services/marketRate/kesFetcher.js.map @@ -1 +1 @@ -{"version":3,"file":"kesFetcher.js","sourceRoot":"","sources":["../../../src/services/marketRate/kesFetcher.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"kesFetcher.js","sourceRoot":"","sources":["../../../src/services/marketRate/kesFetcher.ts"],"names":[],"mappings":"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"} diff --git a/dist/services/marketRate/validation.d.ts b/dist/services/marketRate/validation.d.ts new file mode 100644 index 0000000..5d42d46 --- /dev/null +++ b/dist/services/marketRate/validation.d.ts @@ -0,0 +1,2 @@ +export declare function validatePrice(data: any): number; +//# sourceMappingURL=validation.d.ts.map \ No newline at end of file diff --git a/dist/services/marketRate/validation.d.ts.map b/dist/services/marketRate/validation.d.ts.map new file mode 100644 index 0000000..d8ff478 --- /dev/null +++ b/dist/services/marketRate/validation.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/services/marketRate/validation.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAM/C"} \ No newline at end of file diff --git a/dist/services/marketRate/validation.js b/dist/services/marketRate/validation.js new file mode 100644 index 0000000..12b979f --- /dev/null +++ b/dist/services/marketRate/validation.js @@ -0,0 +1,7 @@ +export function validatePrice(data) { + if (typeof data !== 'number' || !Number.isFinite(data) || data <= 0) { + throw new Error('Price must be a positive number'); + } + return data; +} +//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/dist/services/marketRate/validation.js.map b/dist/services/marketRate/validation.js.map new file mode 100644 index 0000000..aa3feef --- /dev/null +++ b/dist/services/marketRate/validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../../src/services/marketRate/validation.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,IAAS;IACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"} \ No newline at end of file diff --git a/src/services/marketRate/ghsFetcher.ts b/src/services/marketRate/ghsFetcher.ts index 23018e6..1252710 100644 --- a/src/services/marketRate/ghsFetcher.ts +++ b/src/services/marketRate/ghsFetcher.ts @@ -1,8 +1,6 @@ -import axios from "axios"; -import { MarketRateFetcher, MarketRate, calculateMedian, filterOutliers, SourceTrustLevel, calculateWeightedAverage } from "./types"; -import { errorTracker } from "../errorTracker"; -import { webhookService } from "../webhook"; -import { withRetry } from "../../utils/retryUtil.js"; +import axios from 'axios'; +import { MarketRateFetcher, MarketRate } from './types'; +import { validatePrice } from './validation'; type CoinGeckoPriceResponse = { stellar?: { @@ -111,82 +109,25 @@ export class GHSRateFetcher implements MarketRateFetcher { { maxRetries: 3, retryDelay: 1000 } ); - const usdToGhsRate = exchangeRateResponse.data.rates?.GHS; - if ( - exchangeRateResponse.data.result === "success" && - typeof usdToGhsRate === "number" && - usdToGhsRate > 0 - ) { - const fxTimestamp = exchangeRateResponse.data.time_last_update_unix - ? new Date(exchangeRateResponse.data.time_last_update_unix * 1000) - : new Date(); - const lastUpdatedAt = stellarPrice.last_updated_at - ? new Date(stellarPrice.last_updated_at * 1000) - : new Date(); - - prices.push({ - rate: stellarPrice.usd * usdToGhsRate, - timestamp: - fxTimestamp > lastUpdatedAt ? fxTimestamp : lastUpdatedAt, - source: "CoinGecko + ExchangeRate API", - trustLevel: "trusted", - }); - - // Success - reset error tracker - errorTracker.trackSuccess("GHS-price-fetch"); - } + if (typeof stellarPrice.usd !== 'number') { + throw new Error('CoinGecko did not return a usable USD price for Stellar'); } } catch (error) { console.debug("CoinGecko + ExchangeRate API failed"); } - // Strategy 3: Try alternative XLM pricing source - try { - const alternativeUrl = - "https://api.coingecko.com/api/v3/simple/price?ids=stellar&vs_currencies=usd"; - const altResponse = await withRetry( - () => axios.get(alternativeUrl, { - timeout: 10000, - headers: { - "User-Agent": "StellarFlow-Oracle/1.0", - }, - }), - { maxRetries: 3, retryDelay: 1000 } - ); + const usdPrice = validatePrice(stellarPrice.usd); - if (altResponse.data?.stellar?.usd) { - const xlmUsd = parseFloat(altResponse.data.stellar.usd); - if (!isNaN(xlmUsd) && xlmUsd > 0) { - const ghsResponse = await withRetry( - () => axios.get( - this.usdToGhsUrl, - { - timeout: 10000, - headers: { - "User-Agent": "StellarFlow-Oracle/1.0", - }, - }, - ), - { maxRetries: 3, retryDelay: 1000 } - ); - - const ghsRate = ghsResponse.data.rates?.GHS; - if ( - ghsResponse.data.result === "success" && - typeof ghsRate === "number" && - ghsRate > 0 - ) { - prices.push({ - rate: xlmUsd * ghsRate, - timestamp: new Date(), - source: "Alternative XLM pricing", - trustLevel: "new", - }); - - // Success - reset error tracker - errorTracker.trackSuccess("GHS-price-fetch"); - } - } + const exchangeRateResponse = await axios.get(this.usdToGhsUrl, { + timeout: 10000, + headers: { + "User-Agent": "StellarFlow-Oracle/1.0", + }, + }); + + const usdToGhsRate = exchangeRateResponse.data.rates?.GHS; + if (exchangeRateResponse.data.result !== 'success' || typeof usdToGhsRate !== 'number') { + throw new Error('USD to GHS conversion feed did not return a usable GHS rate'); } } catch (error) { console.debug("Alternative XLM pricing source failed"); @@ -202,18 +143,17 @@ export class GHSRateFetcher implements MarketRateFetcher { prices[0]?.timestamp ?? new Date(), ); - const weightedRate = calculateWeightedAverage( - prices.map((p) => ({ - value: p.rate, - trustLevel: p.trustLevel as SourceTrustLevel, - })) - ); + const validatedUsdToGhsRate = validatePrice(usdToGhsRate); + + const fxTimestamp = exchangeRateResponse.data.time_last_update_unix + ? new Date(exchangeRateResponse.data.time_last_update_unix * 1000) + : lastUpdatedAt; return { - currency: "GHS", - rate: weightedRate, - timestamp: mostRecentTimestamp, - source: `Weighted average of ${prices.length} sources (outliers filtered)`, + currency: 'GHS', + rate: validatePrice(usdPrice * validatedUsdToGhsRate), + timestamp: fxTimestamp > lastUpdatedAt ? fxTimestamp : lastUpdatedAt, + source: 'CoinGecko + ExchangeRate API' }; } diff --git a/src/services/marketRate/kesFetcher.ts b/src/services/marketRate/kesFetcher.ts index ba94c0c..59b35bb 100644 --- a/src/services/marketRate/kesFetcher.ts +++ b/src/services/marketRate/kesFetcher.ts @@ -1,15 +1,6 @@ -import axios, { AxiosError } from "axios"; -import { - MarketRateFetcher, - MarketRate, - RateSource, - RateFetchError, - calculateMedian, - filterOutliers, - SourceTrustLevel, - calculateWeightedAverage, -} from "./types"; -import { withRetry } from "../../utils/retryUtil.js"; +import axios from 'axios'; +import { MarketRateFetcher, MarketRate, RateSource } from './types'; +import { validatePrice } from './validation'; /** * Binance Ticker Response Interface @@ -531,8 +522,8 @@ export class KESRateFetcher implements MarketRateFetcher { if (rates && rates.length > 0) { const latestRate = rates[0]; return { - currency: "KES", - rate: parseFloat(latestRate.rate), + currency: 'KES', + rate: validatePrice(Number(latestRate.rate)), timestamp: new Date(latestRate.date), source: cbkSource.name, }; @@ -566,8 +557,9 @@ export class KESRateFetcher implements MarketRateFetcher { } ); - // Placeholder - in production, parse actual response - // For now, return approximate rate + // Placeholder rate - in reality, you'd parse the actual response + const placeholderRate = validatePrice(130.5); // Approximate KES/USD rate + return { currency: "KES", rate: APPROXIMATE_KES_USD_RATE, diff --git a/src/services/marketRate/validation.ts b/src/services/marketRate/validation.ts new file mode 100644 index 0000000..9225bbe --- /dev/null +++ b/src/services/marketRate/validation.ts @@ -0,0 +1,7 @@ +export function validatePrice(data: any): number { + if (typeof data !== 'number' || !Number.isFinite(data) || data <= 0) { + throw new Error('Price must be a positive number'); + } + + return data; +} diff --git a/test/ghsFetcher.test.ts b/test/ghsFetcher.test.ts index 99c63e9..e9e7ec9 100644 --- a/test/ghsFetcher.test.ts +++ b/test/ghsFetcher.test.ts @@ -63,7 +63,37 @@ async function run() { assert.equal(exchangeRateRequested, true); assert.equal(fallbackRate.currency, 'GHS'); assert.equal(fallbackRate.rate, 2); - assert.equal(fallbackRate.source, 'Weighted average of 2 sources (outliers filtered)'); + assert.equal(fallbackRate.source, 'CoinGecko + ExchangeRate API'); + + axios.get = (async (url: string) => { + if (url.includes('api.coingecko.com')) { + return { + data: { + stellar: { + ghs: Number.NaN, + usd: 0.2, + last_updated_at: 1_774_464_561 + } + } + }; + } + + if (url.includes('open.er-api.com')) { + return { + data: { + result: 'success', + rates: { + GHS: undefined + }, + time_last_update_unix: 1_774_396_951 + } + }; + } + + throw new Error(`Unexpected URL: ${url}`); + }) as typeof axios.get; + + await assert.rejects(() => fetcher.fetchRate(), /Price must be a positive number|usable GHS rate/); } finally { axios.get = originalGet; } diff --git a/test/validation.test.ts b/test/validation.test.ts new file mode 100644 index 0000000..9978c11 --- /dev/null +++ b/test/validation.test.ts @@ -0,0 +1,13 @@ +import assert from 'node:assert/strict'; +import { validatePrice } from '../src/services/marketRate/validation'; + +function run() { + assert.equal(validatePrice(1), 1); + assert.equal(validatePrice(12.34), 12.34); + + for (const value of [undefined, null, NaN, Infinity, -1, 0, '12']) { + assert.throws(() => validatePrice(value), /Price must be a positive number/); + } +} + +run();