Files
edr-platform/packages/api-common/src/services/exchange/exchange.service.ts

81 lines
2.4 KiB
TypeScript

import { Inject, Injectable } from "@nestjs/common";
import { CbeExchangeProvider, CbeProviderStatus } from "./cbe.provider";
import { EXCHANGE_OPTIONS, ExchangeOptions } from "./exchange.options";
import { CurrencyCode } from "./exchange.types";
/**
* Currency exchange service. Resolves the rate between any supported currency
* pair and converts amounts, backed by a rate provider (currently CBE).
*
* Resolution order for `getRate(from, to)`:
* 1. `from === to` → `1`.
* 2. Provider supplies the pair directly (e.g. CBE → USD→ETB).
* 3. Provider supplies the inverse → return `1 / inverse` (e.g. ETB→USD).
*
* Configure via {@link ExchangeModule.forRoot} / `forRootAsync`.
*/
@Injectable()
export class ExchangeService {
private readonly provider: CbeExchangeProvider;
constructor(@Inject(EXCHANGE_OPTIONS) options: ExchangeOptions) {
this.provider = new CbeExchangeProvider(options);
}
/**
* Returns the rate to convert 1 unit of `from` into `to`
* (i.e. `amountInTo = amountInFrom * getRate(from, to)`).
*/
async getRate(from: CurrencyCode, to: CurrencyCode): Promise<number> {
if (from === to) {
return 1;
}
const direct = await this.provider.getBaseRate({ from, to });
if (direct !== null) {
return direct;
}
const inverse = await this.provider.getBaseRate({ from: to, to: from });
if (inverse !== null && inverse > 0) {
return 1 / inverse;
}
throw new Error(
`No exchange rate available for ${from}${to} from provider ${this.provider.name}`,
);
}
/**
* Health of the underlying rate feed — what was served last and whether it
* is currently failing. For operator-facing status displays.
*/
getProviderStatus(): CbeProviderStatus {
return this.provider.getStatus();
}
/** Converts `amount` from one currency to another using {@link getRate}. */
async convert(
amount: number,
from: CurrencyCode,
to: CurrencyCode,
): Promise<number> {
const rate = await this.getRate(from, to);
return amount * rate;
}
/**
* Convenience alias for `getRate('USD', 'ETB')`.
* @deprecated Prefer {@link getRate}; kept for existing callers.
*/
getUsdToEtbRate(): Promise<number> {
return this.getRate("USD", "ETB");
}
/** Convenience alias for `getRate('ETB', 'USD')`. */
getEtbToUsdRate(): Promise<number> {
return this.getRate("ETB", "USD");
}
}