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 0a8d1e7
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 3 deletions.
71 changes: 68 additions & 3 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 @@ -135,7 +139,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 @@ -180,9 +184,45 @@ export async function getHistoricExchangeRates(
timestampIndex = searchEndIndex;
}

// 2. Query Coingecko Api
// 2a. If we have a bridged currency, fetch the historic exchange rates for it
let bridgedCurrency: FiatApiBridgedFiatCurrency | undefined;
let bridgedExchangeRatePromise: Promise<Record<string, number>> | undefined;
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;

// Find first and last date requested
const fromDate = new Date(timestamps[0]);
const untilDate = new Date(timestamps[timestamps.length - 1]);
// Adapt dates to Costa Rica timezone (UTC-6, all year round)
fromDate.setHours(fromDate.getHours() - 6);
untilDate.setHours(untilDate.getHours() - 6);
// Get the day portion
const from = fromDate.toISOString().split('T')[0];
const until = untilDate.toISOString().split('T')[0];

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

// 2b. Query Coingecko Api
const fetchPromises = chunks.map(
(chunk) => getHistoricExchangeRatesByRange(cryptoCurrency, vsCurrency, chunk.start, chunk.end),
(chunk) => getHistoricExchangeRatesByRange(
cryptoCurrency,
vsCurrency as FiatApiSupportedFiatCurrency | FiatApiSupportedCryptoCurrency,
chunk.start,
chunk.end,
),
);
const prices = (await Promise.all(fetchPromises)).reduce(
(accumulated, singleResult) => [...singleResult, ...accumulated],
Expand Down Expand Up @@ -226,6 +266,31 @@ export async function getHistoricExchangeRates(
}
++timestampIndex; // Continue with next timestamp and check same priceIndex
}

// 4. If bridged, convert the prices from the intermediate currency
if (bridgedCurrency && bridgedExchangeRatePromise) {
const bridgedExchangeRates = await bridgedExchangeRatePromise;
for (const [timestamp, price] of result) {
if (price) {
switch (bridgedCurrency) {
case FiatApiBridgedFiatCurrency.CRC: {
// Find first and last date requested
const date = new Date(timestamps[0]);
// Adapt date to Costa Rica timezone (UTC-6, all year round)
date.setHours(date.getHours() - 6);
// Get the day portion
const day = date.toISOString().split('T')[0];
// Convert from USD to CRC
result.set(timestamp, price * bridgedExchangeRates[day]);
break;
}
default:
throw new Error(`Unsupported bridged currency: ${bridgedCurrency}`);
}
}
}
}

return result;
}

Expand Down
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])).toBeDefined();
expect(rates.get(timestamps[5])).toBeDefined();
expect(rates.get(timestamps[1])).toBeGreaterThan(16500);
expect(rates.get(timestamps[6])).toBeGreaterThan(16800);
}, 30e3);

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])).toBeDefined();
expect(rates.get(timestamps[5])).toBeDefined();
expect(rates.get(timestamps[1])).toBeGreaterThan(165000); // ~price in BTC * 10
expect(rates.get(timestamps[6])).toBeGreaterThan(168000); // ~price in BTC * 10
}, 30e3);
});

0 comments on commit 0a8d1e7

Please sign in to comment.