Skip to content

Commit

Permalink
Add CRC support to the FiatApi
Browse files Browse the repository at this point in the history
Bridged via USD rates from Coingecko.
  • Loading branch information
sisou committed Jan 25, 2024
1 parent 34ea200 commit cb6cbbe
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 4 deletions.
72 changes: 68 additions & 4 deletions src/fiat-api/FiatApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export enum FiatApiSupportedFiatCurrency {
ZAR = 'zar', // South African Rand
}

export enum FiatApiBridgedFiatCurrency {
CRC = 'crc', // Costa Rican Colón
}

const API_URL = 'https://api.coingecko.com/api/v3';
const COINGECKO_COIN_IDS = {
[FiatApiSupportedCryptoCurrency.NIM]: 'nimiq-2',
Expand Down Expand Up @@ -117,16 +121,70 @@ export async function getExchangeRates(
*/
export async function getHistoricExchangeRatesByRange(
cryptoCurrency: FiatApiSupportedCryptoCurrency,
vsCurrency: FiatApiSupportedFiatCurrency | FiatApiSupportedCryptoCurrency,
vsCurrency: FiatApiSupportedFiatCurrency | FiatApiBridgedFiatCurrency | FiatApiSupportedCryptoCurrency,
from: number, // in milliseconds
to: number, // in milliseconds
): Promise<Array<[number, number]>> {
let bridgedCurrency: FiatApiBridgedFiatCurrency | undefined;
let bridgedExchangeRatePromise: Promise<Record<string, number> | null> = Promise.resolve(null);
if (Object.values(FiatApiBridgedFiatCurrency).includes(vsCurrency as FiatApiBridgedFiatCurrency)) {
bridgedCurrency = vsCurrency as FiatApiBridgedFiatCurrency;

switch (bridgedCurrency) {
case FiatApiBridgedFiatCurrency.CRC: {
// Use USD as the intermediate currency
vsCurrency = FiatApiSupportedFiatCurrency.USD;

// Adapt dates to Costa Rica timezone (UTC-6, all year round)
const fromDate = _timestampToUtcOffset(from, -6);
const toDate = _timestampToUtcOffset(to, -6);
// Get the day portion as ISO string
const fromDay = fromDate.toISOString().split('T')[0];
const toDay = toDate.toISOString().split('T')[0];

bridgedExchangeRatePromise = _fetch(
`https://usd-crc-historic-rate.deno.dev/api/rates/${fromDay}/${toDay}`,
);
break;
}
default:
throw new Error(`Unsupported bridged currency: ${bridgedCurrency}`);
}
}

const coinId = COINGECKO_COIN_IDS[cryptoCurrency.toLowerCase() as FiatApiSupportedCryptoCurrency];
// Note that from and to are expected in seconds but returned timestamps are in ms.
from = Math.floor(from / 1000);
to = Math.ceil(to / 1000);
const { prices: result } = await _fetch(`${API_URL}/coins/${coinId}/market_chart/range`
+ `?vs_currency=${vsCurrency}&from=${from}&to=${to}`);
const [
{ prices: result },
bridgedExchangeRates,
] = await Promise.all([
_fetch(`${API_URL}/coins/${coinId}/market_chart/range?vs_currency=${vsCurrency}&from=${from}&to=${to}`) as Promise<{
prices: [number, number][],
}>,
bridgedExchangeRatePromise,
]);

if (bridgedCurrency && bridgedExchangeRates) {
for (let i = 0; i < result.length; ++i) {
const [timestamp, price] = result[i];
switch (bridgedCurrency) {
case FiatApiBridgedFiatCurrency.CRC: {
// Adapt date to Costa Rica timezone (UTC-6, all year round)
const date = _timestampToUtcOffset(timestamp, -6);
// Get the day portion as ISO string
const day = date.toISOString().split('T')[0];
// Convert from USD to CRC
result[i] = [timestamp, price * bridgedExchangeRates[day]];
break;
}
default:
throw new Error(`Unsupported bridged currency: ${bridgedCurrency}`);
}
}
}

return result;
}

Expand All @@ -135,7 +193,7 @@ export async function getHistoricExchangeRatesByRange(
*/
export async function getHistoricExchangeRates(
cryptoCurrency: FiatApiSupportedCryptoCurrency,
vsCurrency: FiatApiSupportedFiatCurrency | FiatApiSupportedCryptoCurrency,
vsCurrency: FiatApiSupportedFiatCurrency | FiatApiBridgedFiatCurrency | FiatApiSupportedCryptoCurrency,
timestamps: number[],
disableMinutlyData = false,
): Promise<Map<number, number|undefined>> {
Expand Down Expand Up @@ -286,3 +344,9 @@ async function _fetch(input: RequestInfo, init?: RequestInit): Promise<any> {
} while (!result);
return result;
}

function _timestampToUtcOffset(timestamp: number, utcOffset: number): Date {
const date = new Date(timestamp);
date.setHours(date.getHours() + utcOffset);
return date;
}
64 changes: 64 additions & 0 deletions tests/FiatApi.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @jest-environment node
*/

/* global describe, it, expect */

import {
FiatApiBridgedFiatCurrency,
FiatApiSupportedCryptoCurrency,
FiatApiSupportedFiatCurrency,
getHistoricExchangeRates,
} from '../src/fiat-api/FiatApi';

describe('FiatApi', () => {
it('can fetch historic USD rates for BTC', async () => {
const timestamps = [
new Date('2023-01-01T00:00:00.000Z').getTime(),
new Date('2023-01-01T01:00:00.000Z').getTime(),
new Date('2023-01-01T02:00:00.000Z').getTime(),
new Date('2023-01-01T03:00:00.000Z').getTime(),
new Date('2023-01-01T04:00:00.000Z').getTime(),
new Date('2023-10-13T05:00:00.000Z').getTime(),
new Date('2023-10-13T06:00:00.000Z').getTime(),
new Date('2023-10-13T07:00:00.000Z').getTime(),
new Date('2023-10-13T08:00:00.000Z').getTime(),
new Date('2023-10-13T09:00:00.000Z').getTime(),
];
const rates = await getHistoricExchangeRates(
FiatApiSupportedCryptoCurrency.BTC,
FiatApiSupportedFiatCurrency.USD,
timestamps,
);
expect(rates.size).toBe(10);
expect(rates.get(timestamps[0])).toBe(16541.90475052885);
expect(rates.get(timestamps[1])).toBe(16543.017237311888);
expect(rates.get(timestamps[5])).toBe(26793.954797943756);
expect(rates.get(timestamps[6])).toBe(26810.776705117445);
});

it('can fetch historic CRC (bridged) rates for BTC', async () => {
const timestamps = [
new Date('2023-01-01T00:00:00.000Z').getTime(),
new Date('2023-01-01T01:00:00.000Z').getTime(),
new Date('2023-01-01T02:00:00.000Z').getTime(),
new Date('2023-01-01T03:00:00.000Z').getTime(),
new Date('2023-01-01T04:00:00.000Z').getTime(),
new Date('2023-10-13T05:00:00.000Z').getTime(),
new Date('2023-10-13T06:00:00.000Z').getTime(),
new Date('2023-10-13T07:00:00.000Z').getTime(),
new Date('2023-10-13T08:00:00.000Z').getTime(),
new Date('2023-10-13T09:00:00.000Z').getTime(),
];
const rates = await getHistoricExchangeRates(
FiatApiSupportedCryptoCurrency.BTC,
FiatApiBridgedFiatCurrency.CRC,
timestamps,
);
expect(rates.size).toBe(10);
expect(rates.get(timestamps[0])).toBe(9893382.393196296);
expect(rates.get(timestamps[1])).toBe(9894047.749291496);
expect(rates.get(timestamps[5])).toBe(14290555.791483302);
expect(rates.get(timestamps[6])).toBe(14244742.864549162);
});
});

0 comments on commit cb6cbbe

Please sign in to comment.