diff --git a/apps/scan/app/[locale]/leaderboard/account/active/page.tsx b/apps/scan/app/[locale]/leaderboard/account/active/page.tsx index c687dad..a2eec51 100644 --- a/apps/scan/app/[locale]/leaderboard/account/active/page.tsx +++ b/apps/scan/app/[locale]/leaderboard/account/active/page.tsx @@ -7,6 +7,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Address } from "@/components/common/Address"; import { Pagination } from "@/components/common/Pagination"; import { RankBadge } from "@/components/common/RankBadge"; +import { FetchError } from "@/components/common/FetchError"; import { useNetwork, useNetworkFromQuery } from "@/lib/network-context"; import { useActiveAccounts } from "@/lib/hooks"; import { formatNumber } from "@/lib/format"; @@ -16,7 +17,7 @@ const PAGE_SIZE = 25; export default function MostActiveAccountsPage() { const { network } = useNetwork(); useNetworkFromQuery(); - const { data: accounts, loading } = useActiveAccounts(network, 100); + const { data: accounts, loading, error, retry } = useActiveAccounts(network, 100); const [page, setPage] = useState(1); const totalPages = Math.max(1, Math.ceil((accounts?.length ?? 0) / PAGE_SIZE)); @@ -39,6 +40,8 @@ export default function MostActiveAccountsPage() { ))} + ) : error ? ( + ) : (accounts?.length ?? 0) === 0 ? (
diff --git a/apps/scan/app/[locale]/leaderboard/account/holders/page.tsx b/apps/scan/app/[locale]/leaderboard/account/holders/page.tsx index af4a1a4..f098a29 100644 --- a/apps/scan/app/[locale]/leaderboard/account/holders/page.tsx +++ b/apps/scan/app/[locale]/leaderboard/account/holders/page.tsx @@ -7,6 +7,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Address } from "@/components/common/Address"; import { Pagination } from "@/components/common/Pagination"; import { RankBadge } from "@/components/common/RankBadge"; +import { FetchError } from "@/components/common/FetchError"; import { useNetwork, useNetworkFromQuery } from "@/lib/network-context"; import { useRichlist, useValidators } from "@/lib/hooks"; import { formatNumber } from "@/lib/format"; @@ -17,7 +18,7 @@ const PAGE_SIZE = 25; export default function TopHoldersPage() { const { network } = useNetwork(); useNetworkFromQuery(); - const { data: holders, loading } = useRichlist(network, 100); + const { data: holders, loading, error, retry } = useRichlist(network, 100); const { data: validators } = useValidators(network); const [sortKey, setSortKey] = useState("balance"); const [sortDir, setSortDir] = useState<"asc" | "desc">("desc"); @@ -57,6 +58,8 @@ export default function TopHoldersPage() {
{Array.from({ length: 10 }).map((_, i) => )}
+ ) : error ? ( + ) : sorted.length === 0 ? (
diff --git a/apps/scan/app/[locale]/leaderboard/whale/top/page.tsx b/apps/scan/app/[locale]/leaderboard/whale/top/page.tsx index 49c5d47..983ba57 100644 --- a/apps/scan/app/[locale]/leaderboard/whale/top/page.tsx +++ b/apps/scan/app/[locale]/leaderboard/whale/top/page.tsx @@ -6,6 +6,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; import { Address } from "@/components/common/Address"; import { EmptyState } from "@/components/common/EmptyState"; +import { FetchError } from "@/components/common/FetchError"; import { RankBadge } from "@/components/common/RankBadge"; import { useNetwork, useNetworkFromQuery } from "@/lib/network-context"; import { useRichlist, useValidators } from "@/lib/hooks"; @@ -18,7 +19,7 @@ const DEFAULT_THRESHOLD = 100_000; // SRX export default function WhaleTopWalletsPage() { const { network } = useNetwork(); useNetworkFromQuery(); - const { data: holders, loading } = useRichlist(network, 100); + const { data: holders, loading, error, retry } = useRichlist(network, 100); const { data: validators } = useValidators(network); const [threshold, setThreshold] = useState(DEFAULT_THRESHOLD); @@ -60,6 +61,8 @@ export default function WhaleTopWalletsPage() {
{Array.from({ length: 6 }).map((_, i) => )}
+ ) : error ? ( + ) : whales.length === 0 ? ( (null); const [loadingOther, setLoadingOther] = useState(true); - const { data: holders, loading: holdersLoading } = useTokenHolders(network, addr, 50); - const { data: trades, loading: tradesLoading } = useTokenTrades(network, addr, 1, 25); + const { data: holders, loading: holdersLoading, error: holdersError, retry: retryHolders } = useTokenHolders(network, addr, 50); + const { data: trades, loading: tradesLoading, error: tradesError, retry: retryTrades } = useTokenTrades(network, addr, 1, 25); useEffect(() => { setLoading(true); @@ -153,6 +154,8 @@ export default function TokenDetailPage({ params }: { params: Promise<{ addr: st
{Array.from({ length: 6 }).map((_, i) => )}
+ ) : tradesError ? ( + ) : trades && trades.length > 0 ? (
@@ -201,6 +204,8 @@ export default function TokenDetailPage({ params }: { params: Promise<{ addr: st
{Array.from({ length: 6 }).map((_, i) => )}
+ ) : holdersError ? ( + ) : holders && holders.length > 0 ? (
diff --git a/apps/scan/lib/api.ts b/apps/scan/lib/api.ts index 74ef171..fb61a43 100644 --- a/apps/scan/lib/api.ts +++ b/apps/scan/lib/api.ts @@ -971,9 +971,10 @@ interface RawRichlistEntry { percent_of_supply?: number; } -export async function fetchRichlist(network: NetworkId, limit = 100): Promise { +export async function fetchRichlist(network: NetworkId, limit = 100): Promise { const res = await apiFetch<{ holders: RawRichlistEntry[] }>(network, `/richlist?limit=${limit}`); - if (!res?.holders) return []; + if (res === null) return null; + if (!res.holders) return []; return res.holders.map((h, i) => ({ rank: i + 1, address: h.address, @@ -994,12 +995,13 @@ export async function fetchTokenHolders( network: NetworkId, contract: string, limit = 50, -): Promise { +): Promise { const res = await apiFetch<{ holders: RawTokenHolder[] }>( network, `/tokens/${normalizeAddress(contract)}/holders?limit=${limit}`, ); - if (!res?.holders) return []; + if (res === null) return null; + if (!res.holders) return []; return res.holders.map((h) => ({ address: h.address, // 2026-04-30 audit: balance_sentri is the raw 1e8-scaled value; older @@ -1026,13 +1028,14 @@ export async function fetchTokenTrades( contract: string, page = 1, limit = 20, -): Promise { +): Promise { const offset = (page - 1) * limit; const res = await apiFetch<{ trades: RawTokenTrade[] }>( network, `/tokens/${normalizeAddress(contract)}/trades?limit=${limit}&offset=${offset}`, ); - if (!res?.trades) return []; + if (res === null) return null; + if (!res.trades) return []; return res.trades.map((t) => ({ tx_hash: t.txid ?? t.tx_hash ?? "", from: t.from, @@ -1400,9 +1403,10 @@ export interface ActiveAccount { tx_count: number; } -export async function fetchActiveAccounts(network: NetworkId, limit = 100): Promise { +export async function fetchActiveAccounts(network: NetworkId, limit = 100): Promise { const res = await apiFetch<{ accounts: ActiveAccount[] }>(network, `/accounts/active?limit=${limit}`); - return res?.accounts ?? []; + if (res === null) return null; + return res.accounts ?? []; } // ── /contracts/recent ───────────────────────────────────────────────────────