fix(scan): repoint contract leaderboard to real indexer data#96
Conversation
The contract leaderboard's "by calls" / "by gas" tabs crashed on open: they fetched /contracts/stats expecting calls + gas_used per contract, but the indexer never tracks those aggregates (the native block view carries no receipt, so contract_address is never populated). The missing fields hit formatNumber(undefined).toLocaleString() and took down the whole render via the error boundary. Repoint the two tabs to the orderings the indexer actually serves: - Recently Deployed -> /contracts/recent (newest first) - Pioneers -> /contracts/pioneers (earliest first) Both render rank, address, first/last seen block, and code hash through a shared ContractRanking component. The old /calls and /gas paths stay as redirects so existing links don't 404. Drops the now-unused ContractStat type, fetchContractStats, and useContractStats.
📝 WalkthroughWalkthroughThis pull request refactors the contract leaderboard section by replacing stats-based sorting ("top by calls," "top by gas") with deployment-order leaderboards ("recently deployed," "pioneers"). The old client-side leaderboard pages are converted to server-side redirects. A new shared Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/scan/components/leaderboard/ContractRanking.tsx`:
- Around line 30-35: When the dataset changes the existing page state can point
past the new totalPages causing paged to be empty; add an effect that
recalculates totalPages (using data?.length and PAGE_SIZE) and clamps/resets
page via setPage to Math.min(Math.max(1, page), totalPages) (or to 1 when
totalPages is 0) so the current page is valid; implement this behavior wherever
you manage pagination state (references: page, setPage, totalPages, paged,
useMemo, PAGE_SIZE, and the data prop).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b8cf5c59-0929-447a-94dc-f1fa1a67a723
📒 Files selected for processing (10)
apps/scan/app/[locale]/leaderboard/contract/calls/page.tsxapps/scan/app/[locale]/leaderboard/contract/gas/page.tsxapps/scan/app/[locale]/leaderboard/contract/layout.tsxapps/scan/app/[locale]/leaderboard/contract/page.tsxapps/scan/app/[locale]/leaderboard/contract/pioneers/page.tsxapps/scan/app/[locale]/leaderboard/contract/recent/page.tsxapps/scan/components/layout/header.tsxapps/scan/components/leaderboard/ContractRanking.tsxapps/scan/lib/api.tsapps/scan/lib/hooks.ts
| const [page, setPage] = useState(1); | ||
|
|
||
| const totalPages = Math.max(1, Math.ceil((data?.length ?? 0) / PAGE_SIZE)); | ||
| const paged = useMemo( | ||
| () => (data ?? []).slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), | ||
| [data, page], |
There was a problem hiding this comment.
Reset/clamp page when the contract list shrinks to avoid blank tables.
Line 30 keeps prior page state across dataset changes. If the user is on a later page and the new data has fewer pages, Line 34 slices to an empty array while (data?.length ?? 0) > 0, so the table renders with no rows and no empty-state message.
Suggested fix
-import { useMemo, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
@@
const totalPages = Math.max(1, Math.ceil((data?.length ?? 0) / PAGE_SIZE));
+ useEffect(() => {
+ setPage((p) => Math.min(p, totalPages));
+ }, [totalPages]);
+
const paged = useMemo(
() => (data ?? []).slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE),
[data, page],
);Also applies to: 52-77, 98-101
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/scan/components/leaderboard/ContractRanking.tsx` around lines 30 - 35,
When the dataset changes the existing page state can point past the new
totalPages causing paged to be empty; add an effect that recalculates totalPages
(using data?.length and PAGE_SIZE) and clamps/resets page via setPage to
Math.min(Math.max(1, page), totalPages) (or to 1 when totalPages is 0) so the
current page is valid; implement this behavior wherever you manage pagination
state (references: page, setPage, totalPages, paged, useMemo, PAGE_SIZE, and the
data prop).
Problem
The contract leaderboard's By Calls / By Gas Used tabs crashed on open (
/leaderboard/contract/callsis also the default landing for the Contract category). The pages fetched/contracts/statsexpectingcalls+gas_usedper contract, but the indexer never tracks those aggregates — the native block view carries no receipt, sotransactions.contract_addressis never populated. The missing fields hitformatNumber(undefined).toLocaleString()and took down the whole render via the error boundary.Fix
Repoint the two tabs to the orderings the indexer actually serves:
/contracts/recent(newest first)/contracts/pioneers(earliest first)Both render rank, address, first/last seen block, and code hash through a shared
ContractRankingcomponent. The old/callsand/gaspaths stay as redirects so existing links don't 404. Drops the now-unusedContractStattype,fetchContractStats, anduseContractStats.Verification
tsc --noEmitclean/contracts/recentand/contracts/pioneersreturn 200 with the expected shape on testnetSummary by CodeRabbit