Skip to content

Commit

Permalink
FiatApi: refactor to possibly support other providers in the future
Browse files Browse the repository at this point in the history
  • Loading branch information
danimoh committed Jul 11, 2024
1 parent 2201052 commit dcbd088
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 122 deletions.
13 changes: 12 additions & 1 deletion src/components/PaymentInfoLine.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
* expires?: number
* }} PaymentInfo */

/**
* @type {Record<FiatApi.SupportedProvider, string>}
* As Record such that ts notifies us, if an entry is missing.
*/
const FIAT_API_PROVIDER_URLS = {
[FiatApi.SupportedProvider.CoinGecko]: 'coingecko.com',
};
const FIAT_API_PROVIDER_URL = FIAT_API_PROVIDER_URLS[FiatApi.Provider];

class PaymentInfoLine { // eslint-disable-line no-unused-vars
/**
* @param {PaymentInfo} paymentInfo
Expand Down Expand Up @@ -249,7 +258,9 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars

// Converted to absolute percent, rounded to one decimal
const formattedRateDeviation = `${Math.round(Math.abs(rateDeviation) * 100 * 10) / 10}%`;
$rateInfo.textContent = rateInfo.replace('%RATE_DEVIATION%', formattedRateDeviation);
$rateInfo.textContent = rateInfo
.replace('{formattedRateDeviation}', formattedRateDeviation)
.replace('{provider}', FIAT_API_PROVIDER_URL);

$rateInfo.style.display = 'block';
} else {
Expand Down
216 changes: 122 additions & 94 deletions src/lib/FiatApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,110 +11,138 @@ class FiatApi {
* >>>}
*/
static async getExchangeRates(cryptoCurrencies, vsCurrencies) {
const coinIds = cryptoCurrencies.map(currency => FiatApi.COINGECKO_COIN_IDS[currency]);
const response = await fetch(
`${FiatApi.API_URL}/simple/price?ids=${coinIds.join(',')}&vs_currencies=${vsCurrencies.join(',')}`,
);
if (!response.ok) throw new Error(`Failed to fetch exchange rates: ${response.status}`);
const apiResult = await response.json();
// Map coingecko coin ids back to SupportedCryptoCurrency enum and sanitize retrieved data.
return cryptoCurrencies.reduce((result, cryptoCurrency) => {
const record = apiResult[FiatApi.COINGECKO_COIN_IDS[cryptoCurrency]];
const sanitizedRecord = Object.keys(record).reduce((sanitized, fiatCurrency) => ({
...sanitized,
...(Object.values(FiatApi.SupportedFiatCurrency).includes(/** @type {any} */ (fiatCurrency))
? { [fiatCurrency]: parseFloat(record[fiatCurrency]) }
: null
),
}), {});
return {
...result,
[cryptoCurrency]: sanitizedRecord,
};
}, {});
switch (FiatApi.Provider) {
case FiatApi.SupportedProvider.CoinGecko: {
const coinIds = cryptoCurrencies.map(currency => FiatApi.COINGECKO_COIN_IDS[currency]);
const response = await fetch(
`${FiatApi.API_URL}/simple/price?ids=${coinIds.join(',')}&vs_currencies=${vsCurrencies.join(',')}`,
);
if (!response.ok) throw new Error(`Failed to fetch exchange rates: ${response.status}`);
const apiResult = await response.json();
// Map coingecko coin ids back to SupportedCryptoCurrency enum and sanitize retrieved data.
return cryptoCurrencies.reduce((result, cryptoCurrency) => {
const record = apiResult[FiatApi.COINGECKO_COIN_IDS[cryptoCurrency]];
const sanitizedRecord = Object.keys(record).reduce((sanitized, fiatCurrency) => ({
...sanitized,
...(Object.values(FiatApi.SupportedFiatCurrency).includes(/** @type {any} */ (fiatCurrency))
? { [fiatCurrency]: parseFloat(record[fiatCurrency]) }
: null
),
}), {});
return {
...result,
[cryptoCurrency]: sanitizedRecord,
};
}, {});
}

default:
throw new Error(`FiatApi.Provider ${FiatApi.Provider} not supported yet.`);
}
}
}

/**
* @readonly
* @enum { 'nim' | 'btc' }
* Crypto currencies supported by the coingecko api that are currently of interest for us.
* @enum { 'CoinGecko' }
* Supported API providers.
*/
FiatApi.SupportedCryptoCurrency = {
NIM: /** @type {'nim'} */ ('nim'),
BTC: /** @type {'btc'} */ ('btc'),
FiatApi.SupportedProvider = {
CoinGecko: /** @type {'CoinGecko'} */ ('CoinGecko'),
};

/**
* @readonly
* @enum {'aed' | 'ars' | 'aud' | 'bdt' | 'bhd' | 'bmd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'czk' | 'dkk' | 'eur'
* | 'gbp' | 'gel' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'lkr' | 'mmk' | 'mxn' | 'myr'
* | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb' | 'try' | 'twd' | 'uah'
* | 'usd' | 'vnd' | 'zar'}
* Fiat currencies supported by the coingecko api. Note that coingecko supports more vs_currencies (see
* https://api.coingecko.com/api/v3/simple/supported_vs_currencies) but also includes crypto currencies and ounces of
* gold amongst others that are not fiat currencies. See FiatApi in @nimiq/utils for how this list was assembled.
* @type {FiatApi.SupportedProvider}
* The effective provider used by the FiatApi. As this is not meant to be changeable at runtime, all code for other
* providers can be commented out, to avoid type confusion.
*/
FiatApi.SupportedFiatCurrency = {
AED: /** @type {'aed'} */ ('aed'), // Arab Emirates Dirham
ARS: /** @type {'ars'} */ ('ars'), // Argentine Peso
AUD: /** @type {'aud'} */ ('aud'), // Australian Dollar
BDT: /** @type {'bdt'} */ ('bdt'), // Bangladeshi Taka
BHD: /** @type {'bhd'} */ ('bhd'), // Bahraini Dinar
BMD: /** @type {'bmd'} */ ('bmd'), // Bermudan Dollar
BRL: /** @type {'brl'} */ ('brl'), // Brazilian Real
CAD: /** @type {'cad'} */ ('cad'), // Canadian Dollar
CHF: /** @type {'chf'} */ ('chf'), // Swiss Franc
CLP: /** @type {'clp'} */ ('clp'), // Chilean Peso
CNY: /** @type {'cny'} */ ('cny'), // Chinese Yuan
CZK: /** @type {'czk'} */ ('czk'), // Czech Koruna
DKK: /** @type {'dkk'} */ ('dkk'), // Danish Krone
EUR: /** @type {'eur'} */ ('eur'), // Euro
GBP: /** @type {'gbp'} */ ('gbp'), // British Pound
GEL: /** @type {'gel'} */ ('gel'), // Georgian Lari
HKD: /** @type {'hkd'} */ ('hkd'), // Hong Kong Dollar
HUF: /** @type {'huf'} */ ('huf'), // Hungarian Forint
IDR: /** @type {'idr'} */ ('idr'), // Indonesian Rupiah
ILS: /** @type {'ils'} */ ('ils'), // Israeli New Shekel
INR: /** @type {'inr'} */ ('inr'), // Indian Rupee
JPY: /** @type {'jpy'} */ ('jpy'), // Japanese Yen
KRW: /** @type {'krw'} */ ('krw'), // South Korean Won
KWD: /** @type {'kwd'} */ ('kwd'), // Kuwaiti Dinar
LKR: /** @type {'lkr'} */ ('lkr'), // Sri Lankan Rupee
MMK: /** @type {'mmk'} */ ('mmk'), // Burmese Kyat
MXN: /** @type {'mxn'} */ ('mxn'), // Mexican Peso
MYR: /** @type {'myr'} */ ('myr'), // Malaysian Ringgit
NGN: /** @type {'ngn'} */ ('ngn'), // Nigerian Naira
NOK: /** @type {'nok'} */ ('nok'), // Norwegian Krone
NZD: /** @type {'nzd'} */ ('nzd'), // New Zealand Dollar
PHP: /** @type {'php'} */ ('php'), // Philippine Peso
PKR: /** @type {'pkr'} */ ('pkr'), // Pakistani Rupee
PLN: /** @type {'pln'} */ ('pln'), // Poland Złoty
RUB: /** @type {'rub'} */ ('rub'), // Russian Ruble
SAR: /** @type {'sar'} */ ('sar'), // Saudi Riyal
SEK: /** @type {'sek'} */ ('sek'), // Swedish Krona
SGD: /** @type {'sgd'} */ ('sgd'), // Singapore Dollar
THB: /** @type {'thb'} */ ('thb'), // Thai Baht
TRY: /** @type {'try'} */ ('try'), // Turkish Lira
TWD: /** @type {'twd'} */ ('twd'), // New Taiwan Dollar
UAH: /** @type {'uah'} */ ('uah'), // Ukrainian Hryvnia
USD: /** @type {'usd'} */ ('usd'), // United States Dollar
// VEF: /** @type {'vef'} */ ('vef'), // Discontinued Venezuelan Bolívar Fuerte replaced by VES. Rates are off.
VND: /** @type {'vnd'} */ ('vnd'), // Vietnamese Đồng
ZAR: /** @type {'zar'} */ ('zar'), // South African Rand
};
FiatApi.Provider = FiatApi.SupportedProvider.CoinGecko;

/**
* @readonly
* Coingecko api url. Note that the origin must be whitelisted in the csp.
*/
FiatApi.API_URL = 'https://api.coingecko.com/api/v3';
if (FiatApi.Provider === FiatApi.SupportedProvider.CoinGecko) {
/**
* @readonly
* @enum { 'nim' | 'btc' }
* Crypto currencies supported by the coingecko api that are currently of interest to us.
*/
FiatApi.SupportedCryptoCurrency = {
NIM: /** @type {'nim'} */ ('nim'),
BTC: /** @type {'btc'} */ ('btc'),
};

/**
* @readonly
* Crypto currency tickers mapped to coingecko coin ids.
*/
FiatApi.COINGECKO_COIN_IDS = {
[FiatApi.SupportedCryptoCurrency.NIM]: 'nimiq-2',
[FiatApi.SupportedCryptoCurrency.BTC]: 'bitcoin',
};
/**
* @readonly
* @enum {'aed' | 'ars' | 'aud' | 'bdt' | 'bhd' | 'bmd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'czk' | 'dkk'
* | 'eur' | 'gbp' | 'gel' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'lkr' | 'mmk'
* | 'mxn' | 'myr' | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb'
* | 'try' | 'twd' | 'uah' | 'usd' | 'vnd' | 'zar'}
* Fiat currencies supported by the coingecko api. Note that coingecko supports more vs_currencies (see
* https://api.coingecko.com/api/v3/simple/supported_vs_currencies) but also includes crypto currencies and ounces
* of gold amongst others that are not fiat currencies. See FiatApi in @nimiq/utils for how this list was assembled.
*/
FiatApi.SupportedFiatCurrency = {
AED: /** @type {'aed'} */ ('aed'), // Arab Emirates Dirham
ARS: /** @type {'ars'} */ ('ars'), // Argentine Peso
AUD: /** @type {'aud'} */ ('aud'), // Australian Dollar
BDT: /** @type {'bdt'} */ ('bdt'), // Bangladeshi Taka
BHD: /** @type {'bhd'} */ ('bhd'), // Bahraini Dinar
BMD: /** @type {'bmd'} */ ('bmd'), // Bermudan Dollar
BRL: /** @type {'brl'} */ ('brl'), // Brazilian Real
CAD: /** @type {'cad'} */ ('cad'), // Canadian Dollar
CHF: /** @type {'chf'} */ ('chf'), // Swiss Franc
CLP: /** @type {'clp'} */ ('clp'), // Chilean Peso
CNY: /** @type {'cny'} */ ('cny'), // Chinese Yuan
CZK: /** @type {'czk'} */ ('czk'), // Czech Koruna
DKK: /** @type {'dkk'} */ ('dkk'), // Danish Krone
EUR: /** @type {'eur'} */ ('eur'), // Euro
GBP: /** @type {'gbp'} */ ('gbp'), // British Pound
GEL: /** @type {'gel'} */ ('gel'), // Georgian Lari
HKD: /** @type {'hkd'} */ ('hkd'), // Hong Kong Dollar
HUF: /** @type {'huf'} */ ('huf'), // Hungarian Forint
IDR: /** @type {'idr'} */ ('idr'), // Indonesian Rupiah
ILS: /** @type {'ils'} */ ('ils'), // Israeli New Shekel
INR: /** @type {'inr'} */ ('inr'), // Indian Rupee
JPY: /** @type {'jpy'} */ ('jpy'), // Japanese Yen
KRW: /** @type {'krw'} */ ('krw'), // South Korean Won
KWD: /** @type {'kwd'} */ ('kwd'), // Kuwaiti Dinar
LKR: /** @type {'lkr'} */ ('lkr'), // Sri Lankan Rupee
MMK: /** @type {'mmk'} */ ('mmk'), // Burmese Kyat
MXN: /** @type {'mxn'} */ ('mxn'), // Mexican Peso
MYR: /** @type {'myr'} */ ('myr'), // Malaysian Ringgit
NGN: /** @type {'ngn'} */ ('ngn'), // Nigerian Naira
NOK: /** @type {'nok'} */ ('nok'), // Norwegian Krone
NZD: /** @type {'nzd'} */ ('nzd'), // New Zealand Dollar
PHP: /** @type {'php'} */ ('php'), // Philippine Peso
PKR: /** @type {'pkr'} */ ('pkr'), // Pakistani Rupee
PLN: /** @type {'pln'} */ ('pln'), // Poland Złoty
RUB: /** @type {'rub'} */ ('rub'), // Russian Ruble
SAR: /** @type {'sar'} */ ('sar'), // Saudi Riyal
SEK: /** @type {'sek'} */ ('sek'), // Swedish Krona
SGD: /** @type {'sgd'} */ ('sgd'), // Singapore Dollar
THB: /** @type {'thb'} */ ('thb'), // Thai Baht
TRY: /** @type {'try'} */ ('try'), // Turkish Lira
TWD: /** @type {'twd'} */ ('twd'), // New Taiwan Dollar
UAH: /** @type {'uah'} */ ('uah'), // Ukrainian Hryvnia
USD: /** @type {'usd'} */ ('usd'), // United States Dollar
// VEF: /** @type {'vef'} */ ('vef'), // Discontinued Venezuelan Bolívar Fuerte replaced by VES. Rates are off.
VND: /** @type {'vnd'} */ ('vnd'), // Vietnamese Đồng
ZAR: /** @type {'zar'} */ ('zar'), // South African Rand
};

/**
* @readonly
* Coingecko api url. Note that the origin must be whitelisted in the csp.
*/
FiatApi.API_URL = 'https://api.coingecko.com/api/v3';

/**
* @readonly
* Crypto currency tickers mapped to coingecko coin ids.
*/
FiatApi.COINGECKO_COIN_IDS = {
[FiatApi.SupportedCryptoCurrency.NIM]: 'nimiq-2',
[FiatApi.SupportedCryptoCurrency.BTC]: 'bitcoin',
};
} else {
throw new Error(`FiatApi.Provider ${FiatApi.Provider} not supported yet.`);
}
6 changes: 3 additions & 3 deletions src/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"payment-info-line-free-service": "Nimiq bietet diesen Service kostenfrei an.",
"payment-info-line-total": "Gesamt",
"payment-info-line-network-fee": "Netzwerkgebühr",
"payment-info-line-paying-more": "Du zahlst etwa %RATE_DEVIATION% mehr als zum aktuellen Wechselkurs (coingecko.com).",
"payment-info-line-paying-less": "Du zahlst etwa %RATE_DEVIATION% weniger als zum aktuellen Wechselkurs (coingecko.com).",
"payment-info-line-actual-discount": "Dein tatsächlicher Rabatt beträgt etwa %RATE_DEVIATION% zum aktuellen Wechselkurs (coingecko.com).",
"payment-info-line-paying-more": "Du zahlst etwa {formattedRateDeviation} mehr als zum aktuellen Wechselkurs ({provider}).",
"payment-info-line-paying-less": "Du zahlst etwa {formattedRateDeviation} weniger als zum aktuellen Wechselkurs ({provider}).",
"payment-info-line-actual-discount": "Dein tatsächlicher Rabatt beträgt etwa {formattedRateDeviation} zum aktuellen Wechselkurs ({provider}).",

"timer-expiry": "Angebot endet in",
"timer-second": "Sekunde",
Expand Down
6 changes: 3 additions & 3 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"payment-info-line-free-service": "Nimiq provides this service free of charge.",
"payment-info-line-total": "Total",
"payment-info-line-network-fee": "network fee",
"payment-info-line-paying-more": "You are paying approx. %RATE_DEVIATION% more than at the current market rate (coingecko.com).",
"payment-info-line-paying-less": "You are paying approx. %RATE_DEVIATION% less than at the current market rate (coingecko.com).",
"payment-info-line-actual-discount": "Your actual discount is approx. %RATE_DEVIATION% compared to the current market rate (coingecko.com).",
"payment-info-line-paying-more": "You are paying approx. {formattedRateDeviation} more than at the current market rate ({provider}).",
"payment-info-line-paying-less": "You are paying approx. {formattedRateDeviation} less than at the current market rate ({provider}).",
"payment-info-line-actual-discount": "Your actual discount is approx. {formattedRateDeviation} compared to the current market rate ({provider}).",

"timer-expiry": "This offer expires in",
"timer-second": "second",
Expand Down
6 changes: 3 additions & 3 deletions src/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"payment-info-line-free-service": "Nimiq provee este servicio libre de costo",
"payment-info-line-total": "Total",
"payment-info-line-network-fee": "cuota de red",
"payment-info-line-paying-more": "Usted esta pagando aproximadamente. %RATE_DEVIATION% más que la taza del mercado actual (coingecko.com).",
"payment-info-line-paying-less": "Usted esta pagando aproximadamente. %RATE_DEVIATION% menos que la taza del mercado actual (coingecko.com).",
"payment-info-line-actual-discount": "Su descuento actual es aproximadamente %RATE_DEVIATION% en comparación de la taza del mercado actual (coingecko.com).",
"payment-info-line-paying-more": "Usted esta pagando aproximadamente. {formattedRateDeviation} más que la taza del mercado actual ({provider}).",
"payment-info-line-paying-less": "Usted esta pagando aproximadamente. {formattedRateDeviation} menos que la taza del mercado actual ({provider}).",
"payment-info-line-actual-discount": "Su descuento actual es aproximadamente {formattedRateDeviation} en comparación de la taza del mercado actual ({provider}).",

"timer-expiry": "La oferta expira en",
"timer-second": "segundo",
Expand Down
6 changes: 3 additions & 3 deletions src/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"payment-info-line-free-service": "Nimiq fournit ce service gratuitement.",
"payment-info-line-total": "Total",
"payment-info-line-network-fee": "frais du réseau",
"payment-info-line-paying-more": "Vous payez env. %RATE_DEVIATION% de plus que le taux actuel du marché (coingecko.com).",
"payment-info-line-paying-less": "Vous payez env. %RATE_DEVIATION% de moins que le taux actuel du marché (coingecko.com).",
"payment-info-line-actual-discount": "Votre remise réelle est d'env. %RATE_DEVIATION% par rapport au taux actuel du marché (coingecko.com).",
"payment-info-line-paying-more": "Vous payez env. {formattedRateDeviation} de plus que le taux actuel du marché ({provider}).",
"payment-info-line-paying-less": "Vous payez env. {formattedRateDeviation} de moins que le taux actuel du marché ({provider}).",
"payment-info-line-actual-discount": "Votre remise réelle est d'env. {formattedRateDeviation} par rapport au taux actuel du marché ({provider}).",

"timer-expiry": "Cette offre expire dans",
"timer-second": "seconde",
Expand Down
6 changes: 3 additions & 3 deletions src/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"payment-info-line-free-service": "Nimiq biedt deze service gratis aan.",
"payment-info-line-total": "Totaal",
"payment-info-line-network-fee": "netwerkkosten",
"payment-info-line-paying-more": "U betaalt ongeveer %RATE_DEVIATION% meer dan de huidige marktkoers (coingecko.com).",
"payment-info-line-paying-less": "Je betaalt ongeveer %RATE_DEVIATION% minder dan het huidige markttarief (coingecko.com).",
"payment-info-line-actual-discount": "Je daadwerkelijke korting is ca. %RATE_DEVIATION% in vergelijking met het huidige markttarief (coingecko.com).",
"payment-info-line-paying-more": "U betaalt ongeveer {formattedRateDeviation} meer dan de huidige marktkoers ({provider}).",
"payment-info-line-paying-less": "Je betaalt ongeveer {formattedRateDeviation} minder dan het huidige markttarief ({provider}).",
"payment-info-line-actual-discount": "Je daadwerkelijke korting is ca. {formattedRateDeviation} in vergelijking met het huidige markttarief ({provider}).",

"timer-expiry": "Deze aanbieding vervalt over",
"timer-second": "seconde",
Expand Down
6 changes: 3 additions & 3 deletions src/translations/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@
"payment-info-line-free-service": "A Nimiq oferece este serviço.",
"payment-info-line-total": "Total",
"payment-info-line-network-fee": "taxa de rede",
"payment-info-line-paying-more": "Está a pagar aproximadamente %RATE_DEVIATION% a mais do que a taxa atual de mercado (coingecko.com).",
"payment-info-line-paying-less": "Está a pagar aproximadamente %RATE_DEVIATION% a menos do que a taxa atual de mercado (coingecko.com).",
"payment-info-line-actual-discount": "O seu desconto atual é aproximadamente de %RATE_DEVIATION% comparado com a taxa atual de mercado (coingecko.com).",
"payment-info-line-paying-more": "Está a pagar aproximadamente {formattedRateDeviation} a mais do que a taxa atual de mercado ({provider}).",
"payment-info-line-paying-less": "Está a pagar aproximadamente {formattedRateDeviation} a menos do que a taxa atual de mercado ({provider}).",
"payment-info-line-actual-discount": "O seu desconto atual é aproximadamente de {formattedRateDeviation} comparado com a taxa atual de mercado ({provider}).",

"timer-expiry": "Esta oferta expira em",
"timer-second": "segundo",
Expand Down
Loading

0 comments on commit dcbd088

Please sign in to comment.