diff --git a/src/api/hooks/useGetPrice.ts b/src/api/hooks/useGetPrice.ts new file mode 100644 index 00000000..bed363fc --- /dev/null +++ b/src/api/hooks/useGetPrice.ts @@ -0,0 +1,61 @@ +import {useQuery} from "@tanstack/react-query"; + +const COINGECKO_API_ENDPOINT = "https://api.coingecko.com/api/v3/simple/price"; + +/** + * Fetches the USD price for a cryptocurrency using its CoinGecko ID. + * + * Common CoinGecko IDs: + * - "aptos" : Aptos (APT) + * - "bitcoin": Bitcoin (BTC) + * - "ethereum": Ethereum (ETH) + * - "solana": Solana (SOL) + * - "binancecoin": Binance Coin (BNB) + * - "cardano": Cardano (ADA) + * + * Complete list of supported IDs: + * https://api.coingecko.com/api/v3/coins/list + * + * @param coinId - The CoinGecko ID of the cryptocurrency (defaults to "aptos") + * @returns The USD price of the cryptocurrency or null if the price fetch fails + */ +export async function getPrice( + coinId: string = "aptos", +): Promise { + const query = { + ids: coinId, + vs_currencies: "usd", + }; + + const queryString = new URLSearchParams(query); + const url = `${COINGECKO_API_ENDPOINT}?${queryString}`; + + try { + const response = await fetch(url, { + method: "GET", + }); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const data = await response.json(); + return Number(data[coinId].usd); + } catch (error) { + console.error(`Error fetching ${coinId} price from CoinGecko:`, error); + return null; + } +} + +/** + * Fetches the USD price for a cryptocurrency using its CoinGecko ID. + * + * @param coinId - The CoinGecko ID of the cryptocurrency (defaults to "aptos") + * @returns React Query result object containing the price data and query state + */ +export function useGetPrice(coinId: string = "aptos") { + return useQuery({ + queryKey: ["price", coinId], + queryFn: () => getPrice(coinId), + }); +} diff --git a/src/pages/Account/BalanceCard.tsx b/src/pages/Account/BalanceCard.tsx index a80b5aed..f2640ccf 100644 --- a/src/pages/Account/BalanceCard.tsx +++ b/src/pages/Account/BalanceCard.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, {useState, useEffect} from "react"; import {Stack, Typography} from "@mui/material"; import {getFormattedBalanceStr} from "../../components/IndividualPageContent/ContentValue/CurrencyValue"; import {Card} from "../../components/Card"; @@ -6,6 +6,8 @@ import {grey} from "../../themes/colors/aptosColorPalette"; import StyledTooltip from "../../components/StyledTooltip"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import {useGetAccountAPTBalance} from "../../api/hooks/useGetAccountAPTBalance"; +import {getPrice} from "../../api/hooks/useGetPrice"; +import {useGlobalState} from "../../global-config/GlobalConfig"; type BalanceCardProps = { address: string; @@ -13,18 +15,51 @@ type BalanceCardProps = { export default function BalanceCard({address}: BalanceCardProps) { const balance = useGetAccountAPTBalance(address); + const [globalState] = useGlobalState(); + const [price, setPrice] = useState(null); + + useEffect(() => { + const fetchPrice = async () => { + try { + const fetchedPrice = await getPrice(); + setPrice(fetchedPrice); + } catch (error) { + console.error("Error fetching APT price:", error); + setPrice(null); + } + }; + + fetchPrice(); + }, []); + + const balanceUSD = + balance.data && price !== null + ? (Number(balance.data) * Number(price)) / 10e7 + : null; return balance.data ? ( + {/* APT balance */} {`${getFormattedBalanceStr(balance.data)} APT`} + + {/* USD value */} + {globalState.network_name === "mainnet" && balanceUSD !== null && ( + + ${balanceUSD.toLocaleString(undefined, {maximumFractionDigits: 2})}{" "} + USD + + )} + Balance - +