REF: Currency module to TS (#6052)

This commit is contained in:
Marcos Rodriguez Vélez 2024-01-28 11:11:08 -04:00 committed by GitHub
parent e4adb0f742
commit bec42fe42d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 392 additions and 402 deletions

4
App.js
View file

@ -33,8 +33,8 @@ import HandoffComponent from './components/handoff';
import Privacy from './blue_modules/Privacy';
import triggerHapticFeedback, { HapticFeedbackTypes } from './blue_modules/hapticFeedback';
import MenuElements from './components/MenuElements';
import { updateExchangeRate } from './blue_modules/currency';
const A = require('./blue_modules/analytics');
const currency = require('./blue_modules/currency');
const eventEmitter = Platform.OS === 'ios' ? new NativeEventEmitter(NativeModules.EventEmitter) : undefined;
const { EventEmitter, SplashScreen } = NativeModules;
@ -203,7 +203,7 @@ const App = () => {
if (wallets.length === 0) return;
if ((appState.current.match(/background/) && nextAppState === 'active') || nextAppState === undefined) {
setTimeout(() => A(A.ENUM.APP_UNSUSPENDED), 2000);
currency.updateExchangeRate();
updateExchangeRate();
const processed = await processPushNotifications();
if (processed) return;
const clipboard = await BlueClipboard().getClipboardContent();

View file

@ -25,6 +25,7 @@ import {
} from './class/';
import { randomBytes } from './class/rng';
import alert from './components/Alert';
import { initCurrencyDaemon } from './blue_modules/currency';
const encryption = require('./blue_modules/encryption');
const Realm = require('realm');
@ -32,7 +33,6 @@ const createHash = require('create-hash');
let usedBucketNum = false;
let savingInProgress = 0; // its both a flag and a counter of attempts to write to disk
const prompt = require('./helpers/prompt');
const currency = require('./blue_modules/currency');
const BlueElectrum = require('./blue_modules/BlueElectrum');
BlueElectrum.connectMain();
@ -939,6 +939,6 @@ const startAndDecrypt = async retry => {
BlueApp.startAndDecrypt = startAndDecrypt;
BlueApp.AppStorage = AppStorage;
currency.init();
initCurrencyDaemon();
module.exports = BlueApp;

View file

@ -1,261 +0,0 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import DefaultPreference from 'react-native-default-preference';
import * as RNLocalize from 'react-native-localize';
import BigNumber from 'bignumber.js';
import { FiatUnit, getFiatRate } from '../models/fiatUnit';
import WidgetCommunication from './WidgetCommunication';
const PREFERRED_CURRENCY_STORAGE_KEY = 'preferredCurrency';
const EXCHANGE_RATES_STORAGE_KEY = 'currency';
let preferredFiatCurrency = FiatUnit.USD;
let exchangeRates = { LAST_UPDATED_ERROR: false };
let lastTimeUpdateExchangeRateWasCalled = 0;
let skipUpdateExchangeRate = false;
const LAST_UPDATED = 'LAST_UPDATED';
/**
* Saves to storage preferred currency, whole object
* from `./models/fiatUnit`
*
* @param item {Object} one of the values in `./models/fiatUnit`
* @returns {Promise<void>}
*/
async function setPrefferedCurrency(item) {
await AsyncStorage.setItem(PREFERRED_CURRENCY_STORAGE_KEY, JSON.stringify(item));
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
await DefaultPreference.set('preferredCurrency', item.endPointKey);
await DefaultPreference.set('preferredCurrencyLocale', item.locale.replace('-', '_'));
WidgetCommunication.reloadAllTimelines();
}
async function getPreferredCurrency() {
const preferredCurrency = await JSON.parse(await AsyncStorage.getItem(PREFERRED_CURRENCY_STORAGE_KEY));
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
await DefaultPreference.set('preferredCurrency', preferredCurrency.endPointKey);
await DefaultPreference.set('preferredCurrencyLocale', preferredCurrency.locale.replace('-', '_'));
return preferredCurrency;
}
async function _restoreSavedExchangeRatesFromStorage() {
try {
exchangeRates = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
if (!exchangeRates) exchangeRates = { LAST_UPDATED_ERROR: false };
} catch (_) {
exchangeRates = { LAST_UPDATED_ERROR: false };
}
}
async function _restoreSavedPreferredFiatCurrencyFromStorage() {
try {
preferredFiatCurrency = JSON.parse(await AsyncStorage.getItem(PREFERRED_CURRENCY_STORAGE_KEY));
if (preferredFiatCurrency === null) {
throw Error('No Preferred Fiat selected');
}
preferredFiatCurrency = FiatUnit[preferredFiatCurrency.endPointKey] || preferredFiatCurrency;
// ^^^ in case configuration in json file changed (and is different from what we stored) we reload it
} catch (_) {
const deviceCurrencies = RNLocalize.getCurrencies();
if (Object.keys(FiatUnit).some(unit => unit === deviceCurrencies[0])) {
preferredFiatCurrency = FiatUnit[deviceCurrencies[0]];
} else {
preferredFiatCurrency = FiatUnit.USD;
}
}
}
/**
* actual function to reach api and get fresh currency exchange rate. checks LAST_UPDATED time and skips entirely
* if called too soon (30min); saves exchange rate (with LAST_UPDATED info) to storage.
* should be called when app thinks its a good time to refresh exchange rate
*
* @return {Promise<void>}
*/
async function updateExchangeRate() {
if (skipUpdateExchangeRate) return;
if (+new Date() - lastTimeUpdateExchangeRateWasCalled <= 10 * 1000) {
// simple debounce so theres no race conditions
return;
}
lastTimeUpdateExchangeRateWasCalled = +new Date();
if (+new Date() - exchangeRates[LAST_UPDATED] <= 30 * 60 * 1000) {
// not updating too often
return;
}
console.log('updating exchange rate...');
let rate;
try {
rate = await getFiatRate(preferredFiatCurrency.endPointKey);
exchangeRates[LAST_UPDATED] = +new Date();
exchangeRates['BTC_' + preferredFiatCurrency.endPointKey] = rate;
exchangeRates.LAST_UPDATED_ERROR = false;
await AsyncStorage.setItem(EXCHANGE_RATES_STORAGE_KEY, JSON.stringify(exchangeRates));
} catch (Err) {
console.log('Error encountered when attempting to update exchange rate...');
console.warn(Err.message);
rate = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
rate = rate || {}; // init if its null
rate.LAST_UPDATED_ERROR = true;
exchangeRates.LAST_UPDATED_ERROR = true;
await AsyncStorage.setItem(EXCHANGE_RATES_STORAGE_KEY, JSON.stringify(rate));
throw Err;
}
}
async function isRateOutdated() {
try {
const rate = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
return rate.LAST_UPDATED_ERROR || +new Date() - rate.LAST_UPDATED >= 31 * 60 * 1000;
} catch {
return true;
}
}
/**
* this function reads storage and restores current preferred fiat currency & last saved exchange rate, then calls
* updateExchangeRate() to update rates.
* should be called when the app starts and when user changes preferred fiat (with TRUE argument so underlying
* `updateExchangeRate()` would actually update rates via api).
*
* @param clearLastUpdatedTime {boolean} set to TRUE for the underlying
*
* @return {Promise<void>}
*/
async function init(clearLastUpdatedTime = false) {
await _restoreSavedExchangeRatesFromStorage();
await _restoreSavedPreferredFiatCurrencyFromStorage();
if (clearLastUpdatedTime) {
exchangeRates[LAST_UPDATED] = 0;
lastTimeUpdateExchangeRateWasCalled = 0;
}
return updateExchangeRate();
}
function satoshiToLocalCurrency(satoshi, format = true) {
if (!exchangeRates['BTC_' + preferredFiatCurrency.endPointKey]) {
updateExchangeRate();
return '...';
}
let b = new BigNumber(satoshi).dividedBy(100000000).multipliedBy(exchangeRates['BTC_' + preferredFiatCurrency.endPointKey]);
if (b.isGreaterThanOrEqualTo(0.005) || b.isLessThanOrEqualTo(-0.005)) {
b = b.toFixed(2);
} else {
b = b.toPrecision(2);
}
if (format === false) return b;
let formatter;
try {
formatter = new Intl.NumberFormat(preferredFiatCurrency.locale, {
style: 'currency',
currency: preferredFiatCurrency.endPointKey,
minimumFractionDigits: 2,
maximumFractionDigits: 8,
});
} catch (error) {
console.warn(error);
console.log(error);
formatter = new Intl.NumberFormat(FiatUnit.USD.locale, {
style: 'currency',
currency: preferredFiatCurrency.endPointKey,
minimumFractionDigits: 2,
maximumFractionDigits: 8,
});
}
return formatter.format(b);
}
function BTCToLocalCurrency(bitcoin) {
let sat = new BigNumber(bitcoin);
sat = sat.multipliedBy(100000000).toNumber();
return satoshiToLocalCurrency(sat);
}
async function mostRecentFetchedRate() {
const currencyInformation = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
const formatter = new Intl.NumberFormat(preferredFiatCurrency.locale, {
style: 'currency',
currency: preferredFiatCurrency.endPointKey,
});
return {
LastUpdated: currencyInformation[LAST_UPDATED],
Rate: formatter.format(currencyInformation[`BTC_${preferredFiatCurrency.endPointKey}`]),
};
}
function satoshiToBTC(satoshi) {
let b = new BigNumber(satoshi);
b = b.dividedBy(100000000);
return b.toString(10);
}
function btcToSatoshi(btc) {
return new BigNumber(btc).multipliedBy(100000000).toNumber();
}
function fiatToBTC(fiatFloat) {
let b = new BigNumber(fiatFloat);
b = b.dividedBy(exchangeRates['BTC_' + preferredFiatCurrency.endPointKey]).toFixed(8);
return b;
}
function getCurrencySymbol() {
return preferredFiatCurrency.symbol;
}
/**
* Used to mock data in tests
*
* @param {object} currency, one of FiatUnit.*
*/
function _setPreferredFiatCurrency(currency) {
preferredFiatCurrency = currency;
}
/**
* Used to mock data in tests
*
* @param {string} pair as expected by rest of this module, e.g 'BTC_JPY' or 'BTC_USD'
* @param {number} rate exchange rate
*/
function _setExchangeRate(pair, rate) {
exchangeRates[pair] = rate;
}
/**
* Used in unit tests, so the `currency` module wont launch actual http request
*/
function _setSkipUpdateExchangeRate() {
skipUpdateExchangeRate = true;
}
module.exports.updateExchangeRate = updateExchangeRate;
module.exports.init = init;
module.exports.satoshiToLocalCurrency = satoshiToLocalCurrency;
module.exports.fiatToBTC = fiatToBTC;
module.exports.satoshiToBTC = satoshiToBTC;
module.exports.BTCToLocalCurrency = BTCToLocalCurrency;
module.exports.setPrefferedCurrency = setPrefferedCurrency;
module.exports.getPreferredCurrency = getPreferredCurrency;
module.exports.btcToSatoshi = btcToSatoshi;
module.exports.getCurrencySymbol = getCurrencySymbol;
module.exports._setPreferredFiatCurrency = _setPreferredFiatCurrency; // export it to mock data in tests
module.exports._setExchangeRate = _setExchangeRate; // export it to mock data in tests
module.exports._setSkipUpdateExchangeRate = _setSkipUpdateExchangeRate; // export it to mock data in tests
module.exports.PREFERRED_CURRENCY = PREFERRED_CURRENCY_STORAGE_KEY;
module.exports.EXCHANGE_RATES = EXCHANGE_RATES_STORAGE_KEY;
module.exports.LAST_UPDATED = LAST_UPDATED;
module.exports.mostRecentFetchedRate = mostRecentFetchedRate;
module.exports.isRateOutdated = isRateOutdated;

230
blue_modules/currency.ts Normal file
View file

@ -0,0 +1,230 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import DefaultPreference from 'react-native-default-preference';
import * as RNLocalize from 'react-native-localize';
import BigNumber from 'bignumber.js';
import { FiatUnit, FiatUnitType, getFiatRate } from '../models/fiatUnit';
import WidgetCommunication from './WidgetCommunication';
const PREFERRED_CURRENCY_STORAGE_KEY = 'preferredCurrency';
const EXCHANGE_RATES_STORAGE_KEY = 'exchangeRates';
const LAST_UPDATED = 'LAST_UPDATED';
const GROUP_IO_BLUEWALLET = 'group.io.bluewallet.bluewallet';
const BTC_PREFIX = 'BTC_';
export interface CurrencyRate {
LastUpdated: Date | null;
Rate: number | string | null;
}
interface ExchangeRates {
[key: string]: number | boolean | undefined;
LAST_UPDATED_ERROR: boolean;
}
let preferredFiatCurrency: FiatUnitType = FiatUnit.USD;
let exchangeRates: ExchangeRates = { LAST_UPDATED_ERROR: false };
let lastTimeUpdateExchangeRateWasCalled: number = 0;
let skipUpdateExchangeRate: boolean = false;
async function setPreferredCurrency(item: FiatUnitType): Promise<void> {
await AsyncStorage.setItem(PREFERRED_CURRENCY_STORAGE_KEY, JSON.stringify(item));
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
await DefaultPreference.set(PREFERRED_CURRENCY_STORAGE_KEY, item.endPointKey);
await DefaultPreference.set('preferredCurrencyLocale', item.locale.replace('-', '_'));
// @ts-ignore: Convert to TSX later
WidgetCommunication.reloadAllTimelines();
}
async function getPreferredCurrency(): Promise<FiatUnitType> {
const preferredCurrency = JSON.parse((await AsyncStorage.getItem(PREFERRED_CURRENCY_STORAGE_KEY)) || '{}');
await DefaultPreference.setName(GROUP_IO_BLUEWALLET);
await DefaultPreference.set(PREFERRED_CURRENCY_STORAGE_KEY, preferredCurrency.endPointKey);
await DefaultPreference.set('preferredCurrencyLocale', preferredCurrency.locale.replace('-', '_'));
return preferredCurrency;
}
async function _restoreSavedExchangeRatesFromStorage(): Promise<void> {
try {
const rates = await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY);
exchangeRates = rates ? JSON.parse(rates) : { LAST_UPDATED_ERROR: false };
} catch (_) {
exchangeRates = { LAST_UPDATED_ERROR: false };
}
}
async function _restoreSavedPreferredFiatCurrencyFromStorage(): Promise<void> {
try {
const storedCurrency = await AsyncStorage.getItem(PREFERRED_CURRENCY_STORAGE_KEY);
if (!storedCurrency) throw new Error('No Preferred Fiat selected');
preferredFiatCurrency = JSON.parse(storedCurrency);
if (!FiatUnit[preferredFiatCurrency.endPointKey]) {
throw new Error('Invalid Fiat Unit');
}
} catch (_) {
const deviceCurrencies = RNLocalize.getCurrencies();
preferredFiatCurrency = deviceCurrencies[0] && FiatUnit[deviceCurrencies[0]] ? FiatUnit[deviceCurrencies[0]] : FiatUnit.USD;
}
}
async function updateExchangeRate(): Promise<void> {
if (skipUpdateExchangeRate) return;
if (Date.now() - lastTimeUpdateExchangeRateWasCalled <= 10000) {
// simple debounce so there's no race conditions
return;
}
lastTimeUpdateExchangeRateWasCalled = Date.now();
const lastUpdated = exchangeRates[LAST_UPDATED] as number | undefined;
if (lastUpdated && Date.now() - lastUpdated <= 30 * 60 * 1000) {
// not updating too often
return;
}
console.log('updating exchange rate...');
try {
const rate = await getFiatRate(preferredFiatCurrency.endPointKey);
exchangeRates[LAST_UPDATED] = Date.now();
exchangeRates[BTC_PREFIX + preferredFiatCurrency.endPointKey] = rate;
exchangeRates.LAST_UPDATED_ERROR = false;
await AsyncStorage.setItem(EXCHANGE_RATES_STORAGE_KEY, JSON.stringify(exchangeRates));
} catch (error) {
console.error('Error encountered when attempting to update exchange rate...', error);
const rate = JSON.parse((await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY)) || '{}');
rate.LAST_UPDATED_ERROR = true;
exchangeRates.LAST_UPDATED_ERROR = true;
await AsyncStorage.setItem(EXCHANGE_RATES_STORAGE_KEY, JSON.stringify(rate));
}
}
async function isRateOutdated(): Promise<boolean> {
try {
const rate = JSON.parse((await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY)) || '{}');
return rate.LAST_UPDATED_ERROR || Date.now() - (rate[LAST_UPDATED] || 0) >= 31 * 60 * 1000;
} catch {
return true;
}
}
async function initCurrencyDaemon(clearLastUpdatedTime: boolean = false): Promise<void> {
await _restoreSavedExchangeRatesFromStorage();
await _restoreSavedPreferredFiatCurrencyFromStorage();
if (clearLastUpdatedTime) {
exchangeRates[LAST_UPDATED] = 0;
lastTimeUpdateExchangeRateWasCalled = 0;
}
await updateExchangeRate();
}
function satoshiToLocalCurrency(satoshi: number, format: boolean = true): string {
const exchangeRateKey = BTC_PREFIX + preferredFiatCurrency.endPointKey;
const exchangeRate = exchangeRates[exchangeRateKey];
if (typeof exchangeRate !== 'number') {
updateExchangeRate();
return '...';
}
const btcAmount = new BigNumber(satoshi).dividedBy(100000000);
const convertedAmount = btcAmount.multipliedBy(exchangeRate);
let formattedAmount: string;
if (convertedAmount.isGreaterThanOrEqualTo(0.005) || convertedAmount.isLessThanOrEqualTo(-0.005)) {
formattedAmount = convertedAmount.toFixed(2);
} else {
formattedAmount = convertedAmount.toPrecision(2);
}
if (format === false) return formattedAmount;
try {
const formatter = new Intl.NumberFormat(preferredFiatCurrency.locale, {
style: 'currency',
currency: preferredFiatCurrency.endPointKey,
minimumFractionDigits: 2,
maximumFractionDigits: 8,
});
return formatter.format(Number(formattedAmount));
} catch (error) {
console.warn(error);
return formattedAmount;
}
}
function BTCToLocalCurrency(bitcoin: BigNumber.Value): string {
const sat = new BigNumber(bitcoin).multipliedBy(100000000).toNumber();
return satoshiToLocalCurrency(sat);
}
async function mostRecentFetchedRate(): Promise<CurrencyRate> {
const currencyInformation = JSON.parse((await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY)) || '{}');
const formatter = new Intl.NumberFormat(preferredFiatCurrency.locale, {
style: 'currency',
currency: preferredFiatCurrency.endPointKey,
});
const rate = currencyInformation[BTC_PREFIX + preferredFiatCurrency.endPointKey];
return {
LastUpdated: currencyInformation[LAST_UPDATED],
Rate: rate ? formatter.format(rate) : '...',
};
}
function satoshiToBTC(satoshi: number): string {
return new BigNumber(satoshi).dividedBy(100000000).toString(10);
}
function btcToSatoshi(btc: BigNumber.Value): number {
return new BigNumber(btc).multipliedBy(100000000).toNumber();
}
function fiatToBTC(fiatFloat: number): string {
const exchangeRateKey = BTC_PREFIX + preferredFiatCurrency.endPointKey;
const exchangeRate = exchangeRates[exchangeRateKey];
if (typeof exchangeRate !== 'number') {
throw new Error('Exchange rate not available');
}
const btcAmount = new BigNumber(fiatFloat).dividedBy(exchangeRate);
return btcAmount.toFixed(8);
}
function getCurrencySymbol(): string {
return preferredFiatCurrency.symbol;
}
function _setPreferredFiatCurrency(currency: FiatUnitType): void {
preferredFiatCurrency = currency;
}
function _setExchangeRate(pair: string, rate: number): void {
exchangeRates[pair] = rate;
}
function _setSkipUpdateExchangeRate(): void {
skipUpdateExchangeRate = true;
}
export {
updateExchangeRate,
initCurrencyDaemon,
satoshiToLocalCurrency,
fiatToBTC,
satoshiToBTC,
BTCToLocalCurrency,
setPreferredCurrency,
getPreferredCurrency,
btcToSatoshi,
getCurrencySymbol,
_setPreferredFiatCurrency,
_setExchangeRate,
_setSkipUpdateExchangeRate,
PREFERRED_CURRENCY_STORAGE_KEY,
EXCHANGE_RATES_STORAGE_KEY,
LAST_UPDATED,
mostRecentFetchedRate,
isRateOutdated,
};

View file

@ -7,9 +7,9 @@ import loc, { STORAGE_KEY as LOC_STORAGE_KEY } from '../loc';
import { LegacyWallet, WatchOnlyWallet } from '../class';
import alert from '../components/Alert';
import triggerHapticFeedback, { HapticFeedbackTypes } from './hapticFeedback';
import { PREFERRED_CURRENCY_STORAGE_KEY } from './currency';
const BlueApp = require('../BlueApp');
const BlueElectrum = require('./BlueElectrum');
const currency = require('../blue_modules/currency');
const A = require('../blue_modules/analytics');
const _lastTimeTriedToRefetchWallet = {}; // hashmap of timestamps we _started_ refetching some wallet
@ -23,7 +23,7 @@ export const BlueStorageProvider = ({ children }) => {
const [walletsInitialized, setWalletsInitialized] = useState(false);
const [preferredFiatCurrency, _setPreferredFiatCurrency] = useState(FiatUnit.USD);
const [language, _setLanguage] = useState();
const getPreferredCurrencyAsyncStorage = useAsyncStorage(currency.PREFERRED_CURRENCY).getItem;
const getPreferredCurrencyAsyncStorage = useAsyncStorage(PREFERRED_CURRENCY_STORAGE_KEY).getItem;
const getLanguageAsyncStorage = useAsyncStorage(LOC_STORAGE_KEY).getItem;
const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false);
const [isElectrumDisabled, setIsElectrumDisabled] = useState(true);

View file

@ -10,7 +10,14 @@ import loc, { formatBalanceWithoutSuffix, formatBalancePlain, removeTrailingZero
import { BlueText } from '../BlueComponents';
import dayjs from 'dayjs';
import { useTheme } from './themes';
const currency = require('../blue_modules/currency');
import {
fiatToBTC,
getCurrencySymbol,
isRateOutdated,
mostRecentFetchedRate,
satoshiToBTC,
updateExchangeRate,
} from '../blue_modules/currency';
dayjs.extend(require('dayjs/plugin/localizedFormat'));
class AmountInput extends Component {
@ -57,13 +64,12 @@ class AmountInput extends Component {
}
componentDidMount() {
currency
.mostRecentFetchedRate()
.then(mostRecentFetchedRate => {
this.setState({ mostRecentFetchedRate });
mostRecentFetchedRate()
.then(mostRecentFetchedRateValue => {
this.setState({ mostRecentFetchedRate: mostRecentFetchedRateValue });
})
.finally(() => {
currency.isRateOutdated().then(isRateOutdated => this.setState({ isRateOutdated }));
isRateOutdated().then(isRateOutdatedValue => this.setState({ isRateOutdated: isRateOutdatedValue }));
});
}
@ -86,7 +92,7 @@ class AmountInput extends Component {
sats = amount;
break;
case BitcoinUnit.LOCAL_CURRENCY:
sats = new BigNumber(currency.fiatToBTC(amount)).multipliedBy(100000000).toString();
sats = new BigNumber(fiatToBTC(amount)).multipliedBy(100000000).toString();
break;
}
if (previousUnit === BitcoinUnit.LOCAL_CURRENCY && AmountInput.conversionCache[amount + previousUnit]) {
@ -191,14 +197,14 @@ class AmountInput extends Component {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ isRateBeingUpdated: true }, async () => {
try {
await currency.updateExchangeRate();
currency.mostRecentFetchedRate().then(mostRecentFetchedRate => {
await updateExchangeRate();
mostRecentFetchedRate().then(mostRecentFetchedRateValue => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ mostRecentFetchedRate });
this.setState({ mostRecentFetchedRate: mostRecentFetchedRateValue });
});
} finally {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ isRateBeingUpdated: false, isRateOutdated: await currency.isRateOutdated() });
this.setState({ isRateBeingUpdated: false, isRateOutdated: await isRateOutdated() });
}
});
};
@ -220,11 +226,11 @@ class AmountInput extends Component {
secondaryDisplayCurrency = formatBalanceWithoutSuffix((isNaN(amount) ? 0 : amount).toString(), BitcoinUnit.LOCAL_CURRENCY, false);
break;
case BitcoinUnit.LOCAL_CURRENCY:
secondaryDisplayCurrency = currency.fiatToBTC(parseFloat(isNaN(amount) ? 0 : amount));
secondaryDisplayCurrency = fiatToBTC(parseFloat(isNaN(amount) ? 0 : amount));
if (AmountInput.conversionCache[isNaN(amount) ? 0 : amount + BitcoinUnit.LOCAL_CURRENCY]) {
// cache hit! we reuse old value that supposedly doesn't have rounding errors
const sats = AmountInput.conversionCache[isNaN(amount) ? 0 : amount + BitcoinUnit.LOCAL_CURRENCY];
secondaryDisplayCurrency = currency.satoshiToBTC(sats);
secondaryDisplayCurrency = satoshiToBTC(sats);
}
break;
}
@ -251,7 +257,7 @@ class AmountInput extends Component {
<View style={styles.flex}>
<View style={styles.container}>
{unit === BitcoinUnit.LOCAL_CURRENCY && amount !== BitcoinUnit.MAX && (
<Text style={[styles.localCurrency, stylesHook.localCurrency]}>{currency.getCurrencySymbol() + ' '}</Text>
<Text style={[styles.localCurrency, stylesHook.localCurrency]}>{getCurrencySymbol() + ' '}</Text>
)}
{amount !== BitcoinUnit.MAX ? (
<TextInput

View file

@ -65,7 +65,10 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
}, [wallet, verifyIfWalletAllowsOnchainAddress]);
const handleCopyPress = () => {
Clipboard.setString(formatBalance(wallet.getBalance(), wallet.getPreferredBalanceUnit()).toString());
const value = formatBalance(wallet.getBalance(), wallet.getPreferredBalanceUnit());
if (value) {
Clipboard.setString(value);
}
};
const updateWalletVisibility = (w: AbstractWallet, newHideBalance: boolean) => {
@ -134,8 +137,8 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
const balance = useMemo(() => {
const hideBalance = wallet.hideBalance;
const balanceUnit = wallet.getPreferredBalanceUnit();
const balanceFormatted = formatBalance(wallet.getBalance(), balanceUnit, true).toString();
return !hideBalance && balanceFormatted;
const balanceFormatted = formatBalance(wallet.getBalance(), balanceUnit, true);
return !hideBalance && balanceFormatted?.toString();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wallet.hideBalance, wallet.getPreferredBalanceUnit()]);
@ -211,6 +214,7 @@ const TransactionsNavigationHeader: React.FC<TransactionsNavigationHeaderProps>
) : (
<Text
testID="WalletBalance"
// @ts-ignore: Ugh
key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi
numberOfLines={1}
adjustsFontSizeToFit

View file

@ -9,7 +9,7 @@ import BigNumber from 'bignumber.js';
import { BitcoinUnit } from '../models/bitcoinUnits';
import { AvailableLanguages } from './languages';
import { I18nManager } from 'react-native';
const currency = require('../blue_modules/currency');
import { satoshiToLocalCurrency } from '../blue_modules/currency';
export const STORAGE_KEY = 'lang';
@ -310,7 +310,7 @@ export function formatBalance(balance: number, toUnit: string, withFormatting =
} else if (toUnit === BitcoinUnit.SATS) {
return (withFormatting ? new Intl.NumberFormat().format(balance).toString() : String(balance)) + ' ' + loc.units[BitcoinUnit.SATS];
} else if (toUnit === BitcoinUnit.LOCAL_CURRENCY) {
return currency.satoshiToLocalCurrency(balance);
return satoshiToLocalCurrency(balance);
}
}
@ -332,7 +332,7 @@ export function formatBalanceWithoutSuffix(balance = 0, toUnit: string, withForm
} else if (toUnit === BitcoinUnit.SATS) {
return withFormatting ? new Intl.NumberFormat().format(balance).toString() : String(balance);
} else if (toUnit === BitcoinUnit.LOCAL_CURRENCY) {
return currency.satoshiToLocalCurrency(balance);
return satoshiToLocalCurrency(balance);
}
}
return balance.toString();
@ -349,7 +349,7 @@ export function formatBalanceWithoutSuffix(balance = 0, toUnit: string, withForm
export function formatBalancePlain(balance = 0, toUnit: string, withFormatting = false) {
const newInputValue = formatBalanceWithoutSuffix(balance, toUnit, withFormatting);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return _leaveNumbersAndDots(newInputValue);
return _leaveNumbersAndDots(newInputValue.toString());
}
export function _leaveNumbersAndDots(newInputValue: string) {

View file

@ -18,7 +18,7 @@ import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import SafeArea from '../../components/SafeArea';
const currency = require('../../blue_modules/currency');
import { btcToSatoshi, fiatToBTC } from '../../blue_modules/currency';
type LdkOpenChannelProps = RouteProp<
{
@ -223,11 +223,11 @@ const LdkOpenChannel = (props: any) => {
amountSats = parseInt(fundingAmount.amount, 10);
break;
case BitcoinUnit.BTC:
amountSats = currency.btcToSatoshi(fundingAmount.amount);
amountSats = btcToSatoshi(fundingAmount.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
// also accounting for cached fiat->sat conversion to avoid rounding error
amountSats = currency.btcToSatoshi(currency.fiatToBTC(fundingAmount.amount));
amountSats = btcToSatoshi(fiatToBTC(fundingAmount.amount));
break;
}
setFundingAmount({ amount: fundingAmount.amount, amountSats });
@ -237,10 +237,10 @@ const LdkOpenChannel = (props: any) => {
let amountSats = fundingAmount.amountSats;
switch (unit) {
case BitcoinUnit.BTC:
amountSats = currency.btcToSatoshi(text);
amountSats = btcToSatoshi(text);
break;
case BitcoinUnit.LOCAL_CURRENCY:
amountSats = currency.btcToSatoshi(currency.fiatToBTC(text));
amountSats = btcToSatoshi(fiatToBTC(Number(text)));
break;
case BitcoinUnit.SATS:
amountSats = parseInt(text, 10);

View file

@ -31,7 +31,7 @@ import { requestCameraAuthorization } from '../../helpers/scan-qr';
import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
const currency = require('../../blue_modules/currency');
import { btcToSatoshi, fiatToBTC, satoshiToBTC } from '../../blue_modules/currency';
const LNDCreateInvoice = () => {
const { wallets, saveToDisk, setSelectedWalletID } = useContext(BlueStorageContext);
@ -156,11 +156,11 @@ const LNDCreateInvoice = () => {
invoiceAmount = parseInt(invoiceAmount, 10); // basically nop
break;
case BitcoinUnit.BTC:
invoiceAmount = currency.btcToSatoshi(invoiceAmount);
invoiceAmount = btcToSatoshi(invoiceAmount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
// trying to fetch cached sat equivalent for this fiat amount
invoiceAmount = AmountInput.getCachedSatoshis(invoiceAmount) || currency.btcToSatoshi(currency.fiatToBTC(invoiceAmount));
invoiceAmount = AmountInput.getCachedSatoshis(invoiceAmount) || btcToSatoshi(fiatToBTC(invoiceAmount));
break;
}
@ -285,7 +285,7 @@ const LNDCreateInvoice = () => {
// nop
break;
case BitcoinUnit.BTC:
newAmount = currency.satoshiToBTC(newAmount);
newAmount = satoshiToBTC(newAmount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
newAmount = formatBalancePlain(newAmount, BitcoinUnit.LOCAL_CURRENCY);

View file

@ -17,8 +17,8 @@ import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import SafeArea from '../../components/SafeArea';
import { btcToSatoshi, fiatToBTC, satoshiToBTC, satoshiToLocalCurrency } from '../../blue_modules/currency';
const prompt = require('../../helpers/prompt');
const currency = require('../../blue_modules/currency');
/**
* if user has default currency - fiat, attempting to pay will trigger conversion from entered in input field fiat value
@ -86,10 +86,10 @@ const LnurlPay = () => {
}
switch (unit) {
case BitcoinUnit.BTC:
newAmount = currency.satoshiToBTC(newAmount);
newAmount = satoshiToBTC(newAmount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
newAmount = currency.satoshiToLocalCurrency(newAmount, false);
newAmount = satoshiToLocalCurrency(newAmount, false);
_cacheFiatToSat[newAmount] = originalSatAmount;
break;
}
@ -120,13 +120,13 @@ const LnurlPay = () => {
amountSats = parseInt(amountSats, 10); // nop
break;
case BitcoinUnit.BTC:
amountSats = currency.btcToSatoshi(amountSats);
amountSats = btcToSatoshi(amountSats);
break;
case BitcoinUnit.LOCAL_CURRENCY:
if (_cacheFiatToSat[amount]) {
amountSats = _cacheFiatToSat[amount];
} else {
amountSats = currency.btcToSatoshi(currency.fiatToBTC(amountSats));
amountSats = btcToSatoshi(fiatToBTC(amountSats));
}
break;
}

View file

@ -27,7 +27,7 @@ import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import SafeArea from '../../components/SafeArea';
const currency = require('../../blue_modules/currency');
import { btcToSatoshi, fiatToBTC } from '../../blue_modules/currency';
const ScanLndInvoice = () => {
const { wallets, fetchAndSaveWalletTransactions } = useContext(BlueStorageContext);
@ -182,10 +182,10 @@ const ScanLndInvoice = () => {
amountSats = parseInt(amountSats, 10); // nop
break;
case BitcoinUnit.BTC:
amountSats = currency.btcToSatoshi(amountSats);
amountSats = btcToSatoshi(amountSats);
break;
case BitcoinUnit.LOCAL_CURRENCY:
amountSats = currency.btcToSatoshi(currency.fiatToBTC(amountSats));
amountSats = btcToSatoshi(fiatToBTC(amountSats));
break;
}
setIsLoading(true);

View file

@ -38,7 +38,7 @@ import { SuccessView } from '../send/success';
import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
const currency = require('../../blue_modules/currency');
import { fiatToBTC, satoshiToBTC } from '../../blue_modules/currency';
const ReceiveDetails = () => {
const { walletID, address } = useRoute().params;
@ -385,14 +385,14 @@ const ReceiveDetails = () => {
// nop
break;
case BitcoinUnit.SATS:
amount = currency.satoshiToBTC(customAmount);
amount = satoshiToBTC(customAmount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
if (AmountInput.conversionCache[amount + BitcoinUnit.LOCAL_CURRENCY]) {
// cache hit! we reuse old value that supposedly doesnt have rounding errors
amount = currency.satoshiToBTC(AmountInput.conversionCache[amount + BitcoinUnit.LOCAL_CURRENCY]);
amount = satoshiToBTC(AmountInput.conversionCache[amount + BitcoinUnit.LOCAL_CURRENCY]);
} else {
amount = currency.fiatToBTC(customAmount);
amount = fiatToBTC(customAmount);
}
break;
}
@ -447,9 +447,9 @@ const ReceiveDetails = () => {
case BitcoinUnit.BTC:
return customAmount + ' BTC';
case BitcoinUnit.SATS:
return currency.satoshiToBTC(customAmount) + ' BTC';
return satoshiToBTC(customAmount) + ' BTC';
case BitcoinUnit.LOCAL_CURRENCY:
return currency.fiatToBTC(customAmount) + ' BTC';
return fiatToBTC(customAmount) + ' BTC';
}
return customAmount + ' ' + customUnit;
} else {

View file

@ -17,7 +17,7 @@ import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import SafeArea from '../../components/SafeArea';
const currency = require('../../blue_modules/currency');
import { satoshiToBTC, satoshiToLocalCurrency } from '../../blue_modules/currency';
const BlueElectrum = require('../../blue_modules/BlueElectrum');
const Bignumber = require('bignumber.js');
const bitcoin = require('bitcoinjs-lib');
@ -182,11 +182,11 @@ const Confirm = () => {
<>
<View style={styles.valueWrap}>
<Text testID="TransactionValue" style={[styles.valueValue, stylesHook.valueValue]}>
{currency.satoshiToBTC(item.value)}
{satoshiToBTC(item.value)}
</Text>
<Text style={[styles.valueUnit, stylesHook.valueValue]}>{' ' + loc.units[BitcoinUnit.BTC]}</Text>
</View>
<Text style={[styles.transactionAmountFiat, stylesHook.transactionAmountFiat]}>{currency.satoshiToLocalCurrency(item.value)}</Text>
<Text style={[styles.transactionAmountFiat, stylesHook.transactionAmountFiat]}>{satoshiToLocalCurrency(item.value)}</Text>
<BlueCard>
<Text style={[styles.transactionDetailsTitle, stylesHook.transactionDetailsTitle]}>{loc.send.create_to}</Text>
<Text testID="TransactionAddress" style={[styles.transactionDetailsSubtitle, stylesHook.transactionDetailsSubtitle]}>
@ -233,7 +233,7 @@ const Confirm = () => {
<View style={styles.cardBottom}>
<BlueCard>
<Text style={styles.cardText} testID="TransactionFee">
{loc.send.create_fee}: {formatBalance(feeSatoshi, BitcoinUnit.BTC)} ({currency.satoshiToLocalCurrency(feeSatoshi)})
{loc.send.create_fee}: {formatBalance(feeSatoshi, BitcoinUnit.BTC)} ({satoshiToLocalCurrency(feeSatoshi)})
</Text>
{isLoading ? <ActivityIndicator /> : <Button disabled={isElectrumDisabled} onPress={send} title={loc.send.confirm_sendNow} />}
</BlueCard>

View file

@ -17,8 +17,8 @@ import { useNavigation, useRoute } from '@react-navigation/native';
import alert from '../../components/Alert';
import { PERMISSIONS, RESULTS, request } from 'react-native-permissions';
import { useTheme } from '../../components/themes';
import { satoshiToBTC } from '../../blue_modules/currency';
const bitcoin = require('bitcoinjs-lib');
const currency = require('../../blue_modules/currency');
const SendCreate = () => {
const { fee, recipients, memo = '', satoshiPerByte, psbt, showAnimatedQr, tx } = useRoute().params;
@ -116,7 +116,7 @@ const SendCreate = () => {
<Text style={[styles.transactionDetailsSubtitle, styleHooks.transactionDetailsSubtitle]}>{item.address}</Text>
<Text style={[styles.transactionDetailsTitle, styleHooks.transactionDetailsTitle]}>{loc.send.create_amount}</Text>
<Text style={[styles.transactionDetailsSubtitle, styleHooks.transactionDetailsSubtitle]}>
{currency.satoshiToBTC(item.value)} {BitcoinUnit.BTC}
{satoshiToBTC(item.value)} {BitcoinUnit.BTC}
</Text>
{recipients.length > 1 && (
<BlueText style={styles.itemOf}>{loc.formatString(loc._.of, { number: index + 1, total: recipients.length })}</BlueText>

View file

@ -44,7 +44,7 @@ import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import ListItem from '../../components/ListItem';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
const currency = require('../../blue_modules/currency');
import { btcToSatoshi, fiatToBTC } from '../../blue_modules/currency';
const prompt = require('../../helpers/prompt');
const fs = require('../../blue_modules/fs');
const btcAddressRx = /^[a-zA-Z0-9]{26,35}$/;
@ -145,12 +145,12 @@ const SendDetails = () => {
currentAddress.address = address;
if (Number(amount) > 0) {
currentAddress.amount = amount;
currentAddress.amountSats = currency.btcToSatoshi(amount);
currentAddress.amountSats = btcToSatoshi(amount);
}
addrs[scrollIndex.current] = currentAddress;
return [...addrs];
} else {
return [...addrs, { address, amount, amountSats: currency.btcToSatoshi(amount), key: String(Math.random()) }];
return [...addrs, { address, amount, amountSats: btcToSatoshi(amount), key: String(Math.random()) }];
}
});
@ -285,8 +285,8 @@ const SendDetails = () => {
if (value > 0) {
targets.push({ address: transaction.address, value });
} else if (transaction.amount) {
if (currency.btcToSatoshi(transaction.amount) > 0) {
targets.push({ address: transaction.address, value: currency.btcToSatoshi(transaction.amount) });
if (btcToSatoshi(transaction.amount) > 0) {
targets.push({ address: transaction.address, value: btcToSatoshi(transaction.amount) });
}
}
}
@ -517,8 +517,8 @@ const SendDetails = () => {
if (value > 0) {
targets.push({ address: transaction.address, value });
} else if (transaction.amount) {
if (currency.btcToSatoshi(transaction.amount) > 0) {
targets.push({ address: transaction.address, value: currency.btcToSatoshi(transaction.amount) });
if (btcToSatoshi(transaction.amount) > 0) {
targets.push({ address: transaction.address, value: btcToSatoshi(transaction.amount) });
}
}
}
@ -1329,11 +1329,11 @@ const SendDetails = () => {
addr.amountSats = parseInt(addr.amount, 10);
break;
case BitcoinUnit.BTC:
addr.amountSats = currency.btcToSatoshi(addr.amount);
addr.amountSats = btcToSatoshi(addr.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
// also accounting for cached fiat->sat conversion to avoid rounding error
addr.amountSats = AmountInput.getCachedSatoshis(addr.amount) || currency.btcToSatoshi(currency.fiatToBTC(addr.amount));
addr.amountSats = AmountInput.getCachedSatoshis(addr.amount) || btcToSatoshi(fiatToBTC(addr.amount));
break;
}
@ -1350,10 +1350,10 @@ const SendDetails = () => {
item.amount = text;
switch (units[index] || amountUnit) {
case BitcoinUnit.BTC:
item.amountSats = currency.btcToSatoshi(item.amount);
item.amountSats = btcToSatoshi(item.amount);
break;
case BitcoinUnit.LOCAL_CURRENCY:
item.amountSats = currency.btcToSatoshi(currency.fiatToBTC(item.amount));
item.amountSats = btcToSatoshi(fiatToBTC(item.amount));
break;
case BitcoinUnit.SATS:
default:

View file

@ -12,9 +12,9 @@ import alert from '../../components/Alert';
import { useTheme } from '../../components/themes';
import Button from '../../components/Button';
import SafeArea from '../../components/SafeArea';
import { satoshiToBTC, satoshiToLocalCurrency } from '../../blue_modules/currency';
const bitcoin = require('bitcoinjs-lib');
const BigNumber = require('bignumber.js');
const currency = require('../../blue_modules/currency');
const shortenAddress = addr => {
return addr.substr(0, Math.floor(addr.length / 2) - 1) + '\n' + addr.substr(Math.floor(addr.length / 2) - 1, addr.length);
@ -81,7 +81,7 @@ const PsbtMultisig = () => {
}
destination = shortenAddress(destination.join(', '));
const totalBtc = new BigNumber(totalSat).dividedBy(100000000).toNumber();
const totalFiat = currency.satoshiToLocalCurrency(totalSat);
const totalFiat = satoshiToLocalCurrency(totalSat);
const getFee = () => {
return wallet.calculateFeeFromPsbt(psbt);
@ -252,9 +252,9 @@ const PsbtMultisig = () => {
<View style={styles.bottomWrapper}>
<View style={styles.bottomFeesWrapper}>
<BlueText style={[styles.feeFiatText, stylesHook.feeFiatText]}>
{loc.formatString(loc.multisig.fee, { number: currency.satoshiToLocalCurrency(getFee()) })} -{' '}
{loc.formatString(loc.multisig.fee, { number: satoshiToLocalCurrency(getFee()) })} -{' '}
</BlueText>
<BlueText>{loc.formatString(loc.multisig.fee_btc, { number: currency.satoshiToBTC(getFee()) })}</BlueText>
<BlueText>{loc.formatString(loc.multisig.fee_btc, { number: satoshiToBTC(getFee()) })}</BlueText>
</View>
</View>
<View style={styles.marginConfirmButton}>

View file

@ -10,16 +10,17 @@ import dayjs from 'dayjs';
import alert from '../../components/Alert';
import { useTheme } from '../../components/themes';
import ListItem from '../../components/ListItem';
import {
CurrencyRate,
getPreferredCurrency,
initCurrencyDaemon,
mostRecentFetchedRate,
setPreferredCurrency,
} from '../../blue_modules/currency';
dayjs.extend(require('dayjs/plugin/calendar'));
const currency = require('../../blue_modules/currency');
const ITEM_HEIGHT = 50;
interface CurrencyRate {
LastUpdated: Date | null;
Rate: number | null;
}
const Currency: React.FC = () => {
const { setPreferredFiatCurrency } = useContext(BlueStorageContext);
const [isSavingNewPreferredCurrency, setIsSavingNewPreferredCurrency] = useState(false);
@ -39,18 +40,18 @@ const Currency: React.FC = () => {
});
const fetchCurrency = async () => {
let preferredCurrency = FiatUnit.USD;
let preferredCurrency;
try {
preferredCurrency = await currency.getPreferredCurrency();
preferredCurrency = await getPreferredCurrency();
if (preferredCurrency === null) {
throw Error();
}
setSelectedCurrency(preferredCurrency);
} catch (_error) {
setSelectedCurrency(preferredCurrency);
setSelectedCurrency(FiatUnit.USD);
}
const mostRecentFetchedRate = await currency.mostRecentFetchedRate();
setCurrencyRate(mostRecentFetchedRate);
const mostRecentFetchedRateValue = await mostRecentFetchedRate();
setCurrencyRate(mostRecentFetchedRateValue);
};
useLayoutEffect(() => {
@ -78,8 +79,8 @@ const Currency: React.FC = () => {
setIsSavingNewPreferredCurrency(true);
try {
await getFiatRate(item.endPointKey);
await currency.setPrefferedCurrency(item);
await currency.init(true);
await setPreferredCurrency(item);
await initCurrencyDaemon(true);
await fetchCurrency();
setSelectedCurrency(item);
setPreferredFiatCurrency();

View file

@ -2,49 +2,56 @@ import assert from 'assert';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { FiatUnit } from '../../models/fiatUnit';
import {
EXCHANGE_RATES_STORAGE_KEY,
LAST_UPDATED,
PREFERRED_CURRENCY_STORAGE_KEY,
getPreferredCurrency,
initCurrencyDaemon,
setPreferredCurrency,
} from '../../blue_modules/currency';
jest.setTimeout(90 * 1000);
describe('currency', () => {
it('fetches exchange rate and saves to AsyncStorage', async () => {
const currency = require('../../blue_modules/currency');
await currency.init();
let cur = await AsyncStorage.getItem(currency.EXCHANGE_RATES);
await initCurrencyDaemon();
let cur = await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY);
cur = JSON.parse(cur);
assert.ok(Number.isInteger(cur[currency.LAST_UPDATED]));
assert.ok(cur[currency.LAST_UPDATED] > 0);
assert.ok(Number.isInteger(cur[LAST_UPDATED]));
assert.ok(cur[LAST_UPDATED] > 0);
assert.ok(cur.BTC_USD > 0);
// now, setting other currency as default
await AsyncStorage.setItem(currency.PREFERRED_CURRENCY, JSON.stringify(FiatUnit.JPY));
await currency.init(true);
cur = JSON.parse(await AsyncStorage.getItem(currency.EXCHANGE_RATES));
await AsyncStorage.setItem(PREFERRED_CURRENCY_STORAGE_KEY, JSON.stringify(FiatUnit.JPY));
await initCurrencyDaemon(true);
cur = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
assert.ok(cur.BTC_JPY > 0);
// now setting with a proper setter
await currency.setPrefferedCurrency(FiatUnit.EUR);
await currency.init(true);
const preferred = await currency.getPreferredCurrency();
await setPreferredCurrency(FiatUnit.EUR);
await initCurrencyDaemon(true);
const preferred = await getPreferredCurrency();
assert.strictEqual(preferred.endPointKey, 'EUR');
cur = JSON.parse(await AsyncStorage.getItem(currency.EXCHANGE_RATES));
cur = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
assert.ok(cur.BTC_EUR > 0);
// test Yadio rate source
await currency.setPrefferedCurrency(FiatUnit.ARS);
await currency.init(true);
cur = JSON.parse(await AsyncStorage.getItem(currency.EXCHANGE_RATES));
await setPreferredCurrency(FiatUnit.ARS);
await initCurrencyDaemon(true);
cur = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
assert.ok(cur.BTC_ARS > 0);
// test YadioConvert rate source
await currency.setPrefferedCurrency(FiatUnit.LBP);
await currency.init(true);
cur = JSON.parse(await AsyncStorage.getItem(currency.EXCHANGE_RATES));
await setPreferredCurrency(FiatUnit.LBP);
await initCurrencyDaemon(true);
cur = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
assert.ok(cur.BTC_LBP > 0);
// test Exir rate source
await currency.setPrefferedCurrency(FiatUnit.IRT);
await currency.init(true);
cur = JSON.parse(await AsyncStorage.getItem(currency.EXCHANGE_RATES));
await setPreferredCurrency(FiatUnit.IRT);
await initCurrencyDaemon(true);
cur = JSON.parse(await AsyncStorage.getItem(EXCHANGE_RATES_STORAGE_KEY));
assert.ok(cur.BTC_IRT > 0);
});
});

View file

@ -4,7 +4,7 @@ import { TABS } from '../../components/addresses/AddressTypeTabs';
jest.mock('../../blue_modules/currency', () => {
return {
init: jest.fn(),
initCurrencyDaemon: jest.fn(),
};
});

View file

@ -1,36 +1,39 @@
import {
BTCToLocalCurrency,
_setExchangeRate,
_setPreferredFiatCurrency,
satoshiToBTC,
satoshiToLocalCurrency,
} from '../../blue_modules/currency';
import { FiatUnit } from '../../models/fiatUnit';
const assert = require('assert');
describe('currency', () => {
it('formats everything correctly', async () => {
const currency = require('../../blue_modules/currency');
currency._setExchangeRate('BTC_USD', 10000);
_setExchangeRate('BTC_USD', 10000);
assert.strictEqual(currency.satoshiToLocalCurrency(1), '$0.0001');
assert.strictEqual(currency.satoshiToLocalCurrency(-1), '-$0.0001');
assert.strictEqual(currency.satoshiToLocalCurrency(123), '$0.01');
assert.strictEqual(currency.satoshiToLocalCurrency(156), '$0.02');
assert.strictEqual(currency.satoshiToLocalCurrency(51), '$0.01');
assert.strictEqual(currency.satoshiToLocalCurrency(45), '$0.0045');
assert.strictEqual(currency.satoshiToLocalCurrency(123456789), '$12,345.68');
assert.strictEqual(satoshiToLocalCurrency(1), '$0.0001');
assert.strictEqual(satoshiToLocalCurrency(-1), '-$0.0001');
assert.strictEqual(satoshiToLocalCurrency(123), '$0.01');
assert.strictEqual(satoshiToLocalCurrency(156), '$0.02');
assert.strictEqual(satoshiToLocalCurrency(51), '$0.01');
assert.strictEqual(satoshiToLocalCurrency(45), '$0.0045');
assert.strictEqual(satoshiToLocalCurrency(123456789), '$12,345.68');
assert.strictEqual(currency.BTCToLocalCurrency(1), '$10,000.00');
assert.strictEqual(currency.BTCToLocalCurrency(-1), '-$10,000.00');
assert.strictEqual(currency.BTCToLocalCurrency(1.00000001), '$10,000.00');
assert.strictEqual(currency.BTCToLocalCurrency(1.0000123), '$10,000.12');
assert.strictEqual(currency.BTCToLocalCurrency(1.0000146), '$10,000.15');
assert.strictEqual(BTCToLocalCurrency(1), '$10,000.00');
assert.strictEqual(BTCToLocalCurrency(-1), '-$10,000.00');
assert.strictEqual(BTCToLocalCurrency(1.00000001), '$10,000.00');
assert.strictEqual(BTCToLocalCurrency(1.0000123), '$10,000.12');
assert.strictEqual(BTCToLocalCurrency(1.0000146), '$10,000.15');
assert.strictEqual(currency.satoshiToBTC(1), '0.00000001');
assert.strictEqual(currency.satoshiToBTC(-1), '-0.00000001');
assert.strictEqual(currency.satoshiToBTC(100000000), '1');
assert.strictEqual(currency.satoshiToBTC(123456789123456789), '1234567891.2345678'); // eslint-disable-line @typescript-eslint/no-loss-of-precision
assert.strictEqual(satoshiToBTC(1), '0.00000001');
assert.strictEqual(satoshiToBTC(-1), '-0.00000001');
assert.strictEqual(satoshiToBTC(100000000), '1');
assert.strictEqual(satoshiToBTC(123456789123456789), '1234567891.2345678'); // eslint-disable-line @typescript-eslint/no-loss-of-precision
currency._setPreferredFiatCurrency(FiatUnit.JPY);
currency._setExchangeRate('BTC_JPY', 1043740.8614);
_setPreferredFiatCurrency(FiatUnit.JPY);
_setExchangeRate('BTC_JPY', 1043740.8614);
assert.ok(
currency.satoshiToLocalCurrency(1) === '¥0.01' || currency.satoshiToLocalCurrency(1) === '¥0.01',
'Unexpected: ' + currency.satoshiToLocalCurrency(1),
);
assert.ok(satoshiToLocalCurrency(1) === '¥0.01' || satoshiToLocalCurrency(1) === '¥0.01', 'Unexpected: ' + satoshiToLocalCurrency(1));
});
});

View file

@ -2,7 +2,7 @@ import assert from 'assert';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { FiatUnit } from '../../models/fiatUnit';
import { _leaveNumbersAndDots, formatBalanceWithoutSuffix, formatBalancePlain, formatBalance } from '../../loc';
const currency = require('../../blue_modules/currency');
import { _setExchangeRate, _setPreferredFiatCurrency, _setSkipUpdateExchangeRate } from '../../blue_modules/currency';
describe('Localization', () => {
it('internal formatter', () => {
@ -12,8 +12,8 @@ describe('Localization', () => {
});
it('formatBalancePlain() && formatBalancePlain()', () => {
currency._setExchangeRate('BTC_RUB', 660180.143);
currency._setPreferredFiatCurrency(FiatUnit.RUB);
_setExchangeRate('BTC_RUB', 660180.143);
_setPreferredFiatCurrency(FiatUnit.RUB);
let newInputValue = formatBalanceWithoutSuffix(152, BitcoinUnit.LOCAL_CURRENCY, false);
assert.ok(newInputValue === 'RUB 1.00' || newInputValue === '1,00 ₽', 'Unexpected: ' + newInputValue);
newInputValue = formatBalancePlain(152, BitcoinUnit.LOCAL_CURRENCY, false);
@ -32,8 +32,8 @@ describe('Localization', () => {
newInputValue = formatBalancePlain(76, BitcoinUnit.LOCAL_CURRENCY, false);
assert.strictEqual(newInputValue, '0.50');
currency._setExchangeRate('BTC_USD', 10000);
currency._setPreferredFiatCurrency(FiatUnit.USD);
_setExchangeRate('BTC_USD', 10000);
_setPreferredFiatCurrency(FiatUnit.USD);
newInputValue = formatBalanceWithoutSuffix(16793829, BitcoinUnit.LOCAL_CURRENCY, false);
assert.strictEqual(newInputValue, '$1,679.38');
newInputValue = formatBalancePlain(16793829, BitcoinUnit.LOCAL_CURRENCY, false);
@ -55,11 +55,11 @@ describe('Localization', () => {
])(
'can formatBalanceWithoutSuffix',
async (balance, toUnit, withFormatting, expectedResult, shouldResetRate) => {
currency._setExchangeRate('BTC_USD', 1);
currency._setPreferredFiatCurrency(FiatUnit.USD);
_setExchangeRate('BTC_USD', 1);
_setPreferredFiatCurrency(FiatUnit.USD);
if (shouldResetRate) {
currency._setExchangeRate('BTC_USD', false);
currency._setSkipUpdateExchangeRate();
_setExchangeRate('BTC_USD', false);
_setSkipUpdateExchangeRate();
}
const actualResult = formatBalanceWithoutSuffix(balance, toUnit, withFormatting);
assert.strictEqual(actualResult, expectedResult);
@ -74,8 +74,8 @@ describe('Localization', () => {
])(
'can formatBalance',
async (balance, toUnit, withFormatting, expectedResult) => {
currency._setExchangeRate('BTC_USD', 1);
currency._setPreferredFiatCurrency(FiatUnit.USD);
_setExchangeRate('BTC_USD', 1);
_setPreferredFiatCurrency(FiatUnit.USD);
const actualResult = formatBalance(balance, toUnit, withFormatting);
assert.strictEqual(actualResult, expectedResult);
},