import { useState } from 'react'; import { CheckCircle2, Loader2, Search, TriangleAlert } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { toneText } from '@/components/Data/tone'; import { ADDRESS_TYPE_LABELS, CHAIN_SYNC_TONE } from './shared'; import { useBalances, useWalletOperations } from './useWalletData'; // Search the chain for coins this wallet already owns. // // THIS IS THE FIX FOR AN IMPORTED WALLET THAT READS ZERO, and that symptom is why the card exists at all. // An indexing chain source (NBXplorer in front of your own node) only watches an account from the moment // you register it, so a seed with a history arrives at a balance of nothing: every call succeeds, no error // is raised, the coins are simply not in the index yet. A scan walks the node's current UTXO set directly // and finds them regardless of when the account was registered or how far back the node is pruned. // // It recovers SPENDABLE COINS, not history. Transactions spent before the account was registered stay // missing — that needs a full block rescan, which is a heavier thing this does not do. // // Hidden entirely when the chain source cannot rescan: Esplora asks about every address on every refresh, // so it has nothing to catch up on, and offering a button that 501s would invent a problem. type RescanCardProps = { walletId: number }; export const RescanCard = ({ walletId }: RescanCardProps) => { const { sync } = useBalances(walletId); const { rescan } = useWalletOperations(walletId); const [gapLimit, setGapLimit] = useState(''); // `sync` null is a node backend, which owns its own coins and has nothing to look for. `canRescan` // false is Esplora. Note this cannot key off `sync.rescan` being null — that is equally what NBXplorer // reports before its first scan, which is exactly when the card is most needed. if (!sync?.canRescan) return null; const state = sync.rescan; const running = state?.running === true; const parsed = Number(gapLimit); const depth = gapLimit.trim() && Number.isInteger(parsed) && parsed > 0 ? parsed : null; const submit = (ev?: React.FormEvent) => { ev?.preventDefault(); if (running || rescan.isPending) return; rescan.mutate(depth ? { gapLimit: depth } : {}); }; return (
); }; type StatusProps = { state: NonNullableScanning {label} — {state.done} of {state.total} done {state.found > 0 && `, ${state.found} coin${state.found === 1 ? '' : 's'} found so far`}.
); } if (state.error) { return (