dgb-wallet/packages/web-ui/src/usePrice.ts
Digibyte.X d50286a9a5 DigiByte.X Wallet — recovery-first web wallet monorepo
Packages: core (BIP39/32/44/49/84/86 derivation, WIF), keystore (PBKDF2 +
AES-GCM), psbt, p2p (BIP157/158 SPV daemon, Dandelion++ broadcast with
relay-echo verification), blockbook, rpc, electrum, net, bns, web-ui
(React/Vite), mobile (Expo). ops/ holds the nginx vhost, systemd unit and
push.sh deploy script for digibyte.x/web.
2026-09-20 20:16:52 +02:00

52 lines
1.9 KiB
TypeScript

import { useEffect, useState } from 'react';
// CoinGecko DGB→USD, opt-in via Settings > Display. Fetched once per
// mount + on manual refresh. NOT persisted per-second; caching for one
// session is enough — an approximate price stays useful for a while.
//
// Privacy trade-off: enabling this reveals to CoinGecko that a browser
// on the user's IP is checking the DGB price. Not seed-linked; not
// address-linked; but it IS a data point. The Settings toggle names
// the trade-off honestly.
const ENDPOINT = 'https://api.coingecko.com/api/v3/simple/price?ids=digibyte&vs_currencies=usd';
interface PriceState {
usd: number | null;
fetchedAt: number | null;
error: string | null;
loading: boolean;
}
export function usePrice(enabled: boolean): PriceState & { refresh: () => void } {
const [state, setState] = useState<PriceState>({
usd: null, fetchedAt: null, error: null, loading: false,
});
const [nonce, setNonce] = useState(0);
useEffect(() => {
if (!enabled) {
setState({ usd: null, fetchedAt: null, error: null, loading: false });
return;
}
let cancelled = false;
setState((s) => ({ ...s, loading: true, error: null }));
(async () => {
try {
const res = await fetch(ENDPOINT);
if (!res.ok) throw new Error(`CoinGecko HTTP ${res.status}`);
const body = (await res.json()) as { digibyte?: { usd?: number } };
const usd = body.digibyte?.usd ?? null;
if (cancelled) return;
if (typeof usd !== 'number') throw new Error('price missing in response');
setState({ usd, fetchedAt: Date.now(), error: null, loading: false });
} catch (e) {
if (cancelled) return;
setState({ usd: null, fetchedAt: null, error: (e as Error).message, loading: false });
}
})();
return () => { cancelled = true; };
}, [enabled, nonce]);
return { ...state, refresh: () => setNonce((n) => n + 1) };
}