All checks were successful
CI / lint-and-test (push) Successful in 9m52s
Co-authored-by: Cursor <cursoragent@cursor.com>
69 lines
1.7 KiB
TypeScript
69 lines
1.7 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
|
|
const STABLE_USD = 1;
|
|
|
|
async function fetchEthUsd(): Promise<number> {
|
|
const res = await fetch(
|
|
"https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd"
|
|
);
|
|
if (!res.ok) {
|
|
throw new Error(`Price feed HTTP ${res.status}`);
|
|
}
|
|
const json = (await res.json()) as { ethereum?: { usd?: number } };
|
|
const usd = json.ethereum?.usd;
|
|
if (typeof usd !== "number" || !Number.isFinite(usd)) {
|
|
throw new Error("Invalid ETH price payload");
|
|
}
|
|
return usd;
|
|
}
|
|
|
|
export function useUsdPrices() {
|
|
const query = useQuery({
|
|
queryKey: ["usd-prices", "eth"],
|
|
queryFn: fetchEthUsd,
|
|
staleTime: 60_000,
|
|
retry: 2,
|
|
});
|
|
|
|
return {
|
|
ethUsd: query.data ?? null,
|
|
stableUsd: STABLE_USD,
|
|
isLoading: query.isLoading,
|
|
error: query.error,
|
|
};
|
|
}
|
|
|
|
export function rawToDecimal(raw: bigint, decimals: number): number {
|
|
const divisor = 10 ** decimals;
|
|
return Number(raw) / divisor;
|
|
}
|
|
|
|
export function formatUsd(value: number | null | undefined): string {
|
|
if (value == null || !Number.isFinite(value)) {
|
|
return "—";
|
|
}
|
|
return new Intl.NumberFormat("en-US", {
|
|
style: "currency",
|
|
currency: "USD",
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
}).format(value);
|
|
}
|
|
|
|
export function tokenUsdValue(
|
|
raw: bigint | undefined,
|
|
decimals: number,
|
|
symbol: string,
|
|
ethUsd: number | null
|
|
): number | null {
|
|
if (raw === undefined) return null;
|
|
const amount = rawToDecimal(raw, decimals);
|
|
if (symbol === "ETH") {
|
|
return ethUsd != null ? amount * ethUsd : null;
|
|
}
|
|
if (symbol === "cUSDT" || symbol === "cUSDC") {
|
|
return amount * STABLE_USD;
|
|
}
|
|
return null;
|
|
}
|