From d85680e4d6feba06c677143c7300f76234e42ae9 Mon Sep 17 00:00:00 2001 From: ignacio-rsg Date: Thu, 2 Jul 2026 12:50:59 +0300 Subject: [PATCH] fix(index): show 30d chain metrics --- apps/api/src/__tests__/routes-index.test.ts | 63 ++++++++++++++++++++- apps/api/src/routes/index.ts | 12 ++-- apps/web/src/app/page.tsx | 6 +- 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/apps/api/src/__tests__/routes-index.test.ts b/apps/api/src/__tests__/routes-index.test.ts index c06e04b..340bb83 100644 --- a/apps/api/src/__tests__/routes-index.test.ts +++ b/apps/api/src/__tests__/routes-index.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import Fastify from "fastify"; import { errorHandlerPlugin } from "../plugins/error-handler.js"; @@ -20,6 +20,7 @@ async function buildApp() { } beforeEach(() => vi.resetAllMocks()); +afterEach(() => vi.useRealTimers()); describe("GET /index/stats", () => { it("returns aggregate stats enriched with latest block height", async () => { @@ -44,6 +45,66 @@ describe("GET /index/stats", () => { }); }); +describe("GET /index/top-chains", () => { + it("returns top chains using 30-day relay data sorted by CU", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2025-02-15T12:00:00Z")); + (gqlSafe as ReturnType).mockResolvedValue({ + allMvRelayDailies: { + groupedAggregates: [ + { keys: ["LOWCU"], sum: { cu: "100", relays: "10000" } }, + { keys: ["HIGHCU"], sum: { cu: "500", relays: "1" } }, + { keys: ["MIDCU"], sum: { cu: "300", relays: "500" } }, + ], + }, + }); + + const app = await buildApp(); + const res = await app.inject({ method: "GET", url: "/index/top-chains" }); + + expect(res.statusCode).toBe(200); + expect(gqlSafe).toHaveBeenCalledWith( + expect.stringContaining("query($since: Date!)"), + { since: "2025-01-16" }, + null, + ); + expect(JSON.parse(res.body)).toEqual({ + data: [ + { specId: "HIGHCU", totalCu: "500", totalRelays: "1" }, + { specId: "MIDCU", totalCu: "300", totalRelays: "500" }, + { specId: "LOWCU", totalCu: "100", totalRelays: "10000" }, + ], + }); + }); + + it("returns only the top 20 chains", async () => { + (gqlSafe as ReturnType).mockResolvedValue({ + allMvRelayDailies: { + groupedAggregates: Array.from({ length: 25 }, (_, i) => ({ + keys: [`CHAIN${i}`], + sum: { cu: String(25 - i), relays: String(i) }, + })), + }, + }); + + const app = await buildApp(); + const res = await app.inject({ method: "GET", url: "/index/top-chains" }); + + expect(res.statusCode).toBe(200); + expect(JSON.parse(res.body).data).toHaveLength(20); + }); + + it("returns empty data when indexer is down", async () => { + (gqlSafe as ReturnType).mockResolvedValue(null); + + const app = await buildApp(); + const res = await app.inject({ method: "GET", url: "/index/top-chains" }); + + expect(res.statusCode).toBe(200); + expect(JSON.parse(res.body)).toEqual({ data: [] }); + }); +}); + describe("GET /index/charts", () => { it("returns empty data when indexer is down", async () => { (gqlSafe as ReturnType).mockResolvedValue(null); diff --git a/apps/api/src/routes/index.ts b/apps/api/src/routes/index.ts index 40f579f..406b75d 100644 --- a/apps/api/src/routes/index.ts +++ b/apps/api/src/routes/index.ts @@ -57,22 +57,26 @@ export async function indexRoutes(app: FastifyInstance) { app.get("/top-chains", { schema: { tags: ["Index"], - summary: "Top 20 chains by alltime CU", + summary: "Top 20 chains by 30-day CU", }, config: { cacheTTL: CACHE_TTL.LIST }, }, async () => { + const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) + .toISOString() + .slice(0, 10); + const data = await gqlSafe<{ allMvRelayDailies: { groupedAggregates: Array<{ keys: string[]; sum: { cu: string; relays: string } }>; }; - } | null>(`{ - allMvRelayDailies { + } | null>(`query($since: Date!) { + allMvRelayDailies(filter: { date: { greaterThanOrEqualTo: $since } }) { groupedAggregates(groupBy: CHAIN_ID) { keys sum { cu relays } } } - }`, undefined, null); + }`, { since: thirtyDaysAgo }, null); if (!data) return { data: [] }; diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index b300258..81b9d12 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -152,12 +152,12 @@ export default function DashboardPage() { cell: ({ row }) => , }, { - id: "totalCu", header: "Total CU", + id: "totalCu", header: "CU (30 Days)", sortingFn: (a, b) => Number(BigInt(a.original.totalCu || "0") - BigInt(b.original.totalCu || "0")), cell: ({ row }) => {formatNumberKMB(row.original.totalCu)}, }, { - id: "totalRelays", header: "Total Relays", + id: "totalRelays", header: "Relays (30 Days)", sortingFn: (a, b) => Number(BigInt(a.original.totalRelays || "0") - BigInt(b.original.totalRelays || "0")), cell: ({ row }) => {formatNumberKMB(row.original.totalRelays)}, }, @@ -362,7 +362,7 @@ export default function DashboardPage() { {/* Desktop: sortable table */}
- +