diff --git a/src/pages/ethereum/execution/timings/components/GetBlobsTab/GetBlobsTab.tsx b/src/pages/ethereum/execution/timings/components/GetBlobsTab/GetBlobsTab.tsx index 82bb01369..077d5713e 100644 --- a/src/pages/ethereum/execution/timings/components/GetBlobsTab/GetBlobsTab.tsx +++ b/src/pages/ethereum/execution/timings/components/GetBlobsTab/GetBlobsTab.tsx @@ -7,7 +7,7 @@ import { MultiLineChart } from '@/components/Charts/MultiLine'; import { BarChart } from '@/components/Charts/Bar'; import { LoadingContainer } from '@/components/Layout/LoadingContainer'; import { useThemeColors } from '@/hooks/useThemeColors'; -import { formatSlot } from '@/utils'; +import { formatSlot, getExecutionClientColor } from '@/utils'; import type { EngineTimingsData } from '../../hooks/useEngineTimingsData'; import { PER_SLOT_CHART_RANGES, type TimeRange } from '../../IndexPage.types'; import { ClientVersionBreakdown } from '../ClientVersionBreakdown'; @@ -249,16 +249,6 @@ export function GetBlobsTab({ data, timeRange, isLoading }: GetBlobsTabProps): J clientSlotData.get(client)!.set(slot, duration); }); - // Create series for the by-client chart - const clientColors = [ - themeColors.primary, - themeColors.success, - themeColors.warning, - themeColors.danger, - themeColors.accent, - themeColors.secondary, - ]; - // Downsample to ~400 points max per series to keep chart performant const MAX_POINTS_PER_SERIES = 400; @@ -276,7 +266,7 @@ export function GetBlobsTab({ data, timeRange, isLoading }: GetBlobsTabProps): J return { name: client, data: sampledSlots.map(([slot, duration]) => [slot, duration]) as [number, number][], - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -350,7 +340,7 @@ export function GetBlobsTab({ data, timeRange, isLoading }: GetBlobsTabProps): J return { name: client, data: dataPoints, - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -369,7 +359,7 @@ export function GetBlobsTab({ data, timeRange, isLoading }: GetBlobsTabProps): J return { name: client, data: dataPoints, - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -423,7 +413,7 @@ export function GetBlobsTab({ data, timeRange, isLoading }: GetBlobsTabProps): J return { name: client, data: dataPoints, - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); diff --git a/src/pages/ethereum/execution/timings/components/NewPayloadTab/NewPayloadTab.tsx b/src/pages/ethereum/execution/timings/components/NewPayloadTab/NewPayloadTab.tsx index 68ce96516..42ce7b813 100644 --- a/src/pages/ethereum/execution/timings/components/NewPayloadTab/NewPayloadTab.tsx +++ b/src/pages/ethereum/execution/timings/components/NewPayloadTab/NewPayloadTab.tsx @@ -7,7 +7,7 @@ import { MultiLineChart } from '@/components/Charts/MultiLine'; import { BarChart } from '@/components/Charts/Bar'; import { ScatterAndLineChart } from '@/components/Charts/ScatterAndLine'; import { useThemeColors } from '@/hooks/useThemeColors'; -import { formatSlot } from '@/utils'; +import { formatSlot, getExecutionClientColor } from '@/utils'; import type { EngineTimingsData } from '../../hooks/useEngineTimingsData'; import { PER_SLOT_CHART_RANGES, type TimeRange } from '../../IndexPage.types'; import { ClientVersionBreakdown } from '../ClientVersionBreakdown'; @@ -158,16 +158,6 @@ export function NewPayloadTab({ data, timeRange }: NewPayloadTabProps): JSX.Elem const hasBlockComplexityData = clientGasData.size > 0; - // Shared color palette for client charts - const clientColors = [ - themeColors.primary, - themeColors.success, - themeColors.warning, - themeColors.danger, - themeColors.accent, - themeColors.secondary, - ]; - // === EL Client Analysis === // Build a map of EL client -> metrics (VALID status only) const elClientMetrics = new Map< @@ -240,7 +230,7 @@ export function NewPayloadTab({ data, timeRange }: NewPayloadTabProps): JSX.Elem return { name: client, data: sampledSlots.map(([slot, duration]) => [slot, duration] as [number, number]), - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -314,7 +304,7 @@ export function NewPayloadTab({ data, timeRange }: NewPayloadTabProps): JSX.Elem return { name: client, data: dataPoints, - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -333,7 +323,7 @@ export function NewPayloadTab({ data, timeRange }: NewPayloadTabProps): JSX.Elem return { name: client, data: dataPoints, - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -386,7 +376,7 @@ export function NewPayloadTab({ data, timeRange }: NewPayloadTabProps): JSX.Elem return { name: client, data: dataPoints, - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), }; }); @@ -394,7 +384,7 @@ export function NewPayloadTab({ data, timeRange }: NewPayloadTabProps): JSX.Elem const blockComplexitySeries = elClientList.map((client, index) => ({ name: client, data: clientGasData.get(client) ?? [], - color: clientColors[index % clientColors.length], + color: getExecutionClientColor(client, index), symbolSize: 6, })); diff --git a/src/utils/ethereum.ts b/src/utils/ethereum.ts index 0dfe10277..4ea71a034 100644 --- a/src/utils/ethereum.ts +++ b/src/utils/ethereum.ts @@ -102,6 +102,49 @@ export function truncateAddress(pubkey?: string, startChars = 6, endChars = 4): */ const EXECUTION_CLIENTS = ['geth', 'nethermind', 'besu', 'erigon', 'reth'] as const; +/** + * Official brand colors for Ethereum execution clients + */ +export const EXECUTION_CLIENT_COLORS: Record = { + besu: '#1ba0a1', + nethermind: '#02bbec', + reth: '#f44f02', + geth: '#707d91', + 'go-ethereum': '#707d91', + erigon: '#f5ad73', +}; + +/** + * Default fallback colors for unknown clients + */ +const FALLBACK_COLORS = [ + '#6b7280', // gray-500 + '#8b5cf6', // violet-500 + '#ec4899', // pink-500 + '#14b8a6', // teal-500 + '#f97316', // orange-500 + '#84cc16', // lime-500 +]; + +/** + * Get the brand color for an execution client + * + * @param clientName - Name of the execution client (case-insensitive) + * @param fallbackIndex - Index for fallback color if client is unknown (cycles through fallback colors) + * @returns Hex color string + * + * @example + * ```tsx + * getExecutionClientColor('nethermind') // Returns '#02bbec' + * getExecutionClientColor('Reth') // Returns '#f44f02' + * getExecutionClientColor('unknown', 0) // Returns '#6b7280' + * ``` + */ +export function getExecutionClientColor(clientName: string, fallbackIndex = 0): string { + const normalized = clientName.toLowerCase().trim(); + return EXECUTION_CLIENT_COLORS[normalized] ?? FALLBACK_COLORS[fallbackIndex % FALLBACK_COLORS.length]; +} + /** * Parse execution client name from meta client name * Extracts client name and version from strings like "Nethermind/v1.25.4"