mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 13:26:33 +01:00
commit
ef99e0f406
@ -13,176 +13,202 @@ export const FiatUnitSource = {
|
||||
BNR: 'BNR',
|
||||
} as const;
|
||||
|
||||
const handleError = (source: string, ticker: string, error: Error) => {
|
||||
throw new Error(
|
||||
`Could not update rate for ${ticker} from ${source}: ${error.message}. ` + `Make sure the network you're on has access to ${source}.`,
|
||||
);
|
||||
};
|
||||
|
||||
const fetchRate = async (url: string): Promise<unknown> => {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
interface CoinbaseResponse {
|
||||
data: {
|
||||
amount: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface CoinDeskResponse {
|
||||
bpi: {
|
||||
[ticker: string]: {
|
||||
rate_float: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface CoinGeckoResponse {
|
||||
bitcoin: {
|
||||
[ticker: string]: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface BitstampResponse {
|
||||
last: string;
|
||||
}
|
||||
|
||||
interface KrakenResponse {
|
||||
result: {
|
||||
[pair: string]: {
|
||||
c: [string];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface YadioResponse {
|
||||
[ticker: string]: {
|
||||
price: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface YadioConvertResponse {
|
||||
rate: number;
|
||||
}
|
||||
|
||||
interface ExirResponse {
|
||||
last: string;
|
||||
}
|
||||
|
||||
interface CoinpaprikaResponse {
|
||||
quotes: {
|
||||
[ticker: string]: {
|
||||
price: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const RateExtractors = {
|
||||
Coinbase: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://api.coinbase.com/v2/prices/BTC-${ticker.toUpperCase()}/buy`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate(`https://api.coinbase.com/v2/prices/BTC-${ticker.toUpperCase()}/buy`)) as CoinbaseResponse;
|
||||
const rate = Number(json?.data?.amount);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('Coinbase', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
let rate = json?.data?.amount;
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
CoinDesk: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://api.coindesk.com/v1/bpi/currentprice/${ticker}.json`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate(`https://api.coindesk.com/v1/bpi/currentprice/${ticker}.json`)) as CoinDeskResponse;
|
||||
const rate = Number(json?.bpi?.[ticker]?.rate_float);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('CoinDesk', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
let rate = json?.bpi?.[ticker]?.rate_float;
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
CoinGecko: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=${ticker.toLowerCase()}`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate(
|
||||
`https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=${ticker.toLowerCase()}`,
|
||||
)) as CoinGeckoResponse;
|
||||
const rate = Number(json?.bitcoin?.[ticker.toLowerCase()]);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('CoinGecko', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
const rate = json?.bitcoin?.[ticker] || json?.bitcoin?.[ticker.toLowerCase()];
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
Bitstamp: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://www.bitstamp.net/api/v2/ticker/btc${ticker.toLowerCase()}`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate from Bitstamp for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate(`https://www.bitstamp.net/api/v2/ticker/btc${ticker.toLowerCase()}`)) as BitstampResponse;
|
||||
const rate = Number(json?.last);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('Bitstamp', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
|
||||
if (Array.isArray(json)) {
|
||||
throw new Error(`Unsupported ticker for Bitstamp: ${ticker}`);
|
||||
}
|
||||
|
||||
let rate = +json?.last;
|
||||
if (!rate) throw new Error(`Could not update rate from Bitstamp for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate from Bitstamp for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
Kraken: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://api.kraken.com/0/public/Ticker?pair=XXBTZ${ticker.toUpperCase()}`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate from Kraken for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate(`https://api.kraken.com/0/public/Ticker?pair=XXBTZ${ticker.toUpperCase()}`)) as KrakenResponse;
|
||||
const rate = Number(json?.result?.[`XXBTZ${ticker.toUpperCase()}`]?.c?.[0]);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('Kraken', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
|
||||
let rate = json?.result?.[`XXBTZ${ticker.toUpperCase()}`]?.c?.[0];
|
||||
|
||||
if (!rate) throw new Error(`Could not update rate from Kraken for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate from Kraken for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
BNR: async (): Promise<number> => {
|
||||
try {
|
||||
const response = await fetch('https://www.bnr.ro/nbrfxrates.xml');
|
||||
const xmlData = await response.text();
|
||||
|
||||
// Fetching USD to RON rate
|
||||
const pattern = /<Rate currency="USD">([\d.]+)<\/Rate>/;
|
||||
const matches = xmlData.match(pattern);
|
||||
|
||||
const xmlData = await (await fetch('https://www.bnr.ro/nbrfxrates.xml')).text();
|
||||
const matches = xmlData.match(/<Rate currency="USD">([\d.]+)<\/Rate>/);
|
||||
if (matches && matches[1]) {
|
||||
const usdToRonRate = parseFloat(matches[1]);
|
||||
if (!isNaN(usdToRonRate) && usdToRonRate > 0) {
|
||||
// Fetch BTC to USD rate using CoinGecko extractor
|
||||
const btcToUsdRate = await RateExtractors.CoinGecko('USD');
|
||||
|
||||
// Convert BTC to RON using the USD to RON exchange rate
|
||||
return btcToUsdRate * usdToRonRate;
|
||||
}
|
||||
const btcToUsdRate = await RateExtractors.CoinGecko('USD');
|
||||
// Convert BTC to RON using the USD to RON exchange rate
|
||||
return btcToUsdRate * usdToRonRate;
|
||||
}
|
||||
throw new Error('Could not find a valid exchange rate for USD to RON');
|
||||
throw new Error('No valid USD to RON rate found');
|
||||
} catch (error: any) {
|
||||
throw new Error(`Could not fetch RON exchange rate: ${error.message}`);
|
||||
handleError('BNR', 'RON', error);
|
||||
return undefined as never;
|
||||
}
|
||||
},
|
||||
Yadio: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://api.yadio.io/json/${ticker}`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
}
|
||||
let rate = json?.[ticker]?.price;
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
Yadio: async (ticker: string): Promise<number> => {
|
||||
try {
|
||||
const json = (await fetchRate(`https://api.yadio.io/json/${ticker}`)) as YadioResponse;
|
||||
const rate = Number(json?.[ticker]?.price);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('Yadio', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
},
|
||||
|
||||
YadioConvert: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch(`https://api.yadio.io/convert/1/BTC/${ticker}`);
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate(`https://api.yadio.io/convert/1/BTC/${ticker}`)) as YadioConvertResponse;
|
||||
const rate = Number(json?.rate);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('YadioConvert', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
let rate = json?.rate;
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
Exir: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch('https://api.exir.io/v1/ticker?symbol=btc-irt');
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate('https://api.exir.io/v1/ticker?symbol=btc-irt')) as ExirResponse;
|
||||
const rate = Number(json?.last);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('Exir', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
let rate = json?.last;
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
|
||||
rate = Number(rate);
|
||||
if (!(rate >= 0)) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
return rate;
|
||||
},
|
||||
|
||||
coinpaprika: async (ticker: string): Promise<number> => {
|
||||
let json;
|
||||
try {
|
||||
const res = await fetch('https://api.coinpaprika.com/v1/tickers/btc-bitcoin?quotes=INR');
|
||||
json = await res.json();
|
||||
} catch (e: any) {
|
||||
throw new Error(`Could not update rate for ${ticker}: ${e.message}`);
|
||||
const json = (await fetchRate('https://api.coinpaprika.com/v1/tickers/btc-bitcoin?quotes=INR')) as CoinpaprikaResponse;
|
||||
const rate = Number(json?.quotes?.INR?.price);
|
||||
if (!(rate >= 0)) throw new Error('Invalid data received');
|
||||
return rate;
|
||||
} catch (error: any) {
|
||||
handleError('coinpaprika', ticker, error);
|
||||
return undefined as never;
|
||||
}
|
||||
|
||||
const rate = json?.quotes?.INR?.price;
|
||||
if (!rate) throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
|
||||
const parsedRate = Number(rate);
|
||||
if (isNaN(parsedRate) || parsedRate <= 0) {
|
||||
throw new Error(`Could not update rate for ${ticker}: data is wrong`);
|
||||
}
|
||||
|
||||
return parsedRate;
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
mostRecentFetchedRate,
|
||||
setPreferredCurrency,
|
||||
} from '../../blue_modules/currency';
|
||||
import { BlueCard, BlueSpacing10, BlueText } from '../../BlueComponents';
|
||||
import { BlueCard, BlueSpacing10, BlueSpacing20, BlueText } from '../../BlueComponents';
|
||||
import presentAlert from '../../components/Alert';
|
||||
import ListItem from '../../components/ListItem';
|
||||
import { useTheme } from '../../components/themes';
|
||||
@ -136,6 +136,7 @@ const Currency: React.FC = () => {
|
||||
<BlueText>
|
||||
{loc.settings.last_updated}: {dayjs(currencyRate.LastUpdated).calendar() ?? loc._.never}
|
||||
</BlueText>
|
||||
<BlueSpacing20 />
|
||||
</BlueCard>
|
||||
) : null}
|
||||
</View>
|
||||
|
Loading…
Reference in New Issue
Block a user