From aaf63039214c2f5c5479333b9f4424afa95435d8 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Fri, 20 Mar 2026 00:15:05 -0500 Subject: [PATCH 1/2] Display per-tx execution time and state root time on block page Use results[i].executionTimeUs (per-tx EVM time) instead of totalExecutionTimeUs, which on 0.6 is the wall-clock total_time_us that double-counts state root time. Add stateRootTimeUs from the bundle-level meter response as a separate field. --- src/app/api/block/[hash]/route.ts | 29 +++++++++++++++++++++-------- src/app/block/[hash]/page.tsx | 30 ++++++++++++++++++++++-------- src/lib/s3.ts | 1 + 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/app/api/block/[hash]/route.ts b/src/app/api/block/[hash]/route.ts index 0b8983a..5fedd4d 100644 --- a/src/app/api/block/[hash]/route.ts +++ b/src/app/api/block/[hash]/route.ts @@ -73,7 +73,7 @@ async function buildAndCacheBlockData( ): Promise { const transactions: BlockTransaction[] = await Promise.all( rpcBlock.transactions.map(async (tx, index) => { - const { bundleId, executionTimeUs } = + const { bundleId, executionTimeUs, stateRootTimeUs } = await enrichTransactionWithBundleData(tx.hash); return { hash: tx.hash, @@ -81,6 +81,7 @@ async function buildAndCacheBlockData( to: tx.to, gasUsed: tx.gas, executionTimeUs, + stateRootTimeUs, bundleId, index, }; @@ -111,32 +112,43 @@ function isSystemTransaction(tx: BlockTransaction): boolean { async function enrichTransactionWithBundleData( txHash: string, -): Promise<{ bundleId: string | null; executionTimeUs: number | null }> { +): Promise<{ + bundleId: string | null; + executionTimeUs: number | null; + stateRootTimeUs: number | null; +}> { const metadata = await getTransactionMetadataByHash(txHash); if (!metadata || metadata.bundle_ids.length === 0) { - return { bundleId: null, executionTimeUs: null }; + return { bundleId: null, executionTimeUs: null, stateRootTimeUs: null }; } const bundleId = metadata.bundle_ids[0]; const bundleHistory = await getBundleHistory(bundleId); if (!bundleHistory) { - return { bundleId, executionTimeUs: null }; + return { bundleId, executionTimeUs: null, stateRootTimeUs: null }; } const receivedEvent = bundleHistory.history.find( (e) => e.event === "Received", ); if (!receivedEvent?.data?.bundle?.meter_bundle_response?.results) { - return { bundleId, executionTimeUs: null }; + return { bundleId, executionTimeUs: null, stateRootTimeUs: null }; } - const txResult = receivedEvent.data.bundle.meter_bundle_response.results.find( + const meterResponse = receivedEvent.data.bundle.meter_bundle_response; + + // TODO: Switch to meterResponse.totalExecutionTimeUs once 0.7 is deployed. + // On 0.6, totalExecutionTimeUs is the wall-clock total_time_us which includes + // setup, teardown, and state root (double-counting stateRootTimeUs). PR #1111 + // fixes this on main to be the sum of per-tx execution times. + const txResult = meterResponse.results.find( (r: MeterBundleResult) => r.txHash.toLowerCase() === txHash.toLowerCase(), ); return { bundleId, executionTimeUs: txResult?.executionTimeUs ?? null, + stateRootTimeUs: meterResponse.stateRootTimeUs ?? null, }; } @@ -153,9 +165,9 @@ async function refetchMissingTransactionSimulations( const refetchResults = await Promise.all( transactionsToRefetch.map(async (tx) => { - const { bundleId, executionTimeUs } = + const { bundleId, executionTimeUs, stateRootTimeUs } = await enrichTransactionWithBundleData(tx.hash); - return { hash: tx.hash, bundleId, executionTimeUs }; + return { hash: tx.hash, bundleId, executionTimeUs, stateRootTimeUs }; }), ); @@ -168,6 +180,7 @@ async function refetchMissingTransactionSimulations( ...tx, bundleId: refetchResult.bundleId, executionTimeUs: refetchResult.executionTimeUs, + stateRootTimeUs: refetchResult.stateRootTimeUs, }; } return tx; diff --git a/src/app/block/[hash]/page.tsx b/src/app/block/[hash]/page.tsx index da0e24c..71bcca7 100644 --- a/src/app/block/[hash]/page.tsx +++ b/src/app/block/[hash]/page.tsx @@ -92,16 +92,18 @@ function getHeatmapStyle( function TransactionRow({ tx, - maxExecutionTime, + maxTotalTime, }: { tx: BlockTransaction; - maxExecutionTime: number; + maxTotalTime: number; }) { const hasBundle = tx.bundleId !== null; const hasExecutionTime = tx.executionTimeUs !== null; const executionTime = tx.executionTimeUs ?? 0; + const stateRootTime = tx.stateRootTimeUs ?? 0; + const totalTime = executionTime + stateRootTime; const heatmapStyle = hasExecutionTime - ? getHeatmapStyle(executionTime, maxExecutionTime) + ? getHeatmapStyle(totalTime, maxTotalTime) : null; const content = ( @@ -144,7 +146,7 @@ function TransactionRow({ - {executionTime.toLocaleString()}μs + {totalTime.toLocaleString()}μs ) : (
@@ -171,6 +173,10 @@ function BlockStats({ block }: { block: BlockData }) { (sum, tx) => sum + (tx.executionTimeUs ?? 0), 0, ); + const totalStateRootTime = txsWithTime.reduce( + (sum, tx) => sum + (tx.stateRootTimeUs ?? 0), + 0, + ); const bundleCount = block.transactions.filter( (tx) => tx.bundleId !== null, ).length; @@ -178,7 +184,7 @@ function BlockStats({ block }: { block: BlockData }) { return (
-
+
Block Number
@@ -205,6 +211,14 @@ function BlockStats({ block }: { block: BlockData }) { : "—"}
+
+
Total State Root
+
+ {totalStateRootTime > 0 + ? `${totalStateRootTime.toLocaleString()}μs` + : "—"} +
+
@@ -284,11 +298,11 @@ export default function BlockPage({ params }: PageProps) { ); } - const maxExecutionTime = data + const maxTotalTime = data ? Math.max( ...data.transactions .filter((tx) => tx.executionTimeUs !== null) - .map((tx) => tx.executionTimeUs ?? 0), + .map((tx) => (tx.executionTimeUs ?? 0) + (tx.stateRootTimeUs ?? 0)), 0, ) : 0; @@ -478,7 +492,7 @@ export default function BlockPage({ params }: PageProps) { ))}
diff --git a/src/lib/s3.ts b/src/lib/s3.ts index 59c2f1b..60f8f3a 100644 --- a/src/lib/s3.ts +++ b/src/lib/s3.ts @@ -186,6 +186,7 @@ export interface BlockTransaction { to: string | null; gasUsed: bigint; executionTimeUs: number | null; + stateRootTimeUs: number | null; bundleId: string | null; index: number; } From 314876e6e12ccb2625e955b4fe8d1ac46d74d96b Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Sat, 21 Mar 2026 11:08:40 -0500 Subject: [PATCH 2/2] Fix biome formatting in enrichTransactionWithBundleData signature --- src/app/api/block/[hash]/route.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/api/block/[hash]/route.ts b/src/app/api/block/[hash]/route.ts index 5fedd4d..bf48204 100644 --- a/src/app/api/block/[hash]/route.ts +++ b/src/app/api/block/[hash]/route.ts @@ -110,9 +110,7 @@ function isSystemTransaction(tx: BlockTransaction): boolean { return tx.index === 0; } -async function enrichTransactionWithBundleData( - txHash: string, -): Promise<{ +async function enrichTransactionWithBundleData(txHash: string): Promise<{ bundleId: string | null; executionTimeUs: number | null; stateRootTimeUs: number | null;