Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/scan/app/[locale]/tokens/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { EmptyState } from "@/components/common/EmptyState";
import { FetchError } from "@/components/common/FetchError";
import { useNetwork, useNetworkFromQuery } from "@/lib/network-context";
import { useTokens } from "@/lib/hooks";
import { useRefetchOnNewBlock } from "@/lib/ws";
import { formatNumber, shortenAddress } from "@/lib/format";

type SortKey = "supply" | "holders" | "transfers" | "none";
Expand All @@ -26,7 +27,9 @@ export default function TokensPage() {
// Deeplink network switch.
useNetworkFromQuery();
const searchParams = useSearchParams();
const { data: tokens, loading, error, retry } = useTokens(network);
const { data: tokens, loading, error, retry, refetch } = useTokens(network);
// Refresh shortly after each new block instead of waiting for the 30s poll.
useRefetchOnNewBlock(network, refetch);
const [sortKey, setSortKey] = useState<SortKey>("none");
const [sortDir, setSortDir] = useState<"asc" | "desc">("desc");
// ?standard=tokenop|evm pre-selects the filter so the Native rail's
Expand Down
5 changes: 4 additions & 1 deletion apps/scan/app/[locale]/validators/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { StatCard } from "@/components/common/StatCard";
import { EmptyState } from "@/components/common/EmptyState";
import { useNetwork, useNetworkFromQuery } from "@/lib/network-context";
import { useValidators } from "@/lib/hooks";
import { useRefetchOnNewBlock } from "@/lib/ws";
import { formatNumber } from "@/lib/format";

type SortKey = "blocks" | "stake" | "uptime" | "none";
Expand All @@ -32,7 +33,9 @@ export default function ValidatorsPage() {
const { network } = useNetwork();
// Deeplink network switch.
useNetworkFromQuery();
const { data: validators, loading, error, retry } = useValidators(network);
const { data: validators, loading, error, retry, refetch } = useValidators(network);
// Refresh shortly after each new block instead of waiting for the poll cycle.
useRefetchOnNewBlock(network, refetch);
const [sortKey, setSortKey] = useState<SortKey>("none");
const [sortDir, setSortDir] = useState<"asc" | "desc">("desc");
const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
Expand Down
22 changes: 22 additions & 0 deletions apps/scan/lib/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,28 @@ export function useLatestBlock(network: NetworkId): NewHeadEvent | null {
return head;
}

// Trigger `refetch` shortly after each new block so a page reflects chain
// changes without waiting for its full poll interval. Throttled to at most one
// call per `minIntervalMs`: fast block times (testnet ~0.5s) would otherwise
// turn this into a request flood for data that changes slowly (validator set,
// token list), blowing the per-IP rate budget the poll intervals are sized for.
export function useRefetchOnNewBlock(
network: NetworkId,
refetch: () => void,
minIntervalMs = 10_000,
): void {
const head = useLatestBlock(network);
const lastRun = useRef(0);
useEffect(() => {
if (!head) return;
const now = Date.now();
if (now - lastRun.current < minIntervalMs) return;
lastRun.current = now;
refetch();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [head?.number, minIntervalMs]);
}

export function useLatestFinalized(network: NetworkId): number | null {
const [height, setHeight] = useState<number | null>(null);
useEffect(() => {
Expand Down
Loading