Skip to content

/api/stats, lib/stats.ts, and useStats.ts form an entirely dead, permanently-fake statistics subsystem whose live RPC branch is structurally unreachable #130

Description

@prodbycorne

Overview

There is an entirely separate, self-contained "platform statistics" subsystem — src/app/api/stats/route.ts, src/lib/stats.ts, and src/hooks/useStats.ts — that is completely disconnected from the rest of the app and permanently returns fabricated data. lib/stats.ts's fetchStats():

export async function fetchStats(): Promise<StatsData> {
  const factoryId = process.env.NEXT_PUBLIC_FACTORY_CONTRACT_ID;

  if (factoryId) {
    // TODO: wire to Soroban RPC when the factory contract is deployed.
    // Steps: 1. import sorobanRpcUrl ... 6. Return source: "live" once real data is flowing.
  }

  // ── Demo mode ──────────────────────────────────────────────────────────────
  const BASE_TVL_MILLIONS = 302;
  const BASE_USERS = 30_738;
  const sparkline = buildSparkline(BASE_TVL_MILLIONS);
  ...
  return { tvl: formatUsd(tvlRaw), tvlRaw, totalUsers: BASE_USERS, sparkline, lastUpdated: ..., source: "demo" };
}

The if (factoryId) block contains only comments — no return statement — so execution always falls through to the "Demo mode" section regardless of whether a real factory contract is configured. Even once a future contributor follows the TODO's own numbered steps and adds real RPC calls inside that if block, unless they also remember to add a return, their real data will be silently computed and then overwritten by the demo fallback that unconditionally runs immediately afterward — the TODO instructs what to build but the surrounding control flow doesn't leave a slot for it to actually take effect. Separately, and more immediately: nothing in the app currently uses this subsystem at all. grep -rln "useStats" src finds only useStats.ts itself; no page or component imports the useStats() hook. The homepage (src/app/page.tsx) gets its stats from usePlatformStats() (useSorobanQuery.ts), a completely different, already-real (factory-driven) code path. /api/stats is a live, deployed, cached (revalidate = 60) Next.js route with detailed, professional-looking JSDoc ("all clients see fresh data without hammering the RPC") that is reachable by anyone (curl /api/stats) and returns deterministic pseudo-random fake numbers forever, while doing and being used by nothing in the actual product.

Requirements

  • Either wire this subsystem into an actual consumer (fix the missing return and genuinely implement the "live" branch, then use useStats() somewhere), or remove it entirely (route.ts, stats.ts, useStats.ts) if usePlatformStats() is the intended long-term source of truth for platform stats.
  • If kept, fix the unreachable-return structural bug so the TODO's instructions can actually take effect once implemented.
  • If kept but still in demo mode, /api/stats's response should not claim to be live/cached-for-freshness in its own documentation without a source: "demo" field surfaced somewhere a caller can act on (it does return source, but nothing downstream reads or displays it, since nothing downstream exists).

Acceptance Criteria

  • A decision is made and reflected in code: either useStats() is actually consumed by a real page/component, or the entire /api/stats + lib/stats.ts + useStats.ts subsystem is removed.
  • If kept: setting NEXT_PUBLIC_FACTORY_CONTRACT_ID and implementing the TODO's steps actually results in fetchStats() returning source: "live" data — i.e. the if (factoryId) block has a real return path, not a fallthrough.
  • No dead API route remains publicly reachable and documented as if it were part of the live product without any caller.
  • Test coverage exists for whichever outcome is chosen (either a genuine fetchStats() live-branch test, or removal is reflected by deleting stats.test.ts-shaped gaps rather than leaving orphaned untested code).

Additional Notes

More precise references

  • src/lib/stats.ts:53-91 (fetchStats) — confirmed the if (factoryId) { ...only comments... } block (lines 56-72) has no return, and the demo-mode code (lines 74-91) executes unconditionally afterward.
  • src/app/api/stats/route.ts:1-27 — confirmed this is a real, deployed Next.js route (export const revalidate = 60) with JSDoc describing production-grade caching behavior ("Response is cached by Next.js and revalidated every 60 seconds so all clients see fresh data without hammering the RPC").
  • src/hooks/useStats.ts:1-30 — confirmed via grep -rln "useStats" src --include="*.ts*" (excluding the file itself) that this hook has zero consumers anywhere in src/app or src/components.
  • src/app/page.tsx:16-18,83-98 — confirmed the homepage instead uses usePlatformStats/useTotalUserCredits from useSorobanQuery.ts, a wholly separate, factory-driven data path with no relationship to lib/stats.ts.
  • Confirmed no stats.test.ts exists anywhere (find src -iname "*stats*test*" returns nothing), so this entire subsystem — dead or not — has zero test coverage either way.

Additional edge cases

  • useStats.ts's own JSDoc describes a fallback story for static export ("When /api/stats is unavailable (e.g. static GitHub Pages export), the queryFn falls back to calling fetchStats() directly from the browser") — this is a real, thoughtful design for a deployment mode (output: "export") that next.config.ts/README.md reference elsewhere in the project, which makes the complete lack of any consumer even more likely to be an oversight (half-built infrastructure) rather than intentionally-retired code.
  • If this subsystem is kept and wired up, note that BASE_USERS = 30_738 and BASE_TVL_MILLIONS = 302 are the exact same numbers hardcoded independently in src/components/Navbar/Navbar.tsx ("30,738", "$302M") — see the separate Navbar hardcoded-stats issue in this batch; the two should not be fixed independently of each other without noticing they're currently coincidentally-identical fake numbers from two unrelated files.

Implementation sketch (if kept)

if (factoryId) {
  try {
    const { sorobanRpcUrl } = await import("@/config");
    const rpc = new SorobanRpc.Server(sorobanRpcUrl);
    const poolIds = await invokeView(rpc, factoryId, "get_pools", []);
    const lockedTotals = await Promise.all(poolIds.map((id) => invokeView(rpc, id, "get_total_locked", [])));
    const tvlRaw = /* sum + USD conversion */;
    return { tvl: formatUsd(tvlRaw), tvlRaw, totalUsers: /* real count */, sparkline: /* real or omitted */, lastUpdated: new Date().toISOString(), source: "live" };
  } catch (err) {
    console.warn("[SmartDrop] live stats fetch failed, falling back to demo:", err);
    // fall through to demo mode intentionally
  }
}
// demo mode as today

Test/reproduction plan

  • fetchStats() with NEXT_PUBLIC_FACTORY_CONTRACT_ID set and RPC mocked to return real pool data asserts source === "live" and tvl/totalUsers reflect the mocked values, not BASE_TVL_MILLIONS/BASE_USERS.
  • If removed instead: confirm next build succeeds with the route deleted and no remaining imports reference lib/stats.ts/useStats.ts.

Cross-references

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignOfficial Campaign | FWC26Campaign: Official Campaign | FWC26Stellar WaveIssues in the Stellar wave programThird CampaignCampaign: Third CampaignbugSomething isn't workingvery hardExtremely hard — deep expertise, careful design, and significant time required

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions