Files

777 lines
22 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { action, observable, runInAction } from 'mobx';
import ReactNativeBlobUtil from 'react-native-blob-util';
import BigNumber from 'bignumber.js';
import SettingsStore from './SettingsStore';
import {
SATS_PER_BTC,
numberWithCommas,
numberWithDecimals
} from '../utils/UnitsUtils';
interface CurrencyDisplayRules {
symbol: string;
space: boolean;
rtl: boolean;
separatorSwap: boolean;
decimalPlaces?: number;
}
export default class FiatStore {
@observable public fiatRates:
| {
name?: string;
cryptoCode: string;
currencyPair: string;
code: string;
rate: number;
}[]
| undefined;
@observable public loading = false;
@observable public error = false;
private sourceOfCurrentFiatRates: string | undefined;
getFiatRatesToken: any;
settingsStore: SettingsStore;
constructor(settingsStore: SettingsStore) {
this.settingsStore = settingsStore;
this.getFiatRates();
}
// Resource below may be helpful for formatting
// https://fastspring.com/blog/how-to-format-30-currencies-from-countries-all-over-the-world/
@observable public symbolLookup = (
symbol: string
): CurrencyDisplayRules => {
const symbolPairs: any = {
AED: {
symbol: 'د.إ',
space: false,
rtl: true,
separatorSwap: false
},
ALL: {
symbol: 'L',
space: true,
rtl: true,
separatorSwap: true
},
ANG: {
symbol: 'f',
space: false,
rtl: false,
separatorSwap: true
},
AOA: {
symbol: 'Kz',
space: true,
rtl: false,
separatorSwap: true
},
ARS: {
symbol: '$',
space: true,
rtl: false,
separatorSwap: true
},
AUD: {
symbol: '$',
space: true,
rtl: false,
separatorSwap: false
},
BDT: {
symbol: '৳',
space: true,
rtl: false,
separatorSwap: false
},
BGN: {
symbol: 'лв',
space: true,
rtl: false,
separatorSwap: true
},
BHD: {
symbol: 'BD',
space: true,
rtl: false,
separatorSwap: true,
decimalPlaces: 3
},
BIF: {
symbol: 'FBu',
space: true,
rtl: true,
separatorSwap: true,
decimalPlaces: 0
},
BMD: {
symbol: 'BD$',
space: false,
rtl: false,
separatorSwap: false
},
BOB: {
symbol: 'Bs',
space: false,
rtl: false,
separatorSwap: false
},
BRL: {
symbol: 'R$',
space: true,
rtl: false,
separatorSwap: false
},
BWP: {
symbol: 'P',
space: true,
rtl: false,
separatorSwap: false
},
BZD: {
symbol: 'BZ$',
space: false,
rtl: false,
separatorSwap: false
},
CAD: { symbol: '$', space: true, rtl: false, separatorSwap: false },
CDF: {
symbol: 'FC',
space: true,
rtl: false,
separatorSwap: false
},
CHF: {
symbol: 'fr.',
space: true,
rtl: false,
separatorSwap: false
},
CLP: {
symbol: '$',
space: true,
rtl: false,
separatorSwap: false,
decimalPlaces: 0
},
CNY: { symbol: '¥', space: true, rtl: false, separatorSwap: false },
COP: { symbol: '$', space: true, rtl: false, separatorSwap: false },
CRC: {
symbol: '₡',
space: false,
rtl: false,
separatorSwap: false
},
CUP: {
symbol: '$',
space: false,
rtl: false,
separatorSwap: false
},
CZK: {
symbol: 'Kč',
space: true,
rtl: true,
separatorSwap: true
},
DJF: {
symbol: 'Fdj',
space: true,
rtl: true,
separatorSwap: true,
decimalPlaces: 0
},
DKK: {
symbol: 'kr.',
space: true,
rtl: false,
separatorSwap: true
},
DOP: {
symbol: 'RD$',
space: false,
rtl: false,
separatorSwap: false
},
DZD: {
symbol: 'دج',
space: true,
rtl: true,
separatorSwap: true
},
EGP: {
symbol: '.ج.م',
space: true,
rtl: true,
separatorSwap: false
},
ETB: {
symbol: 'Br',
space: true,
rtl: true,
separatorSwap: false
},
EUR: {
symbol: '€',
space: false,
rtl: false,
separatorSwap: true
},
GBP: {
symbol: '£',
space: false,
rtl: false,
separatorSwap: false
},
GEL: {
symbol: '₾',
space: true,
rtl: true,
separatorSwap: true
},
GHS: {
symbol: 'GH₵',
space: false,
rtl: false,
separatorSwap: false
},
GNF: {
symbol: 'FG',
space: true,
rtl: true,
separatorSwap: true,
decimalPlaces: 0
},
GTQ: {
symbol: 'Q',
space: false,
rtl: false,
separatorSwap: false
},
HKD: {
symbol: 'HK$',
space: true,
rtl: false,
separatorSwap: false
},
HNL: {
symbol: 'L',
space: false,
rtl: false,
separatorSwap: false
},
HUF: {
symbol: 'Ft',
space: true,
rtl: true,
separatorSwap: true
},
IDR: {
symbol: 'Rp',
space: false,
rtl: false,
separatorSwap: false
},
ILS: { symbol: '₪', space: true, rtl: false, separatorSwap: false },
INR: { symbol: '₹', space: true, rtl: false, separatorSwap: false },
IRR: {
symbol: '﷼',
space: true,
rtl: true,
separatorSwap: false
},
ISK: {
symbol: 'kr',
space: true,
rtl: true,
separatorSwap: true,
decimalPlaces: 0
},
JMD: {
symbol: '$',
space: false,
rtl: false,
separatorSwap: false
},
JOD: {
symbol: 'JD',
space: false,
rtl: false,
separatorSwap: false,
decimalPlaces: 3
},
JPY: {
symbol: '¥',
space: true,
rtl: false,
separatorSwap: false,
decimalPlaces: 0
},
KES: {
symbol: '/=',
space: false,
rtl: true,
separatorSwap: false
},
KGS: {
symbol: 'сом',
space: true,
rtl: true,
separatorSwap: true
},
KRW: {
symbol: '₩',
space: true,
rtl: false,
separatorSwap: false,
decimalPlaces: 0
},
// KWD: { symbol: 'د.ك', space: true, rtl: true, separatorSwap: false, decimalPlaces: 3 },
KZT: {
symbol: '₸',
space: true,
rtl: true,
separatorSwap: true
},
LBP: {
symbol: 'LL',
space: true,
rtl: false,
separatorSwap: false
},
LKR: {
symbol: 'Rs',
space: false,
rtl: true,
separatorSwap: false
},
LSL: {
symbol: 'L',
space: true,
rtl: false,
separatorSwap: false
},
MAD: {
symbol: 'DH',
space: true,
rtl: true,
separatorSwap: true
},
MGA: {
symbol: 'Ar',
space: true,
rtl: true,
separatorSwap: true
},
MRU: {
symbol: 'UM',
space: true,
rtl: true,
separatorSwap: false
},
MWK: {
symbol: 'MK',
space: true,
rtl: false,
separatorSwap: false
},
MXN: { symbol: '$', space: true, rtl: false, separatorSwap: false },
MYR: {
symbol: 'RM',
space: false,
rtl: false,
separatorSwap: false
},
NAD: {
symbol: 'N$',
space: false,
rtl: false,
separatorSwap: false
},
NGN: {
symbol: '₦',
space: false,
rtl: false,
separatorSwap: false
},
NIO: {
symbol: 'C$',
space: false,
rtl: false,
separatorSwap: false
},
NOK: {
symbol: 'kr',
space: true,
rtl: false,
separatorSwap: false
},
NPR: {
symbol: 'रु',
space: false,
rtl: false,
separatorSwap: false
},
NZD: { symbol: '$', space: true, rtl: false, separatorSwap: false },
OMR: {
symbol: '﷼',
space: true,
rtl: true,
separatorSwap: false,
decimalPlaces: 3
},
PAB: {
symbol: 'B/',
space: false,
rtl: false,
separatorSwap: false
},
PEN: {
symbol: 'S/',
space: false,
rtl: false,
separatorSwap: false
},
PHP: { symbol: '₱', space: true, rtl: false, separatorSwap: false },
PKR: {
symbol: 'Rs',
space: true,
rtl: true,
separatorSwap: false
},
PLN: { symbol: 'zł', space: true, rtl: true, separatorSwap: false },
PYG: {
symbol: '₲',
space: false,
rtl: false,
separatorSwap: true,
decimalPlaces: 0
},
QAR: {
symbol: 'QR',
space: true,
rtl: false,
separatorSwap: false
},
RON: {
symbol: 'lei',
space: true,
rtl: true,
separatorSwap: false
},
RSD: {
symbol: 'дин',
space: true,
rtl: true,
separatorSwap: true
},
RUB: {
symbol: 'p.',
space: true,
rtl: true,
separatorSwap: true
},
RWF: {
symbol: 'FRw',
space: true,
rtl: true,
separatorSwap: false,
decimalPlaces: 0
},
SAR: { symbol: '﷼', space: true, rtl: true, separatorSwap: false },
SEK: {
symbol: 'kr',
space: true,
rtl: true,
separatorSwap: true
},
SGD: {
symbol: '$',
space: false,
rtl: false,
separatorSwap: false
},
SZL: {
symbol: 'E',
space: true,
rtl: false,
separatorSwap: false
},
THB: { symbol: '฿', space: true, rtl: true, separatorSwap: false },
TND: {
symbol: 'د.ت',
space: false,
rtl: true,
separatorSwap: false,
decimalPlaces: 3
},
TRY: { symbol: '₺', space: true, rtl: true, separatorSwap: false },
TTD: {
symbol: 'TT$',
space: false,
rtl: false,
separatorSwap: false
},
TWD: {
symbol: '元',
space: true,
rtl: false,
separatorSwap: false
},
TZS: {
symbol: '/=',
space: false,
rtl: true,
separatorSwap: false
},
UAH: { symbol: '₴', space: false, rtl: false, separatorSwap: true },
UGX: {
symbol: '/=',
space: false,
rtl: true,
separatorSwap: false,
decimalPlaces: 0
},
USD: {
symbol: '$',
space: false,
rtl: false,
separatorSwap: false
},
UYU: {
symbol: 'U$',
space: false,
rtl: false,
separatorSwap: false
},
UZS: {
symbol: 'soʻm',
space: true,
rtl: true,
separatorSwap: false
},
VES: {
symbol: 'Bs.',
space: true,
rtl: false,
separatorSwap: false
},
VND: {
symbol: '₫',
space: true,
rtl: true,
separatorSwap: true,
decimalPlaces: 0
},
XAF: {
symbol: 'F.CFA',
space: true,
rtl: false,
separatorSwap: false,
decimalPlaces: 0
},
XAG: {
symbol: 'Ag oz',
space: true,
rtl: true,
separatorSwap: false
},
XAU: {
symbol: 'Au oz',
space: true,
rtl: true,
separatorSwap: false
},
XOF: {
symbol: 'F.CFA',
space: true,
rtl: false,
separatorSwap: false,
decimalPlaces: 0
},
ZAR: {
symbol: 'R',
space: true,
rtl: false,
separatorSwap: false
},
ZMW: {
symbol: 'K',
space: false,
rtl: false,
separatorSwap: false
}
};
if (symbol in symbolPairs) {
return symbolPairs[symbol];
} else {
return {
symbol: symbol || 'N/A',
space: true,
rtl: false,
separatorSwap: false
};
}
};
public getSymbol = () => {
const { settings } = this.settingsStore;
const { fiat } = settings;
if (fiat) {
return this.symbolLookup(fiat);
} else {
return {
symbol: fiat,
space: true,
rtl: true,
separatorSwap: false,
decimalPlaces: 2
};
}
};
public getRate = (sats: boolean = false) => {
const { settings } = this.settingsStore;
const { fiat } = settings;
if (fiat && this.fiatRates) {
const fiatEntry = this.fiatRates.filter(
(entry) => entry.code === fiat
)[0];
const rate = (fiatEntry && fiatEntry.rate) || 0;
const { symbol, space, rtl, separatorSwap } = this.symbolLookup(
fiatEntry && fiatEntry.code
);
const moscowTime = new BigNumber(1)
.div(rate)
.multipliedBy(SATS_PER_BTC)
.toFixed(0);
const formattedRate = separatorSwap
? numberWithDecimals(rate)
: numberWithCommas(rate);
const formattedMoscow = separatorSwap
? numberWithDecimals(moscowTime)
: numberWithCommas(moscowTime);
if (sats) {
return `${formattedMoscow} sats = 1 ${fiat}`;
}
if (rtl) {
return `${formattedRate}${
space ? ' ' : ''
}${symbol} BTC/${fiat}`;
} else {
return `${symbol}${
space ? ' ' : ''
}${formattedRate} BTC/${fiat}`;
}
}
return '$N/A';
};
// as of December 8, 2025
// BTCPAY rates string:
// BTC_USD,BTC_AUD,BTC_BRL,BTC_CAD,BTC_CHF,BTC_CLP,BTC_CNY,BTC_CZK,BTC_DKK,BTC_EUR,BTC_GBP,BTC_HKD,BTC_HUF,BTC_INR,BTC_ISK,BTC_JPY,BTC_KRW,BTC_NZD,BTC_PLN,BTC_RON,BTC_RUB,BTC_SEK,BTC_SGD,BTC_THB,BTC_TRY,BTC_TWD,BTC_ILS,BTC_ARS,BTC_NGN,BTC_LBP,BTC_MYR,BTC_UAH,BTC_JMD,BTC_COP,BTC_MXN,BTC_VES,BTC_TZS,BTC_QAR,BTC_TND,BTC_NOK,BTC_AED,BTC_TTD,BTC_PHP,BTC_CDF,BTC_XAF,BTC_KES,BTC_UGX,BTC_ZAR,BTC_CUP,BTC_DOP,BTC_BZD,BTC_BOB,BTC_CRC,BTC_GTQ,BTC_NIO,BTC_PYG,BTC_UYU,BTC_MRU,BTC_ALL,BTC_ANG,BTC_AOA,BTC_BDT,BTC_BGN,BTC_BHD,BTC_BIF,BTC_BMD,BTC_BWP,BTC_DJF,BTC_DZD,BTC_EGP,BTC_ETB,BTC_GEL,BTC_GHS,BTC_GNF,BTC_HNL,BTC_IRR,BTC_JOD,BTC_KGS,BTC_KZT,BTC_LKR,BTC_MAD,BTC_MGA,BTC_NAD,BTC_NPR,BTC_PAB,BTC_PEN,BTC_PKR,BTC_RSD,BTC_RWF,BTC_UZS,BTC_VND,BTC_XAG,BTC_XAU,BTC_ZMW,BTC_SAR,BTC_OMR,BTC_XOF,BTC_MWK,BTC_LSL,BTC_SZL
@action
public getFiatRates = async () => {
// try not to slam endpoint
if (this.loading) return;
this.loading = true;
try {
const settings = await this.settingsStore.getSettings();
if (
this.fiatRates != null &&
this.sourceOfCurrentFiatRates != settings.fiatRatesSource
) {
// clear rates to display loading indicator after rates source switch
this.fiatRates = undefined;
}
if (settings.fiatRatesSource.toLowerCase() === 'zeus') {
this.fiatRates = await this.getFiatRatesFromZeus();
} else if (settings.fiat != null) {
const rate = await this.getSelectedFiatRateFromYadio(
settings.fiat
);
runInAction(() => {
if (this.fiatRates) {
this.fiatRates = this.fiatRates.filter(
(r) => r.code !== settings.fiat
);
if (rate != null) {
this.fiatRates = this.fiatRates.concat([rate]);
}
} else if (rate) {
this.fiatRates = [rate];
}
});
}
this.sourceOfCurrentFiatRates = settings.fiatRatesSource;
} finally {
this.loading = false;
}
};
public formatAmountForDisplay = (input: string | number) => {
const { symbol, space, rtl, separatorSwap } = this.getSymbol();
const amount = separatorSwap
? numberWithDecimals(input)
: numberWithCommas(input);
if (rtl) return `${amount}${space ? ' ' : ''}${symbol}`;
return `${symbol}${space ? ' ' : ''}${amount}`;
};
private getSelectedFiatRateFromYadio = async (code: string) => {
try {
const response = await ReactNativeBlobUtil.fetch(
'GET',
`https://api.yadio.io/rate/${code}/BTC`
);
const status = response.info().status;
if (status == 200) {
return {
cryptoCode: 'BTC',
code,
rate: response.json().rate,
currencyPair: `BTC_${code}`
};
}
} catch (error) {
console.error('Error fetching fiat rates from yadio', error);
}
return undefined;
};
private getFiatRatesFromZeus = async () => {
try {
const response = await ReactNativeBlobUtil.fetch(
'GET',
'https://pay.zeusln.app/api/rates?storeId=Fjt7gLnGpg4UeBMFccLquy3GTTEz4cHU4PZMU63zqMBo'
);
const status = response.info().status;
if (status == 200) {
return response.json();
}
} catch (error) {
console.error('Error fetching fiat rates from zeus', error);
}
return undefined;
};
}