diff --git a/src/pages/ethereum/execution/gas-profiler/SimulatePage.tsx b/src/pages/ethereum/execution/gas-profiler/SimulatePage.tsx index 92f99cd6e..ddf89a2a4 100644 --- a/src/pages/ethereum/execution/gas-profiler/SimulatePage.tsx +++ b/src/pages/ethereum/execution/gas-profiler/SimulatePage.tsx @@ -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'; @@ -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'; @@ -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(null); @@ -494,6 +501,7 @@ export function SimulatePage(): JSX.Element { body: JSON.stringify({ blockNumber, gasSchedule: { overrides: gasSchedule }, + ...(useMaxGasLimit && { maxGasLimit: true }), }), }); @@ -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 @@ -780,6 +788,26 @@ export function SimulatePage(): JSX.Element { + {/* Advanced Options */} +
+ + + +
+ {/* Validation / Loading / Error messages */} {inputError && ( - + { + setUseMaxGasLimit(true); + setGasLimitInfoOpen(true); + } + } + /> )} @@ -1233,6 +1272,27 @@ export function SimulatePage(): JSX.Element { onCancel={handlePresetCancel} defaults={gasScheduleDefaults ?? null} /> + + {/* Gas Limit Info Dialog */} + setGasLimitInfoOpen(false)} + title="Simulated Gas Limit Override" + size="sm" + > +
+

+ 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. +

+

+ 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. +

+

The original execution always uses the real transaction gas limit, so you can still compare the two.

+
+
); } diff --git a/src/pages/ethereum/execution/gas-profiler/components/BlockSimulationResultsV2/BlockSimulationResultsV2.tsx b/src/pages/ethereum/execution/gas-profiler/components/BlockSimulationResultsV2/BlockSimulationResultsV2.tsx index f316fd2a9..7e3bcc105 100644 --- a/src/pages/ethereum/execution/gas-profiler/components/BlockSimulationResultsV2/BlockSimulationResultsV2.tsx +++ b/src/pages/ethereum/execution/gas-profiler/components/BlockSimulationResultsV2/BlockSimulationResultsV2.tsx @@ -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; } @@ -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('all'); const [sortBy, setSortBy] = useState('delta'); @@ -873,7 +877,27 @@ function TransactionImpactView({ {/* Pre-execution error (e.g. intrinsic gas too low) */} {hasPreExecError && ( - + +
{tx.error}
+ + + ) : ( + tx.error + ) + } + className="mt-3" + /> )} {/* Error cards (if any) */} @@ -957,6 +981,7 @@ function TransactionImpactView({ export function BlockSimulationResultsV2({ result, modifiedParams, + onEnableMaxGasLimit, className, }: BlockSimulationResultsV2Props): JSX.Element { // Overall gas delta @@ -1105,7 +1130,11 @@ export function BlockSimulationResultsV2({ {/* TRANSACTIONS TAB */} {/* ============================================================ */} - + diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 7318c274e..4a47c238c 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -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 @@ -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' @@ -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 }