Severity: High
Source
Cross-cutting — 9 files across Earn components
Description
There are zero Number.isFinite() guards anywhere in the Earn display pipeline. A single corrupt upstream value (RPC returning bad data, API returning malformed numbers) cascades to every display boundary, rendering literal "NaN", "NaN%", or "$NaN" in the DOM.
formatCompact (defined independently in 4 files) has the same vulnerable pattern everywhere:
function formatCompact(n: number): string {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
return n.toFixed(2); // NaN falls through all guards -> "NaN"
}
Affected files
| File |
What renders |
components/earn/VaultCard.tsx:54,63,67,71,89 |
"NaN%", "$NaN" on .toFixed() |
components/earn/LpPositionDashboard.tsx:134,137,144 |
"NaN" share value, APY |
components/earn/OiCapMeter.tsx:57,67 |
width: NaN% (bar invisible) |
components/earn/EarnHeader.tsx:53-94 |
AnimatedNumber receives NaN |
components/earn/InsuranceFundDisplay.tsx:69-75,123 |
Same |
app/earn/[slab]/page.tsx:432 |
formatCompact(NaN) renders "NaN" |
Fix
- Add a single shared
formatCompact in lib/format.ts with Number.isFinite(n) guard, returning "—" for invalid values
- Replace all 4 independent copies with imports
- Add
Number.isFinite before every .toFixed() call and AnimatedNumber value prop in the Earn component tree
Severity: High
Source
Cross-cutting — 9 files across Earn components
Description
There are zero
Number.isFinite()guards anywhere in the Earn display pipeline. A single corrupt upstream value (RPC returning bad data, API returning malformed numbers) cascades to every display boundary, rendering literal "NaN", "NaN%", or "$NaN" in the DOM.formatCompact(defined independently in 4 files) has the same vulnerable pattern everywhere:Affected files
components/earn/VaultCard.tsx:54,63,67,71,89components/earn/LpPositionDashboard.tsx:134,137,144components/earn/OiCapMeter.tsx:57,67components/earn/EarnHeader.tsx:53-94components/earn/InsuranceFundDisplay.tsx:69-75,123app/earn/[slab]/page.tsx:432Fix
formatCompactinlib/format.tswithNumber.isFinite(n)guard, returning"—"for invalid valuesNumber.isFinitebefore every.toFixed()call andAnimatedNumbervalue prop in the Earn component tree