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.
52 lines
1.9 KiB
TypeScript
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) };
|
|
}
|