import { useEffect, useRef, useState } from 'react'; import { getNativeBalances, type OnChainBalance } from '../services/chain138'; export interface OnChainBalancesState { balances: Record; loading: boolean; error: string | null; lastUpdated: Date | null; } /** * Fetches native Chain-138 balances for the given addresses and re-polls * every `pollMs` (default 30s). Addresses array must be stable — pass a * memoized list, or the hook will re-fetch on every render. */ export function useOnChainBalances(addresses: string[], pollMs = 30_000): OnChainBalancesState { const [balances, setBalances] = useState>({}); const [loading, setLoading] = useState(addresses.length > 0); const [error, setError] = useState(null); const [lastUpdated, setLastUpdated] = useState(null); const mounted = useRef(true); const key = addresses.join(','); useEffect(() => { mounted.current = true; if (addresses.length === 0) { setLoading(false); return; } let cancelled = false; const tick = async () => { try { const result = await getNativeBalances(addresses); if (cancelled || !mounted.current) return; setBalances(result); setError(null); setLastUpdated(new Date()); } catch (e) { if (cancelled || !mounted.current) return; setError(e instanceof Error ? e.message : String(e)); } finally { if (!cancelled && mounted.current) setLoading(false); } }; void tick(); const id = setInterval(tick, pollMs); return () => { cancelled = true; mounted.current = false; clearInterval(id); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [key, pollMs]); return { balances, loading, error, lastUpdated }; }