Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions src/pages/ethereum/execution/gas-profiler/SimulatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ExclamationTriangleIcon,
BoltIcon,
CubeIcon,
InformationCircleIcon,
} from '@heroicons/react/24/outline';
import clsx from 'clsx';
import ReactEChartsCore from 'echarts-for-react/lib/core';
Expand All @@ -30,6 +31,8 @@ import { Stats } from '@/components/DataDisplay/Stats';
import type { Stat } from '@/components/DataDisplay/Stats/Stats.types';
import { Alert } from '@/components/Feedback/Alert';
import { Input } from '@/components/Forms/Input';
import { Checkbox } from '@/components/Forms/Checkbox';
import { Dialog } from '@/components/Overlays/Dialog';
import { Button } from '@/components/Elements/Button';
import { useNetwork } from '@/hooks/useNetwork';
import { useThemeColors } from '@/hooks/useThemeColors';
Expand Down Expand Up @@ -225,6 +228,10 @@ export function SimulatePage(): JSX.Element {
// Preset modal state
const [presetModalOpen, setPresetModalOpen] = useState(false);

// Gas limit override state
const [useMaxGasLimit, setUseMaxGasLimit] = useState(false);
const [gasLimitInfoOpen, setGasLimitInfoOpen] = useState(false);

// Selection state
const [selectedBlockIndex, setSelectedBlockIndex] = useState<number | null>(null);

Expand Down Expand Up @@ -494,6 +501,7 @@ export function SimulatePage(): JSX.Element {
body: JSON.stringify({
blockNumber,
gasSchedule: { overrides: gasSchedule },
...(useMaxGasLimit && { maxGasLimit: true }),
}),
});

Expand All @@ -506,7 +514,7 @@ export function SimulatePage(): JSX.Element {
const apiResult: ApiBlockSimulationResponse = await response.json();
return transformApiResponse(apiResult);
},
[gasSchedule, currentNetwork]
[gasSchedule, currentNetwork, useMaxGasLimit]
);

// Run the range simulation
Expand Down Expand Up @@ -780,6 +788,26 @@ export function SimulatePage(): JSX.Element {
</div>
</div>

{/* Advanced Options */}
<div className="mt-4 flex items-center gap-2 border-t border-border pt-4">
<Checkbox checked={useMaxGasLimit} onChange={setUseMaxGasLimit} disabled={isRunning} />
<button
type="button"
onClick={() => !isRunning && setUseMaxGasLimit(!useMaxGasLimit)}
disabled={isRunning}
className={clsx('text-left text-sm', useMaxGasLimit ? 'text-foreground' : 'text-muted')}
>
Use max gas limit for simulated execution
</button>
<button
type="button"
onClick={() => setGasLimitInfoOpen(true)}
className="text-muted transition-colors hover:text-foreground"
>
<InformationCircleIcon className="size-4" />
</button>
</div>

{/* Validation / Loading / Error messages */}
{inputError && (
<Alert
Expand Down Expand Up @@ -1193,7 +1221,18 @@ export function SimulatePage(): JSX.Element {

{/* Reuse existing BlockSimulationResults */}
<div className="p-4">
<BlockSimulationResultsV2 result={selectedResult} modifiedParams={modifiedParamNames} />
<BlockSimulationResultsV2
result={selectedResult}
modifiedParams={modifiedParamNames}
onEnableMaxGasLimit={
useMaxGasLimit
? undefined
: () => {
setUseMaxGasLimit(true);
setGasLimitInfoOpen(true);
}
}
/>
</div>
</Card>
)}
Expand Down Expand Up @@ -1233,6 +1272,27 @@ export function SimulatePage(): JSX.Element {
onCancel={handlePresetCancel}
defaults={gasScheduleDefaults ?? null}
/>

{/* Gas Limit Info Dialog */}
<Dialog
open={gasLimitInfoOpen}
onClose={() => setGasLimitInfoOpen(false)}
title="Simulated Gas Limit Override"
size="sm"
>
<div className="space-y-3 text-sm text-muted">
<p>
When gas prices change, transactions that succeeded under old pricing may run out of gas under new pricing —
not because the transaction logic is wrong, but because the original gas limit was set for the old costs.
</p>
<p>
With this option enabled, the simulated execution lifts the transaction gas limit so it is no longer a
constraining factor. This prevents artificial out-of-gas failures and shows the true gas cost under the new
pricing.
</p>
<p>The original execution always uses the real transaction gas limit, so you can still compare the two.</p>
</div>
</Dialog>
</Container>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export interface BlockSimulationResultsV2Props {
result: BlockSimulationResult;
/** Names of gas params the user explicitly modified (e.g. ['SSTORE_SET', 'SLOAD_COLD']) */
modifiedParams?: string[];
/** Callback to enable max gas limit and open the info modal. When provided, a hint is shown on pre-execution errors. */
onEnableMaxGasLimit?: () => void;
className?: string;
}

Expand Down Expand Up @@ -598,9 +600,11 @@ type TxSort = 'delta' | 'index' | 'gas';
function TransactionImpactView({
transactions,
blockNumber,
onEnableMaxGasLimit,
}: {
transactions: TxSummary[];
blockNumber: number;
onEnableMaxGasLimit?: () => void;
}): JSX.Element {
const [filter, setFilter] = useState<TxFilter>('all');
const [sortBy, setSortBy] = useState<TxSort>('delta');
Expand Down Expand Up @@ -873,7 +877,27 @@ function TransactionImpactView({

{/* Pre-execution error (e.g. intrinsic gas too low) */}
{hasPreExecError && (
<Alert variant="error" title="Pre-execution error" description={tx.error} className="mt-3" />
<Alert
variant="error"
title="Pre-execution error"
description={
onEnableMaxGasLimit ? (
<div>
<div>{tx.error}</div>
<button
type="button"
onClick={onEnableMaxGasLimit}
className="mt-1 underline hover:no-underline"
>
Try with max gas limit enabled?
</button>
</div>
) : (
tx.error
)
}
className="mt-3"
/>
)}

{/* Error cards (if any) */}
Expand Down Expand Up @@ -957,6 +981,7 @@ function TransactionImpactView({
export function BlockSimulationResultsV2({
result,
modifiedParams,
onEnableMaxGasLimit,
className,
}: BlockSimulationResultsV2Props): JSX.Element {
// Overall gas delta
Expand Down Expand Up @@ -1105,7 +1130,11 @@ export function BlockSimulationResultsV2({
{/* TRANSACTIONS TAB */}
{/* ============================================================ */}
<TabPanel>
<TransactionImpactView transactions={result.transactions} blockNumber={result.blockNumber} />
<TransactionImpactView
transactions={result.transactions}
blockNumber={result.blockNumber}
onEnableMaxGasLimit={onEnableMaxGasLimit}
/>
</TabPanel>
</TabPanels>
</TabGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ export interface FileRoutesByFullPath {
'/xatu/geographical-checklist': typeof XatuGeographicalChecklistRoute
'/xatu/locally-built-blocks': typeof XatuLocallyBuiltBlocksRoute
'/experiments/': typeof ExperimentsIndexRoute
'/xatu-data/': typeof XatuDataIndexRoute
'/xatu-data': typeof XatuDataIndexRoute
'/beacon/block-production/live': typeof BeaconBlockProductionLiveRoute
'/beacon/slot/live': typeof BeaconSlotLiveRoute
'/ethereum/consensus/overview': typeof EthereumConsensusOverviewRoute
Expand Down Expand Up @@ -589,7 +589,7 @@ export interface FileRouteTypes {
| '/xatu/geographical-checklist'
| '/xatu/locally-built-blocks'
| '/experiments/'
| '/xatu-data/'
| '/xatu-data'
| '/beacon/block-production/live'
| '/beacon/slot/live'
| '/ethereum/consensus/overview'
Expand Down Expand Up @@ -781,7 +781,7 @@ declare module '@tanstack/react-router' {
'/xatu-data/': {
id: '/xatu-data/'
path: '/xatu-data'
fullPath: '/xatu-data/'
fullPath: '/xatu-data'
preLoaderRoute: typeof XatuDataIndexRouteImport
parentRoute: typeof rootRouteImport
}
Expand Down