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
39 changes: 31 additions & 8 deletions src/pages/ethereum/execution/gas-profiler/CallPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,24 @@ export function CallPage(): JSX.Element {
const [copied, setCopied] = useState(false);

// Fetch all transaction data
// Pass blockNumber from URL search params for efficient partition-based filtering
// Requires blockFromSearch to match ClickHouse primary key prefix for efficient queries
const {
data: txData,
isLoading,
error,
} = useTransactionGasData({
transactionHash: txHash,
transactionHash: blockFromSearch ? txHash : null,
blockNumber: blockFromSearch,
});

// Resolve block number: prefer API response (source of truth), fall back to URL param
const blockNumber = txData?.metadata.blockNumber ?? blockFromSearch ?? null;

// Fetch all opcode data for badge stats on call trace
// Requires blockNumber to match ClickHouse primary key prefix for efficient queries
const { data: opcodeMap } = useAllCallFrameOpcodes({
transactionHash: txHash,
blockNumber: blockFromSearch,
enabled: true,
transactionHash: blockFromSearch ? txHash : null,
blockNumber,
});

// Derive badge stats from full opcode data (SSTORE, SLOAD, LOG*, CREATE*, SELFDESTRUCT)
Expand Down Expand Up @@ -158,9 +161,6 @@ export function CallPage(): JSX.Element {
return statsMap;
}, [opcodeMap]);

// Block number: prefer from response (source of truth), fall back to URL param
const blockNumber = txData?.metadata.blockNumber ?? blockFromSearch ?? null;

// Fetch frame-specific opcodes (only when we have blockNumber)
const { data: frameOpcodes, isLoading: frameOpcodesLoading } = useFrameOpcodes({
blockNumber,
Expand Down Expand Up @@ -312,6 +312,29 @@ export function CallPage(): JSX.Element {
return () => window.removeEventListener('popstate', handlePopState);
}, [getTabIndexFromHash]);

// Missing block context (direct URL access without ?block= param)
if (!blockFromSearch) {
return (
<Container>
<Header title="Internal Tx Details" description="Internal tx gas analysis" />
<Alert
variant="warning"
title="Block context required"
description="This transaction link is missing block context. Please search for the transaction through the gas profiler to load it efficiently."
/>
<div className="mt-4">
<Link
to="/ethereum/execution/gas-profiler"
className="flex items-center gap-1 text-sm text-muted transition-colors hover:text-foreground"
>
<ArrowLeftIcon className="size-4" />
Back to Gas Profiler
</Link>
</div>
</Container>
);
}

// Loading state
if (isLoading) {
return (
Expand Down
39 changes: 31 additions & 8 deletions src/pages/ethereum/execution/gas-profiler/TransactionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,25 @@ export function TransactionPage(): JSX.Element {
} | null>(null);

// Fetch transaction data by tx hash
// Pass blockNumber from URL search params for efficient partition-based filtering
// Requires blockFromSearch to match ClickHouse primary key prefix for efficient queries
const {
data: txData,
isLoading,
error,
} = useTransactionGasData({
transactionHash: txHash,
transactionHash: blockFromSearch ? txHash : null,
blockNumber: blockFromSearch,
});

// Resolve block number: prefer API response (source of truth), fall back to URL param
const blockNumber = txData?.metadata.blockNumber ?? blockFromSearch ?? null;

// Fetch all opcode data - used for both flame graph visualization AND badge stats
// Single fetch instead of separate calls for badges vs flame graph
// Requires blockNumber to match ClickHouse primary key prefix for efficient queries
const { data: opcodeMap, isLoading: isLoadingOpcodes } = useAllCallFrameOpcodes({
transactionHash: txHash,
blockNumber: blockFromSearch,
enabled: true, // Always fetch - eliminates duplicate API call for badge data
transactionHash: blockFromSearch ? txHash : null,
blockNumber,
});

// Derive badge stats from full opcode data (SSTORE, SLOAD, LOG*, CREATE*, SELFDESTRUCT)
Expand Down Expand Up @@ -165,9 +168,6 @@ export function TransactionPage(): JSX.Element {
// Get theme colors for charts
const colors = useThemeColors();

// Block number: prefer from response (source of truth), fall back to URL param
const blockNumber = txData?.metadata.blockNumber ?? blockFromSearch ?? null;

// Get contract owners from txData for name lookup (memoized to avoid dependency issues)
const contractOwners = useMemo(() => txData?.contractOwners ?? {}, [txData?.contractOwners]);

Expand Down Expand Up @@ -641,6 +641,29 @@ export function TransactionPage(): JSX.Element {
};
}, [contractTree, totalEvmGas, colors]);

// Missing block context (direct URL access without ?block= param)
if (!blockFromSearch) {
return (
<Container>
<Header title={`TX ${txHash.slice(0, 10)}...${txHash.slice(-8)}`} description="Transaction gas analysis" />
<Alert
variant="warning"
title="Block context required"
description="This transaction link is missing block context. Please search for the transaction through the gas profiler to load it efficiently."
/>
<div className="mt-4">
<Link
to="/ethereum/execution/gas-profiler"
className="flex items-center gap-1 text-sm text-muted transition-colors hover:text-foreground"
>
<ArrowLeftIcon className="size-4" />
Back to Gas Profiler
</Link>
</div>
</Container>
);
}

// Loading state
if (isLoading) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ export function TransactionRow({
const navigate = useNavigate();

// Fetch full transaction data when expanded
// Always pass blockNumber to match ClickHouse primary key prefix for efficient queries
const { data: txData, isLoading: txLoading } = useTransactionGasData({
transactionHash: isExpanded ? transaction.transactionHash : null,
blockNumber,
});

// Navigate to call page when a frame is clicked
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export type AllCallFrameOpcodesMap = Map<number, CallFrameOpcodeData[]>;
export interface UseAllCallFrameOpcodesOptions {
/** Transaction hash to fetch data for */
transactionHash: string | null;
/** Block number for efficient API queries (uses partition key) */
blockNumber?: number | null;
/** Block number for efficient API queries (matches primary key prefix) */
blockNumber: number | null;
/** Whether to enable the query (use for toggle) */
enabled?: boolean;
}
Expand Down Expand Up @@ -58,16 +58,15 @@ export function useAllCallFrameOpcodes({
{
query: {
transaction_hash_eq: transactionHash!,
// Include block_number for efficient partition-based filtering with FINAL
...(blockNumber ? { block_number_eq: blockNumber } : {}),
block_number_eq: blockNumber!,
order_by: 'gas DESC',
page_size: 10000,
},
},
'int_transaction_call_frame_opcode_gas',
signal
),
enabled: !!currentNetwork && !!transactionHash && enabled,
enabled: !!currentNetwork && !!transactionHash && !!blockNumber && enabled,
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export interface TransactionGasData {
export interface UseTransactionGasDataOptions {
/** Transaction hash to fetch data for */
transactionHash: string | null;
/** Block number for efficient API queries (uses partition key) */
/** Block number for efficient API queries (matches primary key prefix). Should always be provided when known. */
blockNumber?: number | null;
}

Expand Down Expand Up @@ -299,7 +299,6 @@ export function useTransactionGasData({
{
query: {
transaction_hash_eq: transactionHash!,
// Include block_number for efficient partition-based filtering with FINAL
...(blockNumber ? { block_number_eq: blockNumber } : {}),
order_by: 'call_frame_id ASC',
page_size: 10000,
Expand All @@ -319,7 +318,6 @@ export function useTransactionGasData({
{
query: {
transaction_hash_eq: transactionHash!,
// Include block_number for efficient partition-based filtering with FINAL
...(blockNumber ? { block_number_eq: blockNumber } : {}),
order_by: 'gas DESC',
page_size: 10000,
Expand Down
Loading