Skip to content

Block page: internal transactions tab #2721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions lib/api/services/general/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
BlockCountdownResponse,
BlockEpoch,
BlockEpochElectionRewardDetailsResponse,
BlockInternalTransactionsResponse,
} from 'types/api/block';
import type { TTxsWithBlobsFilters } from 'types/api/txsFilters';

Expand All @@ -27,6 +28,11 @@ export const GENERAL_API_BLOCK_RESOURCES = {
filterFields: [ 'type' as const ],
paginated: true,
},
block_internal_txs: {
path: '/api/v2/blocks/:height_or_hash/internal-transactions',
pathParams: [ 'height_or_hash' as const ],
paginated: true,
},
block_withdrawals: {
path: '/api/v2/blocks/:height_or_hash/withdrawals',
pathParams: [ 'height_or_hash' as const ],
Expand Down Expand Up @@ -54,6 +60,7 @@ R extends 'general:blocks' ? BlocksResponse :
R extends 'general:block' ? Block :
R extends 'general:block_countdown' ? BlockCountdownResponse :
R extends 'general:block_txs' ? BlockTransactionsResponse :
R extends 'general:block_internal_txs' ? BlockInternalTransactionsResponse :
R extends 'general:block_withdrawals' ? BlockWithdrawalsResponse :
R extends 'general:block_epoch' ? BlockEpoch :
R extends 'general:block_election_rewards' ? BlockEpochElectionRewardDetailsResponse :
Expand Down
2 changes: 2 additions & 0 deletions mocks/blocks/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const base: Block = {
timestamp: '2022-11-11T11:59:35Z',
total_difficulty: '10258276095980170141167591583995189665817672619',
transactions_count: 5,
internal_transactions_count: 12,
transaction_fees: '26853607500000000',
type: 'block',
uncles_hashes: [],
Expand Down Expand Up @@ -90,6 +91,7 @@ export const genesis: Block = {
timestamp: '2017-12-16T00:13:24.000000Z',
total_difficulty: '131072',
transactions_count: 0,
internal_transactions_count: 0,
transaction_fees: '0',
type: 'block',
uncles_hashes: [],
Expand Down
1 change: 1 addition & 0 deletions stubs/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const BLOCK: Block = {
timestamp: '2023-05-12T19:29:12.000000Z',
total_difficulty: '10837812015930321201107455268036056402048391639',
transactions_count: 142,
internal_transactions_count: 42,
transaction_fees: '19241635547777613',
type: 'block',
uncles_hashes: [],
Expand Down
10 changes: 10 additions & 0 deletions types/api/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Reward } from 'types/api/reward';
import type { Transaction } from 'types/api/transaction';

import type { ArbitrumBatchStatus, ArbitrumL2TxData } from './arbitrumL2';
import type { InternalTransaction } from './internalTransaction';
import type { OptimisticL2BatchDataContainer, OptimisticL2BlobTypeEip4844, OptimisticL2BlobTypeCelestia } from './optimisticL2';
import type { TokenInfo } from './token';
import type { TokenTransfer } from './tokenTransfer';
Expand All @@ -21,6 +22,7 @@ export interface Block {
height: number;
timestamp: string;
transactions_count: number;
internal_transactions_count: number;
miner: AddressParam;
size: number;
hash: string;
Expand Down Expand Up @@ -125,6 +127,14 @@ export interface BlockTransactionsResponse {
} | null;
}

export interface BlockInternalTransactionsResponse {
items: Array<InternalTransaction>;
next_page_params: {
block_index: number;
items_count: number;
} | null;
}

export interface NewBlockSocketResponse {
average_block_time: string;
block: Block;
Expand Down
39 changes: 39 additions & 0 deletions ui/block/BlockInternalTxs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Box } from '@chakra-ui/react';
import React from 'react';

import InternalTxsList from 'ui/internalTxs/InternalTxsList';
import InternalTxsTable from 'ui/internalTxs/InternalTxsTable';
import DataListDisplay from 'ui/shared/DataListDisplay';
import type { QueryWithPagesResult } from 'ui/shared/pagination/useQueryWithPages';

interface Props {
query: QueryWithPagesResult<'general:block_internal_txs'>;
top?: number;
}

const BlockInternalTxs = ({ query, top }: Props) => {
const { data, isPlaceholderData, isError } = query;

const content = data?.items ? (
<>
<Box hideFrom="lg">
<InternalTxsList data={ data.items } isLoading={ isPlaceholderData } showBlockInfo={ false }/>
</Box>
<Box hideBelow="lg">
<InternalTxsTable data={ data.items } isLoading={ isPlaceholderData } top={ top } showBlockInfo={ false }/>
</Box>
</>
) : null;

return (
<DataListDisplay
isError={ isError }
itemsNum={ data?.items.length }
emptyText="There are no internal transactions."
>
{ content }
</DataListDisplay>
);
};

export default React.memo(BlockInternalTxs);
25 changes: 25 additions & 0 deletions ui/block/useBlockInternalTxsQuery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { INTERNAL_TX } from 'stubs/internalTx';
import { generateListStub } from 'stubs/utils';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';

import type { BlockQuery } from './useBlockQuery';

interface Params {
heightOrHash: string;
blockQuery: BlockQuery;
tab: string;
}

export default function useBlockInternalTxsQuery({ heightOrHash, blockQuery, tab }: Params) {
const apiQuery = useQueryWithPages({
resourceName: 'general:block_internal_txs',
pathParams: { height_or_hash: heightOrHash },
options: {
enabled: Boolean(tab === 'internal_txs' && !blockQuery.isPlaceholderData && blockQuery.data?.internal_transactions_count),
placeholderData: generateListStub<'general:block_internal_txs'>(INTERNAL_TX, 10, { next_page_params: null }),
refetchOnMount: false,
},
});

return apiQuery;
}
1 change: 1 addition & 0 deletions ui/block/useBlockQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
height: Number(block.number),
timestamp: dayjs.unix(Number(block.timestamp)).format(),
transactions_count: block.transactions.length,
internal_transactions_count: 0,
miner: { ...unknownAddress, hash: block.miner },
size: Number(block.size),
hash: block.hash,
Expand Down
4 changes: 3 additions & 1 deletion ui/internalTxs/InternalTxsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ type Props = {
data: Array<InternalTransaction>;
currentAddress?: string;
isLoading?: boolean;
showBlockInfo?: boolean;
};

const InternalTxsList = ({ data, currentAddress, isLoading }: Props) => {
const InternalTxsList = ({ data, currentAddress, isLoading, showBlockInfo = true }: Props) => {
return (
<Box>
{ data.map((item, index) => (
Expand All @@ -20,6 +21,7 @@ const InternalTxsList = ({ data, currentAddress, isLoading }: Props) => {
{ ...item }
currentAddress={ currentAddress }
isLoading={ isLoading }
showBlockInfo={ showBlockInfo }
/>
)) }
</Box>
Expand Down
23 changes: 13 additions & 10 deletions ui/internalTxs/InternalTxsListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import TxStatus from 'ui/shared/statusTag/TxStatus';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';

type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean };
type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean; showBlockInfo?: boolean };

const InternalTxsListItem = ({
type,
Expand All @@ -31,6 +31,7 @@ const InternalTxsListItem = ({
timestamp,
currentAddress,
isLoading,
showBlockInfo = true,
}: Props) => {
const typeTitle = TX_INTERNALS_ITEMS.find(({ id }) => id === type)?.title;
const toData = to ? to : createdContract;
Expand All @@ -56,15 +57,17 @@ const InternalTxsListItem = ({
fontSize="sm"
/>
</Flex>
<HStack gap={ 1 }>
<Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Block</Skeleton>
<BlockEntity
isLoading={ isLoading }
number={ blockNumber }
noIcon
textStyle="sm"
/>
</HStack>
{ showBlockInfo && (
<HStack gap={ 1 }>
<Skeleton loading={ isLoading } fontSize="sm" fontWeight={ 500 }>Block</Skeleton>
<BlockEntity
isLoading={ isLoading }
number={ blockNumber }
noIcon
textStyle="sm"
/>
</HStack>
) }
<AddressFromTo
from={ from }
to={ toData }
Expand Down
9 changes: 6 additions & 3 deletions ui/internalTxs/InternalTxsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ interface Props {
data: Array<InternalTransaction>;
currentAddress?: string;
isLoading?: boolean;
top?: number;
showBlockInfo?: boolean;
}

const InternalTxsTable = ({ data, currentAddress, isLoading }: Props) => {
const InternalTxsTable = ({ data, currentAddress, isLoading, top, showBlockInfo = true }: Props) => {
return (
<AddressHighlightProvider>
<TableRoot minW="900px">
<TableHeaderSticky top={ 68 }>
<TableHeaderSticky top={ top ?? 68 }>
<TableRow>
<TableColumnHeader width="180px">Parent txn hash</TableColumnHeader>
<TableColumnHeader width="15%">Type</TableColumnHeader>
<TableColumnHeader width="15%">Block</TableColumnHeader>
{ showBlockInfo && <TableColumnHeader width="15%">Block</TableColumnHeader> }
<TableColumnHeader width="50%">From/To</TableColumnHeader>
<TableColumnHeader width="20%" isNumeric>
Value { currencyUnits.ether }
Expand All @@ -36,6 +38,7 @@ const InternalTxsTable = ({ data, currentAddress, isLoading }: Props) => {
{ ...item }
currentAddress={ currentAddress }
isLoading={ isLoading }
showBlockInfo={ showBlockInfo }
/>
)) }
</TableBody>
Expand Down
23 changes: 13 additions & 10 deletions ui/internalTxs/InternalTxsTableItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
import TruncatedValue from 'ui/shared/TruncatedValue';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';

type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean };
type Props = InternalTransaction & { currentAddress?: string; isLoading?: boolean; showBlockInfo?: boolean };

const InternalTxsTableItem = ({
type,
Expand All @@ -30,6 +30,7 @@ const InternalTxsTableItem = ({
timestamp,
currentAddress,
isLoading,
showBlockInfo = true,
}: Props) => {
const typeTitle = TX_INTERNALS_ITEMS.find(({ id }) => id === type)?.title;
const toData = to ? to : createdContract;
Expand Down Expand Up @@ -63,15 +64,17 @@ const InternalTxsTableItem = ({
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
</Flex>
</TableCell>
<TableCell verticalAlign="middle">
<BlockEntity
isLoading={ isLoading }
number={ blockNumber }
noIcon
textStyle="sm"
fontWeight={ 500 }
/>
</TableCell>
{ showBlockInfo && (
<TableCell verticalAlign="middle">
<BlockEntity
isLoading={ isLoading }
number={ blockNumber }
noIcon
textStyle="sm"
fontWeight={ 500 }
/>
</TableCell>
) }
<TableCell verticalAlign="middle">
<AddressFromTo
from={ from }
Expand Down
20 changes: 18 additions & 2 deletions ui/pages/Block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import BlockCeloEpochTag from 'ui/block/BlockCeloEpochTag';
import BlockDetails from 'ui/block/BlockDetails';
import BlockEpochRewards from 'ui/block/BlockEpochRewards';
import BlockInternalTxs from 'ui/block/BlockInternalTxs';
import BlockWithdrawals from 'ui/block/BlockWithdrawals';
import useBlockBlobTxsQuery from 'ui/block/useBlockBlobTxsQuery';
import useBlockInternalTxsQuery from 'ui/block/useBlockInternalTxsQuery';
import useBlockQuery from 'ui/block/useBlockQuery';
import useBlockTxsQuery from 'ui/block/useBlockTxsQuery';
import useBlockWithdrawalsQuery from 'ui/block/useBlockWithdrawalsQuery';
Expand Down Expand Up @@ -50,10 +52,12 @@ const BlockPageContent = () => {
const blockTxsQuery = useBlockTxsQuery({ heightOrHash, blockQuery, tab });
const blockWithdrawalsQuery = useBlockWithdrawalsQuery({ heightOrHash, blockQuery, tab });
const blockBlobTxsQuery = useBlockBlobTxsQuery({ heightOrHash, blockQuery, tab });
const blockInternalTxsQuery = useBlockInternalTxsQuery({ heightOrHash, blockQuery, tab });

const hasPagination = !isMobile && (
(tab === 'txs' && blockTxsQuery.pagination.isVisible) ||
(tab === 'withdrawals' && blockWithdrawalsQuery.pagination.isVisible)
(tab === 'withdrawals' && blockWithdrawalsQuery.pagination.isVisible) ||
(tab === 'internal_txs' && blockInternalTxsQuery.pagination.isVisible)
);

const tabs: Array<TabItemRegular> = React.useMemo(() => ([
Expand All @@ -77,6 +81,16 @@ const BlockPageContent = () => {
</>
),
},
blockQuery.data?.internal_transactions_count ? {
id: 'internal_txs',
title: 'Internal txns',
component: (
<>
{ blockTxsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockTxsQuery.isPlaceholderData } mb={ 6 }/> }
<BlockInternalTxs query={ blockInternalTxsQuery } top={ hasPagination ? TABS_HEIGHT : 0 }/>
</>
),
} : null,
config.features.dataAvailability.isEnabled && blockQuery.data?.blob_transaction_count ?
{
id: 'blob_txs',
Expand All @@ -101,13 +115,15 @@ const BlockPageContent = () => {
title: 'Epoch rewards',
component: <BlockEpochRewards heightOrHash={ heightOrHash }/>,
} : null,
].filter(Boolean)), [ blockBlobTxsQuery, blockQuery, blockTxsQuery, blockWithdrawalsQuery, hasPagination, heightOrHash ]);
].filter(Boolean)), [ blockBlobTxsQuery, blockInternalTxsQuery, blockQuery, blockTxsQuery, blockWithdrawalsQuery, hasPagination, heightOrHash ]);

let pagination;
if (tab === 'txs') {
pagination = blockTxsQuery.pagination;
} else if (tab === 'withdrawals') {
pagination = blockWithdrawalsQuery.pagination;
} else if (tab === 'internal_txs') {
pagination = blockInternalTxsQuery.pagination;
}

const backLink = React.useMemo(() => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading