Skip to content

perf(dashboard): drop 12.7k lines of mocked datasets from the client bundle - #2095

Open
pikonha wants to merge 3 commits into
devfrom
work/DEV-834
Open

perf(dashboard): drop 12.7k lines of mocked datasets from the client bundle#2095
pikonha wants to merge 3 commits into
devfrom
work/DEV-834

Conversation

@pikonha

@pikonha pikonha commented Jul 30, 2026

Copy link
Copy Markdown
Member

Description

Two dashboard bundle/perf items from the 2026-06-11 repo audit (DEV-834).

1. Mocked chart datasets (12,746 lines) no longer ship to every DAO.

  • mocked-token-dist-datasets.ts (6,884 lines / 161.6K) had zero references anywhere in source — it was dead code, not a lazy-load candidate. Deleted outright rather than dynamically imported.
  • mocked-attack-profitability-datasets.ts (5,862 lines) is now loaded with a dynamic import(), only when the research-pending blur is active. It was read inside a useMemo gated on async SWR data, so await import() couldn't live there — it needed useState + useEffect with an unmount guard.

2. next/image no longer accepts arbitrary remote hosts.

  • Replaced remotePatterns: [{ https, ** }, { http, ** }] with an explicit five-host allowlist.
  • Removed the deprecated domains key (its hosts are now covered by remotePatterns).
  • Dropped plaintext http entirely — no image src in the app uses it.

Allowed hosts, each justified: euc.li, ensdata.net, static.ricmoo.com, www.ricmoo.com (previously in domains), and raw.githubusercontent.com (Trust Wallet token logos, features/create-proposal/constants.ts:25AddTransferModal.tsx:350). Service-provider avatars come from remote config and already render with unoptimized, so they are unaffected.

Steps to reproduce

Open any DAO's attack-profitability page. For a DAO with real data the mocked dataset is never fetched; for a DAO in research-pending state it loads on demand behind the existing blur.

Related Issue

DEV-834 — https://app.clickup.com/t/86aj0kb5t

Changes

  • Code refactoring
  • New feature implementation
  • Bug fix
  • Documentation
  • Infrastructure

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable? — no new tests; this is a bundling change with no behaviour change
  • Have you successfully run tests with your changes locally? — see verification below
  • Is it available on the remote server?

Verification

Check Result
pnpm typecheck pass
pnpm lint pass — 0 errors, 82 pre-existing warnings
pnpm test (jest) pass — 41 suites, 240 tests
pnpm build pass
pnpm test:e2e blocked, see below

Bundle evidence (DoD item 1). No bundle analyzer is installed, so this uses build output — .next/diagnostics/route-bundle-stats.json, which lists firstLoadChunkPaths per route:

  • The dataset lives entirely in 0xs7ldph~44hu.js (66KB, all 366 treasuryNonDAO datapoints), and that chunk appears in no route's firstLoadChunkPaths across all 37 routes.
  • The two first-load chunks that mention mockedAttackProfitabilityDatasets contain only the call site — 3 incidental occurrences each, visible in the minified output as e.A(283768).then(e => F(e.mockedAttackProfitabilityDatasets)).

E2E is blocked in this environment (DoD item 2 is NOT verified). The suite fails 55/59 with toBeVisible() timeouts on headings, tables, and tabs — every page renders empty because apps/dashboard/.env is missing ANTICAPTURE_API_URL (plus ERPC_URL, COINGECKO_API_KEY, and 9 others). No failure mentions images, but that is not evidence images work — those tests never got far enough to load one.

A reviewer with a working environment should confirm images still render across DAO pages, particularly service-provider avatars and the Trust Wallet token logos in the transfer modal. That is the one DoD line this PR cannot self-certify.

Additional Notes

.gitignore gains .work/ — local run state for the tooling that produced this PR, never committed.


Note

Medium Risk
Tightening remotePatterns can break remote images if any production URL uses a host not on the allowlist; mocked-chart behavior depends on the dynamic import path when research-pending.

Overview
Shrinks the dashboard client bundle by removing ~12.7k lines of static mocked chart data and tightening image remote configuration.

Mocked chart data: Deletes the unused mocked-token-dist-datasets module entirely. For attack profitability, removes the static import of mocked-attack-profitability-datasets and dynamically imports it only when time-series data is empty (research-pending / blur state), using useState + useEffect with an unmount guard so the chart uses {} until the chunk loads.

next/image: Replaces wildcard https/http remotePatterns with a five-host HTTPS allowlist (euc.li, ensdata.net, Ricmoo static hosts, raw.githubusercontent.com) and removes the deprecated domains key and plaintext http.

Also ignores local .work/ in .gitignore.

Reviewed by Cursor Bugbot for commit 45bf07e. Configure here.

…bundle

Delete the unreferenced token-distribution dataset (6,884 lines) and load the
attack-profitability dataset dynamically, only when the research-pending blur
is active.

Replace the wildcard next/image remotePatterns with an explicit host allowlist
and drop the deprecated domains key.

DEV-834

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
anticapture-storybook Ready Ready Preview Jul 30, 2026 12:26am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
anticapture Ignored Ignored Jul 30, 2026 12:26am

Request Review

@railway-app

railway-app Bot commented Jul 30, 2026

Copy link
Copy Markdown

🚅 Deployed to the anticapture-pr-2095 environment in anticapture-infra

Service Status Web Updated (UTC)
ens-indexer-offchain ✅ Success (View Logs) Jul 30, 2026 at 8:17 am
uniswap-indexer-offchain ✅ Success (View Logs) Jul 30, 2026 at 8:17 am
shutter-indexer-offchain ✅ Success (View Logs) Jul 30, 2026 at 8:17 am
compound-indexer-offchain ✅ Success (View Logs) Jul 30, 2026 at 8:15 am
gitcoin-indexer-offchain ✅ Success (View Logs) Jul 30, 2026 at 8:15 am
prometheus ✅ Success (View Logs) Jul 30, 2026 at 1:13 am
grafana ✅ Success (View Logs) Web Jul 30, 2026 at 1:13 am
otelcol ✅ Success (View Logs) Jul 30, 2026 at 12:25 am
authful ✅ Success (View Logs) Web Jul 30, 2026 at 12:25 am
loki ✅ Success (View Logs) Jul 30, 2026 at 12:25 am
tempo ✅ Success (View Logs) Jul 30, 2026 at 12:25 am
gateful ✅ Success (View Logs) Web Jul 30, 2026 at 12:25 am
alertmanager ✅ Success (View Logs) Web Jul 30, 2026 at 12:24 am
mcp ✅ Success (View Logs) Web Jul 30, 2026 at 12:15 am
docs ✅ Success (View Logs) Web Jul 30, 2026 at 12:15 am
gitcoin-api ✅ Success (View Logs) Jul 30, 2026 at 12:15 am
obol-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
fluid-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
lil-nouns-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
obol-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
tornado-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
ens-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
shutter-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
ens-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
tornado-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
shutter-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
lil-nouns-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
gitcoin-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
uniswap-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
nouns-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
address-enrichment ✅ Success (View Logs) Web Jul 30, 2026 at 12:14 am
compound-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
aave-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
scroll-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
fluid-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
compound-api ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
aave-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
uniswap-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
nouns-indexer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
ens-relayer ✅ Success (View Logs) Jul 30, 2026 at 12:14 am
erpc ✅ Success (View Logs) Web Jul 30, 2026 at 12:14 am
nodeful ✅ Success (View Logs) Jul 30, 2026 at 12:13 am
user-api ✅ Success (View Logs) Web Jul 30, 2026 at 12:12 am
scroll-api ✅ Success (View Logs) Jul 30, 2026 at 12:12 am

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: OAuth avatar hosts missing
    • Added lh3.googleusercontent.com and avatars.githubusercontent.com to next/image remotePatterns so Google and GitHub OAuth profile images pass Next.js validation.
  • ✅ Fixed: Blur shows empty chart briefly
    • Preload mocked datasets on mount and extend the loading state until they resolve so the chart never renders empty under the research-pending blur.

Create PR

Or push these changes by commenting:

@cursor push 2859a26171
Preview (2859a26171)
diff --git a/apps/dashboard/features/attack-profitability/components/MultilineChartAttackProfitability.tsx b/apps/dashboard/features/attack-profitability/components/MultilineChartAttackProfitability.tsx
--- a/apps/dashboard/features/attack-profitability/components/MultilineChartAttackProfitability.tsx
+++ b/apps/dashboard/features/attack-profitability/components/MultilineChartAttackProfitability.tsx
@@ -98,7 +98,7 @@
   > | null>(null);
 
   useEffect(() => {
-    if (!mocked || mockedDatasets !== null) return;
+    if (mockedDatasets !== null) return;
     let mounted = true;
     import("@/shared/constants/mocked-data/mocked-attack-profitability-datasets")
       .then((m) => {
@@ -110,7 +110,7 @@
     return () => {
       mounted = false;
     };
-  }, [mocked, mockedDatasets]);
+  }, [mockedDatasets]);
 
   const quorumValue = Number(
     formatUnits(daoData?.quorum || 0n, daoConfig.decimals),
@@ -254,7 +254,9 @@
   }, [chartData, mocked, setCsvData]);
 
   const isLoading =
-    isLoadingDaoTokenPriceHistoricalData || isLoadingTimeSeriesData;
+    isLoadingDaoTokenPriceHistoricalData ||
+    isLoadingTimeSeriesData ||
+    (mocked && mockedDatasets === null);
 
   if (isLoading) {
     return (

diff --git a/apps/dashboard/next.config.ts b/apps/dashboard/next.config.ts
--- a/apps/dashboard/next.config.ts
+++ b/apps/dashboard/next.config.ts
@@ -9,6 +9,8 @@
       { protocol: "https", hostname: "static.ricmoo.com" },
       { protocol: "https", hostname: "www.ricmoo.com" },
       { protocol: "https", hostname: "raw.githubusercontent.com" },
+      { protocol: "https", hostname: "lh3.googleusercontent.com" },
+      { protocol: "https", hostname: "avatars.githubusercontent.com" },
     ],
     dangerouslyAllowSVG: true,
     contentDispositionType: "attachment",

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 45bf07e. Configure here.

{ protocol: "https", hostname: "ensdata.net" },
{ protocol: "https", hostname: "static.ricmoo.com" },
{ protocol: "https", hostname: "www.ricmoo.com" },
{ protocol: "https", hostname: "raw.githubusercontent.com" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OAuth avatar hosts missing

Medium Severity

Replacing wildcard remotePatterns with a five-host allowlist leaves out common OAuth profile image hosts (e.g. lh3.googleusercontent.com, avatars.githubusercontent.com). SessionAccountButton passes user.image to next/image without unoptimized, so provider avatars for Google or GitHub sign-in can fail Next.js remote-image validation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 45bf07e. Configure here.

return () => {
mounted = false;
};
}, [mocked, mockedDatasets]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blur shows empty chart briefly

Low Severity

In research-pending mode, chartData uses mockedDatasets ?? {} while the dynamic import is still in flight, so the chart renders with no series until the chunk resolves. The blur overlay appears immediately, but placeholder lines that used to ship synchronously are missing for that window.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 45bf07e. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45bf07efed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +7 to +11
{ protocol: "https", hostname: "euc.li" },
{ protocol: "https", hostname: "ensdata.net" },
{ protocol: "https", hostname: "static.ricmoo.com" },
{ protocol: "https", hostname: "www.ricmoo.com" },
{ protocol: "https", hostname: "raw.githubusercontent.com" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve dynamic avatar sources in the image allowlist

When a user signs in through Google, Better Auth stores the OIDC picture in user.image, and SessionAccountButton.tsx:69-74 passes that typically lh3.googleusercontent.com URL to optimized next/image. Because this allowlist omits that host, Next throws a “hostname is not configured” error while rendering the account button. It also omits metadata.ens.domains, the documented ENS avatar source emitted by address enrichment and rendered by EnsAvatar.tsx:170-180, so holder and delegate rows can fail similarly. Add or proxy these dynamic avatar hosts, or render those images unoptimized, before removing the wildcard patterns.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

🎨 UI Review

Automated review · Figma: not found (linked ClickUp task DEV-834 has no Figma URL on record, no description/comments) · Preview: not reachable
ℹ️ Reviewed without a Figma reference — grounded in diff/repo review + UX-expert evaluation. The PR's dashboard Vercel deployment shows as Ignored/Skipped (only the unrelated anticapture-storybook project built), so findings below are code-level, not visually confirmed against a live preview.


Global — next/image remotePatterns tightening

Must-fix — ENS avatar in the wallet-connect chip has no fallback and can now silently break
The new remotePatterns allowlist (apps/dashboard/next.config.ts) restricts images to 5 hosts (euc.li, ensdata.net, static.ricmoo.com, www.ricmoo.com, raw.githubusercontent.com) with no wildcard fallback — before this PR, remotePatterns allowed https://**/http://**, so any host worked. account.ensAvatar (RainbowKit/viem's resolved ENS avatar) is rendered directly through next/image with no unoptimized and no onError fallback in three places: ConnectWallet.tsx (~line 100), ConnectWalletCustom.tsx, and WhitelabelConnectWallet.tsx (same pattern in all three). ENS avatar text records can point to any host — IPFS gateways, NFT-metadata CDNs, personal domains — not just the five listed here. A connected wallet whose ENS avatar resolves to an unlisted host will now render a broken image in the header connect button, where before it always rendered. [Code-only]

Contrast with shared/components/design-system/avatars/ens-avatar/EnsAvatar.tsx (used in delegate tables, address tooltips, etc.), which already handles exactly this case via onError={handleImageError} falling back to a Blockies identicon. Recommend mirroring that fallback in the three wallet-chip components — cheaper than trying to enumerate every possible ENS avatar host.

Nice-to-have — confirm whether SessionAccountButton.tsx's user.image can resolve to an unlisted host
Same pattern (next/image, no unoptimized, no onError) is used for user.image (shared/components/auth/SessionAccountButton.tsx, sourced from the auth session; the file's own comment references "magic link / Google"). Couldn't confirm a live social-login provider producing external avatar URLs is wired up today, so this may be no-op risk right now — but if/when one is added, it'll break the same way, silently. Worth a quick confirmation either way. [Code-only]


Attack Profitability chart — dynamic import of the mocked dataset

Validated against the code — no change needed
Checked whether the new async import() (resolving only after mount) could flash an empty chart before the mocked dataset loads. It can't: {mocked && <ResearchPendingChartBlur />} renders unconditionally whenever mocked is true, independent of whether mockedDatasets has resolved yet — so the blur overlay already covers the chart before, during, and after the dynamic import settles. No loading-state gap is visible to the user. [Code-only]

Repo-wide check

Confirmed mocked-token-dist-datasets.ts (deleted, 6,884 lines) has zero remaining references anywhere in the codebase — the PR's dead-code claim holds up. [Code-only]

Scope note

This is a UI-only review — architecture, hook structure, and other code-quality concerns are left to the code reviewer.


Generated by Claude Code

…cked data loads

Restricting remotePatterns broke OAuth profile images: user.image comes from
the social provider (Google avatars are served from googleusercontent
subdomains), so the host can't be allowlisted at build time. Mark that image
unoptimized, matching how service-provider avatars already handle
config-driven hosts.

Also fold the in-flight mocked-dataset import into the existing loading
condition, so research-pending charts show the loader instead of an empty
chart while the lazy chunk resolves.

Both raised by Cursor Bugbot on #2095.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🔍 Vercel preview: https://anticapture-dwtkc3jjg-ful.vercel.app

ens.avatar passes through the ENS avatar text record, which any name owner
can point at any host, so the remotePatterns allowlist can never cover it.
Mark the image unoptimized, after the imageProps spread so a caller can't
re-enable optimization for a host we can't validate.

Raised by chatgpt-codex-connector on #2095.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@pikonha

pikonha commented Jul 30, 2026

Copy link
Copy Markdown
Member Author

Automated review pass complete — ready for human review

Three commits. Both automated reviewers were addressed; the loop is now handing off.

Review feedback addressed

Finding Reviewer Resolution
OAuth avatar hosts missing (Medium) Cursor Bugbot unoptimized on SessionAccountButton.tsxuser.image is a provider-supplied host (d2ba8cb0e)
Blur shows empty chart briefly (Low) Cursor Bugbot Folded the in-flight import into the existing isLoading condition, so the existing Lottie loader shows instead of an empty chart (d2ba8cb0e)
Preserve dynamic avatar sources (P1) chatgpt-codex-connector unoptimized on EnsAvatar.tsx (0308f4198) — see note below

Note on the P1

The review named metadata.ens.domains, but that host appears only as an OpenAPI example. The real value is ens?.avatar (EnsAvatar.tsx:122address-enrichment/src/clients/ens.ts:128), which passes through the ENS avatar text record — any name owner can point it at any host. No allowlist can cover that, so unoptimized is the fix rather than adding hosts. It's placed after the {...imageProps} spread so a caller can't re-enable optimization for a host we can't validate.

The Google half of that P1 was already fixed in d2ba8cb0e before the review landed.

All three avatar surfaces in this app now render unoptimized, because all three take provider- or user-controlled hosts: service providers (pre-existing), OAuth, and ENS. A follow-up worth considering: proxy avatars through our own origin so optimization can be restored. That's a bigger change than this ticket and should be its own PR.

Verification

pnpm typecheck ✓ · pnpm lint ✓ (0 errors, 82 pre-existing warnings) · pnpm test ✓ (41 suites, 240 tests) · pnpm build

Bundle claim re-verified after every commit: the dataset chunk 0xs7ldph~44hu.js (65KB, all 366 datapoints) appears in zero of 37 routes' firstLoadChunkPaths.

Still open for a human

E2E was never run successfully. The suite fails 55/59 locally because apps/dashboard/.env lacks ANTICAPTURE_API_URL and 11 others, so pages render empty and every toBeVisible() times out. No failure mentions images — but those tests never got far enough to load one, so that is not evidence.

The ticket's DoD line "all images across DAO pages still render (E2E green)" remains unverified. Given this PR narrowed image host validation and three separate components needed fixes for it, please confirm avatars render on holders/delegates rows, the account button, service-provider tables, and the transfer-modal token logos in a working environment.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0308f41985

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +7 to +11
{ protocol: "https", hostname: "euc.li" },
{ protocol: "https", hostname: "ensdata.net" },
{ protocol: "https", hostname: "static.ricmoo.com" },
{ protocol: "https", hostname: "www.ricmoo.com" },
{ protocol: "https", hostname: "raw.githubusercontent.com" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve connected-wallet ENS avatar sources

When a connected wallet has an ENS avatar stored on any host outside this list, RainbowKit passes the resolved record URL through account.ensAvatar, and the optimized next/image instances in ConnectWallet.tsx:112-119, ConnectWalletCustom.tsx:86-93, and WhitelabelConnectWallet.tsx:80-87 throw the unconfigured-host error while rendering wallet controls. Fresh evidence after the prior avatar fixes is that these three renderers remain optimized; Viem preserves arbitrary HTTPS avatar URLs and maps IPFS/Arweave records to ipfs.io/arweave.net, neither of which is allowed here. Render these dynamic wallet avatars unoptimized or proxy/allow their sources.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants