diff --git a/src-gui/src/renderer/api.ts b/src-gui/src/renderer/api.ts index e5054b98..e0438920 100644 --- a/src-gui/src/renderer/api.ts +++ b/src-gui/src/renderer/api.ts @@ -58,10 +58,30 @@ async function fetchCurrencyUsdPrice(currency: string): Promise { } } +export async function fetchXmrBtcRate(): Promise { + try { + const response = await fetch('https://api.kraken.com/0/public/Ticker?pair=XMRXBT'); + const data = await response.json(); + + if (data.error && data.error.length > 0) { + throw new Error(`Kraken API error: ${data.error[0]}`); + } + + const result = data.result.XXMRXXBT; + const lastTradePrice = parseFloat(result.c[0]); + + return lastTradePrice; + } catch (error) { + console.error('Error fetching XMR/BTC rate from Kraken:', error); + throw error; + } +} + + export async function fetchBtcPrice(): Promise { return fetchCurrencyUsdPrice("bitcoin"); } export async function fetchXmrPrice(): Promise { return fetchCurrencyUsdPrice("monero"); -} +} \ No newline at end of file diff --git a/src-gui/src/renderer/components/modal/provider/ProviderInfo.tsx b/src-gui/src/renderer/components/modal/provider/ProviderInfo.tsx index e9bb8103..525f7221 100644 --- a/src-gui/src/renderer/components/modal/provider/ProviderInfo.tsx +++ b/src-gui/src/renderer/components/modal/provider/ProviderInfo.tsx @@ -9,6 +9,7 @@ import { import { satsToBtc, secondsToDays } from "utils/conversionUtils"; import { isProviderOutdated } from 'utils/multiAddrUtils'; import WarningIcon from '@material-ui/icons/Warning'; +import { useAppSelector } from "store/hooks"; const useStyles = makeStyles((theme) => ({ content: { @@ -25,6 +26,24 @@ const useStyles = makeStyles((theme) => ({ }, })); +function ProviderSpreadChip({ provider }: { provider: ExtendedProviderStatus }) { + const xmrBtcPrice = useAppSelector(s => s.rates?.xmrBtcRate); + + if (xmrBtcPrice === null) { + return null; + } + + const providerPrice = satsToBtc(provider.price); + const spread = ((providerPrice - xmrBtcPrice) / xmrBtcPrice) * 100; + + return ( + + + + ); + +} + export default function ProviderInfo({ provider, }: { @@ -78,6 +97,7 @@ export default function ProviderInfo({ } color="primary" /> )} + ); diff --git a/src-gui/src/renderer/index.tsx b/src-gui/src/renderer/index.tsx index 1c58041d..bb2719e0 100644 --- a/src-gui/src/renderer/index.tsx +++ b/src-gui/src/renderer/index.tsx @@ -6,12 +6,13 @@ import { registryConnectionFailed, setRegistryProviders, } from "store/features/providersSlice"; -import { setBtcPrice, setXmrPrice } from "store/features/ratesSlice"; +import { setBtcPrice, setXmrBtcRate, setXmrPrice } from "store/features/ratesSlice"; import logger from "../utils/logger"; import { fetchAlertsViaHttp, fetchBtcPrice, fetchProvidersViaHttp, + fetchXmrBtcRate, fetchXmrPrice, } from "./api"; import App from "./components/App"; @@ -64,6 +65,14 @@ async function fetchInitialData() { } catch (e) { logger.error(e, "Error retrieving fiat prices"); } + + try { + const xmrBtcRate = await fetchXmrBtcRate(); + store.dispatch(setXmrBtcRate(xmrBtcRate)); + logger.info({ xmrBtcRate }, "Fetched XMR/BTC rate"); + } catch (e) { + logger.error(e, "Error retrieving XMR/BTC rate"); + } } fetchInitialData(); diff --git a/src-gui/src/store/features/ratesSlice.ts b/src-gui/src/store/features/ratesSlice.ts index 7985d638..fbac52f9 100644 --- a/src-gui/src/store/features/ratesSlice.ts +++ b/src-gui/src/store/features/ratesSlice.ts @@ -1,13 +1,18 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; export interface RatesState { + // USD price of 1 BTC btcPrice: number | null; + // USD price of 1 XMR xmrPrice: number | null; + // XMR/BTC exchange rate + xmrBtcRate: number | null; } const initialState: RatesState = { btcPrice: null, xmrPrice: null, + xmrBtcRate: null, }; const ratesSlice = createSlice({ @@ -20,9 +25,12 @@ const ratesSlice = createSlice({ setXmrPrice: (state, action: PayloadAction) => { state.xmrPrice = action.payload; }, + setXmrBtcRate: (state, action: PayloadAction) => { + state.xmrBtcRate = action.payload; + }, }, }); -export const { setBtcPrice, setXmrPrice } = ratesSlice.actions; +export const { setBtcPrice, setXmrPrice, setXmrBtcRate } = ratesSlice.actions; export default ratesSlice.reducer;