Skip to content

Commit 1c5ba5f

Browse files
authored
Merge pull request #357 from ethpandaops/refactor/ref-node-modal
refactor: extract reference-nodes dialog into reusable component
2 parents 3834619 + 61c46e3 commit 1c5ba5f

File tree

4 files changed

+122
-79
lines changed

4 files changed

+122
-79
lines changed

src/pages/ethereum/execution/timings/IndexPage.tsx

Lines changed: 2 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import { Header } from '@/components/Layout/Header';
66
import { Tab } from '@/components/Navigation/Tab';
77
import { ScrollableTabs } from '@/components/Navigation/ScrollableTabs';
88
import { Alert } from '@/components/Feedback/Alert';
9-
import { Dialog } from '@/components/Overlays/Dialog';
109
import { Checkbox } from '@/components/Forms/Checkbox';
1110
import { useTabState } from '@/hooks/useTabState';
1211
import { useEngineTimingsData } from './hooks/useEngineTimingsData';
1312
import { TimingsSkeleton } from './components/TimingsSkeleton';
1413
import { NewPayloadTab } from './components/NewPayloadTab';
1514
import { GetBlobsTab } from './components/GetBlobsTab';
15+
import { ReferenceNodesInfoDialog } from './components/ReferenceNodesInfoDialog';
1616
import type { TimeRange } from './IndexPage.types';
1717

1818
/**
@@ -116,77 +116,7 @@ export function IndexPage(): JSX.Element {
116116
</div>
117117

118118
{/* Reference Node Info Dialog */}
119-
<Dialog open={showRefNodeInfo} onClose={() => setShowRefNodeInfo(false)} title="About Reference Nodes" size="lg">
120-
<div className="space-y-4 text-sm leading-relaxed text-muted">
121-
<p>
122-
<span className="font-medium text-foreground">
123-
Reference nodes are controlled by ethPandaOps and follow EIP-7870 hardware specifications.
124-
</span>{' '}
125-
This EIP establishes standardized hardware and bandwidth recommendations to ensure consistent, meaningful
126-
benchmark comparisons across the Ethereum network.
127-
</p>
128-
<p>
129-
EIP-7870 defines three node types with specific requirements: Full Nodes for chain following, Attesters for
130-
validators, and Local Block Builders for block proposal. Each has distinct hardware and bandwidth needs.
131-
</p>
132-
<div className="overflow-x-auto">
133-
<table className="w-full text-left text-xs">
134-
<thead>
135-
<tr className="border-b border-border">
136-
<th className="py-2 pr-4 font-medium text-foreground">Node Type</th>
137-
<th className="py-2 pr-4 font-medium text-foreground">RAM</th>
138-
<th className="py-2 pr-4 font-medium text-foreground">CPU</th>
139-
<th className="py-2 font-medium text-foreground">Bandwidth</th>
140-
</tr>
141-
</thead>
142-
<tbody className="text-muted">
143-
<tr className="border-b border-border/50">
144-
<td className="py-2 pr-4">Full Node</td>
145-
<td className="py-2 pr-4">32 GB</td>
146-
<td className="py-2 pr-4">4c/8t</td>
147-
<td className="py-2">50/15 Mbps</td>
148-
</tr>
149-
<tr className="border-b border-border/50">
150-
<td className="py-2 pr-4">Attester</td>
151-
<td className="py-2 pr-4">64 GB</td>
152-
<td className="py-2 pr-4">8c/16t</td>
153-
<td className="py-2">50/25 Mbps</td>
154-
</tr>
155-
<tr>
156-
<td className="py-2 pr-4">Local Block Builder</td>
157-
<td className="py-2 pr-4">64 GB</td>
158-
<td className="py-2 pr-4">8c/16t</td>
159-
<td className="py-2">100/50 Mbps</td>
160-
</tr>
161-
</tbody>
162-
</table>
163-
</div>
164-
<div className="rounded-sm border border-accent/20 bg-accent/5 p-3">
165-
<p className="text-foreground">
166-
<span className="font-medium">Why filter by reference nodes?</span>{' '}
167-
<span className="italic">
168-
This ensures timing data reflects consistent, spec-compliant hardware rather than varied community
169-
setups.
170-
</span>
171-
</p>
172-
</div>
173-
<p>
174-
All reference nodes use 4 TB NVMe storage. By filtering to reference nodes, you see Engine API performance
175-
on standardized infrastructure, making it easier to identify client-specific behavior versus
176-
hardware-related variance.
177-
</p>
178-
<p className="text-xs">
179-
<a
180-
href="https://eips.ethereum.org/EIPS/eip-7870"
181-
target="_blank"
182-
rel="noopener noreferrer"
183-
className="text-accent hover:underline"
184-
>
185-
Read EIP-7870 specification
186-
</a>
187-
</p>
188-
</div>
189-
</Dialog>
119+
<ReferenceNodesInfoDialog open={showRefNodeInfo} onClose={() => setShowRefNodeInfo(false)} />
190120

191121
{/* Tabbed Content */}
192122
<TabGroup selectedIndex={selectedIndex} onChange={onChange}>
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import type { JSX } from 'react';
2+
import { Dialog } from '@/components/Overlays/Dialog';
3+
4+
export interface ReferenceNodesInfoDialogProps {
5+
open: boolean;
6+
onClose: () => void;
7+
}
8+
9+
/**
10+
* Dialog explaining reference nodes and EIP-7870 hardware specifications.
11+
* Used across engine timing views to provide context about the data source.
12+
*/
13+
export function ReferenceNodesInfoDialog({ open, onClose }: ReferenceNodesInfoDialogProps): JSX.Element {
14+
return (
15+
<Dialog open={open} onClose={onClose} title="About Reference Nodes" size="lg">
16+
<div className="space-y-4 text-sm leading-relaxed text-muted">
17+
<p>
18+
<span className="font-medium text-foreground">
19+
Reference nodes are controlled by ethPandaOps and follow EIP-7870 hardware specifications.
20+
</span>{' '}
21+
This EIP establishes standardized hardware and bandwidth recommendations to ensure consistent, meaningful
22+
benchmark comparisons across the Ethereum network.
23+
</p>
24+
<p>
25+
EIP-7870 defines three node types with specific requirements: Full Nodes for chain following, Attesters for
26+
validators, and Local Block Builders for block proposal. Each has distinct hardware and bandwidth needs.
27+
</p>
28+
<div className="overflow-x-auto">
29+
<table className="w-full text-left text-xs">
30+
<thead>
31+
<tr className="border-b border-border">
32+
<th className="py-2 pr-4 font-medium text-foreground">Node Type</th>
33+
<th className="py-2 pr-4 font-medium text-foreground">RAM</th>
34+
<th className="py-2 pr-4 font-medium text-foreground">CPU</th>
35+
<th className="py-2 font-medium text-foreground">Bandwidth</th>
36+
</tr>
37+
</thead>
38+
<tbody className="text-muted">
39+
<tr className="border-b border-border/50">
40+
<td className="py-2 pr-4">Full Node</td>
41+
<td className="py-2 pr-4">32 GB</td>
42+
<td className="py-2 pr-4">4c/8t</td>
43+
<td className="py-2">50/15 Mbps</td>
44+
</tr>
45+
<tr className="border-b border-border/50">
46+
<td className="py-2 pr-4">Attester</td>
47+
<td className="py-2 pr-4">64 GB</td>
48+
<td className="py-2 pr-4">8c/16t</td>
49+
<td className="py-2">50/25 Mbps</td>
50+
</tr>
51+
<tr>
52+
<td className="py-2 pr-4">Local Block Builder</td>
53+
<td className="py-2 pr-4">64 GB</td>
54+
<td className="py-2 pr-4">8c/16t</td>
55+
<td className="py-2">100/50 Mbps</td>
56+
</tr>
57+
</tbody>
58+
</table>
59+
</div>
60+
<div className="rounded-sm border border-accent/20 bg-accent/5 p-3">
61+
<p className="text-foreground">
62+
<span className="font-medium">Why filter by reference nodes?</span>{' '}
63+
<span className="italic">
64+
This ensures timing data reflects consistent, spec-compliant hardware rather than varied community setups.
65+
</span>
66+
</p>
67+
</div>
68+
<p>
69+
All reference nodes use 4 TB NVMe storage. By filtering to reference nodes, you see Engine API performance on
70+
standardized infrastructure, making it easier to identify client-specific behavior versus hardware-related
71+
variance.
72+
</p>
73+
<p className="text-xs">
74+
<a
75+
href="https://eips.ethereum.org/EIPS/eip-7870"
76+
target="_blank"
77+
rel="noopener noreferrer"
78+
className="text-accent hover:underline"
79+
>
80+
Read EIP-7870 specification
81+
</a>
82+
</p>
83+
</div>
84+
</Dialog>
85+
);
86+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { ReferenceNodesInfoDialog } from './ReferenceNodesInfoDialog';
2+
export type { ReferenceNodesInfoDialogProps } from './ReferenceNodesInfoDialog';

src/pages/ethereum/slots/components/EngineTimingsCard/EngineTimingsCard.tsx

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import type { JSX } from 'react';
1+
import { useState, type JSX } from 'react';
22
import clsx from 'clsx';
3+
import { InformationCircleIcon } from '@heroicons/react/24/outline';
34
import { Card } from '@/components/Layout/Card';
45
import { MiniStat } from '@/components/DataDisplay/MiniStat';
56
import type { SlotEngineTimingsData } from '../../hooks/useSlotEngineTimings';
67
import { ClientVersionBreakdown } from '@/pages/ethereum/execution/timings/components/ClientVersionBreakdown';
8+
import { ReferenceNodesInfoDialog } from '@/pages/ethereum/execution/timings/components/ReferenceNodesInfoDialog';
79

810
export interface EngineTimingsCardProps {
911
data: SlotEngineTimingsData | null;
@@ -16,6 +18,8 @@ export interface EngineTimingsCardProps {
1618
* Shows newPayload and getBlobs metrics with per-EL-client breakdown.
1719
*/
1820
export function EngineTimingsCard({ data, isLoading, hasBlobsInSlot }: EngineTimingsCardProps): JSX.Element | null {
21+
const [showRefNodeInfo, setShowRefNodeInfo] = useState(false);
22+
1923
// Don't render anything if no data and not loading
2024
if (!isLoading && !data?.newPayloadByStatus?.length && !data?.getBlobsByStatus?.length) {
2125
return null;
@@ -88,9 +92,18 @@ export function EngineTimingsCard({ data, isLoading, hasBlobsInSlot }: EngineTim
8892
{/* newPayload Section */}
8993
{hasNewPayloadData && (
9094
<Card>
91-
<div className="mb-4">
92-
<h3 className="text-lg font-semibold text-foreground">engine_newPayload</h3>
93-
<p className="text-sm text-muted">Block validation timing</p>
95+
<div className="mb-4 flex items-start justify-between">
96+
<div>
97+
<h3 className="text-lg font-semibold text-foreground">engine_newPayload</h3>
98+
<p className="text-sm text-muted">Block validation timing</p>
99+
</div>
100+
<button
101+
onClick={() => setShowRefNodeInfo(true)}
102+
className="rounded-xs p-1 text-muted transition-colors hover:text-foreground"
103+
aria-label="Learn more about reference nodes"
104+
>
105+
<InformationCircleIcon className="size-5" />
106+
</button>
94107
</div>
95108

96109
{/* Summary Stats */}
@@ -116,9 +129,18 @@ export function EngineTimingsCard({ data, isLoading, hasBlobsInSlot }: EngineTim
116129
{/* getBlobs Section - show if there's timing data or if slot has blobs */}
117130
{showGetBlobsSection && (
118131
<Card>
119-
<div className="mb-4">
120-
<h3 className="text-lg font-semibold text-foreground">engine_getBlobs</h3>
121-
<p className="text-sm text-muted">Blob retrieval timing</p>
132+
<div className="mb-4 flex items-start justify-between">
133+
<div>
134+
<h3 className="text-lg font-semibold text-foreground">engine_getBlobs</h3>
135+
<p className="text-sm text-muted">Blob retrieval timing</p>
136+
</div>
137+
<button
138+
onClick={() => setShowRefNodeInfo(true)}
139+
className="rounded-xs p-1 text-muted transition-colors hover:text-foreground"
140+
aria-label="Learn more about reference nodes"
141+
>
142+
<InformationCircleIcon className="size-5" />
143+
</button>
122144
</div>
123145

124146
{hasGetBlobsData ? (
@@ -148,6 +170,9 @@ export function EngineTimingsCard({ data, isLoading, hasBlobsInSlot }: EngineTim
148170
)}
149171
</Card>
150172
)}
173+
174+
{/* Reference Node Info Dialog */}
175+
<ReferenceNodesInfoDialog open={showRefNodeInfo} onClose={() => setShowRefNodeInfo(false)} />
151176
</div>
152177
);
153178
}

0 commit comments

Comments
 (0)