Skip to content

Commit b3b8ba8

Browse files
authored
Display per-tx execution time and state root time on block page (#155)
* 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. * Fix biome formatting in enrichTransactionWithBundleData signature
1 parent a7b365e commit b3b8ba8

3 files changed

Lines changed: 44 additions & 18 deletions

File tree

src/app/api/block/[hash]/route.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,15 @@ async function buildAndCacheBlockData(
7373
): Promise<BlockData> {
7474
const transactions: BlockTransaction[] = await Promise.all(
7575
rpcBlock.transactions.map(async (tx, index) => {
76-
const { bundleId, executionTimeUs } =
76+
const { bundleId, executionTimeUs, stateRootTimeUs } =
7777
await enrichTransactionWithBundleData(tx.hash);
7878
return {
7979
hash: tx.hash,
8080
from: tx.from,
8181
to: tx.to,
8282
gasUsed: tx.gas,
8383
executionTimeUs,
84+
stateRootTimeUs,
8485
bundleId,
8586
index,
8687
};
@@ -109,34 +110,43 @@ function isSystemTransaction(tx: BlockTransaction): boolean {
109110
return tx.index === 0;
110111
}
111112

112-
async function enrichTransactionWithBundleData(
113-
txHash: string,
114-
): Promise<{ bundleId: string | null; executionTimeUs: number | null }> {
113+
async function enrichTransactionWithBundleData(txHash: string): Promise<{
114+
bundleId: string | null;
115+
executionTimeUs: number | null;
116+
stateRootTimeUs: number | null;
117+
}> {
115118
const metadata = await getTransactionMetadataByHash(txHash);
116119
if (!metadata || metadata.bundle_ids.length === 0) {
117-
return { bundleId: null, executionTimeUs: null };
120+
return { bundleId: null, executionTimeUs: null, stateRootTimeUs: null };
118121
}
119122

120123
const bundleId = metadata.bundle_ids[0];
121124
const bundleHistory = await getBundleHistory(bundleId);
122125
if (!bundleHistory) {
123-
return { bundleId, executionTimeUs: null };
126+
return { bundleId, executionTimeUs: null, stateRootTimeUs: null };
124127
}
125128

126129
const receivedEvent = bundleHistory.history.find(
127130
(e) => e.event === "Received",
128131
);
129132
if (!receivedEvent?.data?.bundle?.meter_bundle_response?.results) {
130-
return { bundleId, executionTimeUs: null };
133+
return { bundleId, executionTimeUs: null, stateRootTimeUs: null };
131134
}
132135

133-
const txResult = receivedEvent.data.bundle.meter_bundle_response.results.find(
136+
const meterResponse = receivedEvent.data.bundle.meter_bundle_response;
137+
138+
// TODO: Switch to meterResponse.totalExecutionTimeUs once 0.7 is deployed.
139+
// On 0.6, totalExecutionTimeUs is the wall-clock total_time_us which includes
140+
// setup, teardown, and state root (double-counting stateRootTimeUs). PR #1111
141+
// fixes this on main to be the sum of per-tx execution times.
142+
const txResult = meterResponse.results.find(
134143
(r: MeterBundleResult) => r.txHash.toLowerCase() === txHash.toLowerCase(),
135144
);
136145

137146
return {
138147
bundleId,
139148
executionTimeUs: txResult?.executionTimeUs ?? null,
149+
stateRootTimeUs: meterResponse.stateRootTimeUs ?? null,
140150
};
141151
}
142152

@@ -153,9 +163,9 @@ async function refetchMissingTransactionSimulations(
153163

154164
const refetchResults = await Promise.all(
155165
transactionsToRefetch.map(async (tx) => {
156-
const { bundleId, executionTimeUs } =
166+
const { bundleId, executionTimeUs, stateRootTimeUs } =
157167
await enrichTransactionWithBundleData(tx.hash);
158-
return { hash: tx.hash, bundleId, executionTimeUs };
168+
return { hash: tx.hash, bundleId, executionTimeUs, stateRootTimeUs };
159169
}),
160170
);
161171

@@ -168,6 +178,7 @@ async function refetchMissingTransactionSimulations(
168178
...tx,
169179
bundleId: refetchResult.bundleId,
170180
executionTimeUs: refetchResult.executionTimeUs,
181+
stateRootTimeUs: refetchResult.stateRootTimeUs,
171182
};
172183
}
173184
return tx;

src/app/block/[hash]/page.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,18 @@ function getHeatmapStyle(
9292

9393
function TransactionRow({
9494
tx,
95-
maxExecutionTime,
95+
maxTotalTime,
9696
}: {
9797
tx: BlockTransaction;
98-
maxExecutionTime: number;
98+
maxTotalTime: number;
9999
}) {
100100
const hasBundle = tx.bundleId !== null;
101101
const hasExecutionTime = tx.executionTimeUs !== null;
102102
const executionTime = tx.executionTimeUs ?? 0;
103+
const stateRootTime = tx.stateRootTimeUs ?? 0;
104+
const totalTime = executionTime + stateRootTime;
103105
const heatmapStyle = hasExecutionTime
104-
? getHeatmapStyle(executionTime, maxExecutionTime)
106+
? getHeatmapStyle(totalTime, maxTotalTime)
105107
: null;
106108

107109
const content = (
@@ -144,7 +146,7 @@ function TransactionRow({
144146
<span
145147
className={`inline-block px-2 py-0.5 rounded text-sm font-medium ${heatmapStyle.bg} ${heatmapStyle.text}`}
146148
>
147-
{executionTime.toLocaleString()}μs
149+
{totalTime.toLocaleString()}μs
148150
</span>
149151
) : (
150152
<div className="text-sm font-medium text-gray-400"></div>
@@ -171,14 +173,18 @@ function BlockStats({ block }: { block: BlockData }) {
171173
(sum, tx) => sum + (tx.executionTimeUs ?? 0),
172174
0,
173175
);
176+
const totalStateRootTime = txsWithTime.reduce(
177+
(sum, tx) => sum + (tx.stateRootTimeUs ?? 0),
178+
0,
179+
);
174180
const bundleCount = block.transactions.filter(
175181
(tx) => tx.bundleId !== null,
176182
).length;
177183

178184
return (
179185
<Card>
180186
<div className="p-5">
181-
<div className="grid grid-cols-2 lg:grid-cols-4 gap-6">
187+
<div className="grid grid-cols-2 lg:grid-cols-5 gap-6">
182188
<div>
183189
<div className="text-xs text-gray-500 mb-1">Block Number</div>
184190
<div className="text-xl font-semibold text-gray-900">
@@ -205,6 +211,14 @@ function BlockStats({ block }: { block: BlockData }) {
205211
: "—"}
206212
</div>
207213
</div>
214+
<div>
215+
<div className="text-xs text-gray-500 mb-1">Total State Root</div>
216+
<div className="text-xl font-semibold text-gray-900">
217+
{totalStateRootTime > 0
218+
? `${totalStateRootTime.toLocaleString()}μs`
219+
: "—"}
220+
</div>
221+
</div>
208222
</div>
209223
</div>
210224
<div className="border-t border-gray-100 px-5 py-3 bg-gray-50/50 grid grid-cols-3 gap-4 text-xs">
@@ -284,11 +298,11 @@ export default function BlockPage({ params }: PageProps) {
284298
);
285299
}
286300

287-
const maxExecutionTime = data
301+
const maxTotalTime = data
288302
? Math.max(
289303
...data.transactions
290304
.filter((tx) => tx.executionTimeUs !== null)
291-
.map((tx) => tx.executionTimeUs ?? 0),
305+
.map((tx) => (tx.executionTimeUs ?? 0) + (tx.stateRootTimeUs ?? 0)),
292306
0,
293307
)
294308
: 0;
@@ -478,7 +492,7 @@ export default function BlockPage({ params }: PageProps) {
478492
<TransactionRow
479493
key={tx.hash}
480494
tx={tx}
481-
maxExecutionTime={maxExecutionTime}
495+
maxTotalTime={maxTotalTime}
482496
/>
483497
))}
484498
</div>

src/lib/s3.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ export interface BlockTransaction {
186186
to: string | null;
187187
gasUsed: bigint;
188188
executionTimeUs: number | null;
189+
stateRootTimeUs: number | null;
189190
bundleId: string | null;
190191
index: number;
191192
}

0 commit comments

Comments
 (0)