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 ───────────────────────────────────────────────────────