diff --git a/src/pages/ethereum/execution/payloads/IndexPage.tsx b/src/pages/ethereum/execution/payloads/IndexPage.tsx index bf308c4a2..be5eed0ec 100644 --- a/src/pages/ethereum/execution/payloads/IndexPage.tsx +++ b/src/pages/ethereum/execution/payloads/IndexPage.tsx @@ -29,10 +29,20 @@ export function IndexPage(): JSX.Element { // Calculate time range in seconds // Use URL params if provided, otherwise default to last hour with no upper bound + // Skip time filtering when blockNumber is provided for deep-linking const timeRange = useMemo(() => { const nowSeconds = Math.floor(Date.now() / 1000); const defaultRangeSeconds = DEFAULT_TIME_RANGE_HOURS * 60 * 60; + // When blockNumber is provided, skip time filtering to allow deep-linking + // to blocks that fall outside the default 6-hour window + if (search.blockNumber !== undefined) { + return { + start: undefined, + end: undefined, + }; + } + if (search.timeStart !== undefined && search.timeEnd !== undefined) { return { start: search.timeStart, @@ -58,7 +68,7 @@ export function IndexPage(): JSX.Element { start: nowSeconds - defaultRangeSeconds, end: undefined, }; - }, [search.timeStart, search.timeEnd]); + }, [search.timeStart, search.timeEnd, search.blockNumber]); // Check if live mode is enabled from URL (only if feature is enabled) const isLive = LIVE_MODE_ENABLED && (search.isLive ?? false); @@ -85,7 +95,7 @@ export function IndexPage(): JSX.Element { } = useQuery({ ...intEngineNewPayloadServiceListOptions({ query: { - slot_start_date_time_gte: timeRange.start, + ...(timeRange.start !== undefined && { slot_start_date_time_gte: timeRange.start }), ...(timeRange.end !== undefined && { slot_start_date_time_lte: timeRange.end }), duration_ms_gte: durationMin, page_size: search.pageSize ?? DEFAULT_PAGE_SIZE, @@ -348,6 +358,9 @@ export function IndexPage(): JSX.Element { ); }, [data, search.detailSlot, search.detailNodeName]); + // Get tracoor URL from network config for external links + const tracoorUrl = currentNetwork?.service_urls?.tracoor; + return ( void; // Duration threshold for display durationThreshold: number; + // External links + tracoorUrl?: string; // Live mode props isLive?: boolean; onLiveModeToggle?: () => void; @@ -177,6 +180,7 @@ export function PayloadsView({ onFiltersChange, onClearFilters, durationThreshold, + tracoorUrl, isLive = false, onLiveModeToggle, newItemIdsRef, @@ -289,8 +293,35 @@ export function PayloadsView({ }, sortingFn: 'basic', }), + // Tracoor external link column (only shown if tracoorUrl is available) + ...(tracoorUrl + ? [ + columnHelper.display({ + id: 'tracoor', + header: '', + cell: info => { + const blockNumber = info.row.original.block_number; + if (blockNumber === undefined) return null; + const url = `${tracoorUrl}/execution_block_trace?executionBlockTraceBlockNumber=${blockNumber}`; + return ( + e.stopPropagation()} + className="inline-flex items-center transition-opacity hover:opacity-70" + title="View in Tracoor" + > + + + ); + }, + enableSorting: false, + }), + ] + : []), ], - [onFilterClick] + [onFilterClick, tracoorUrl] ); // Get row ID for live mode highlighting