Skip to content

Commit

Permalink
Internal transactions list page (blockscout#2553)
Browse files Browse the repository at this point in the history
* Internal transactions list page

Fixes blockscout#2526

* update nav icon for internal txns route

* fix ts error

* fix external txs test
  • Loading branch information
tom2drum authored Feb 18, 2025
1 parent bb77f8f commit 540d95e
Show file tree
Hide file tree
Showing 27 changed files with 209 additions and 78 deletions.
11 changes: 11 additions & 0 deletions icons/internal_txns.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
117 changes: 64 additions & 53 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,9 @@ export const RESOURCES = {
withdrawals_counters: {
path: '/api/v2/withdrawals/counters',
},
internal_txs: {
path: '/api/v2/internal-transactions',
},

// ADDRESSES
addresses: {
Expand Down Expand Up @@ -1273,7 +1276,7 @@ export interface ResourceError<T = unknown> {
export type ResourceErrorAccount<T> = ResourceError<{ errors: T }>;

export type PaginatedResources = 'blocks' | 'block_txs' | 'block_election_rewards' |
'txs_validated' | 'txs_pending' | 'txs_with_blobs' | 'txs_watchlist' | 'txs_execution_node' |
'txs_validated' | 'txs_pending' | 'txs_with_blobs' | 'txs_watchlist' | 'txs_execution_node' | 'internal_txs' |
'tx_internal_txs' | 'tx_logs' | 'tx_token_transfers' | 'tx_state_changes' | 'tx_blobs' |
'addresses' | 'addresses_metadata_search' |
'address_txs' | 'address_internal_txs' | 'address_token_transfers' | 'address_blocks_validated' | 'address_coin_balance' |
Expand Down Expand Up @@ -1342,6 +1345,7 @@ Q extends 'txs_pending' ? TransactionsResponsePending :
Q extends 'txs_with_blobs' ? TransactionsResponseWithBlobs :
Q extends 'txs_watchlist' ? TransactionsResponseWatchlist :
Q extends 'txs_execution_node' ? TransactionsResponseValidated :
Q extends 'internal_txs' ? InternalTransactionsResponse :
Q extends 'tx' ? Transaction :
Q extends 'tx_internal_txs' ? InternalTransactionsResponse :
Q extends 'tx_logs' ? LogsResponseTx :
Expand Down Expand Up @@ -1388,20 +1392,6 @@ Q extends 'verified_contracts' ? VerifiedContractsResponse :
Q extends 'verified_contracts_counters' ? VerifiedContractsCounters :
Q extends 'visualize_sol2uml' ? visualizer.VisualizeResponse :
Q extends 'contract_verification_config' ? SmartContractVerificationConfigRaw :
Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse :
Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse :
Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse :
Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse :
Q extends 'optimistic_l2_txn_batches_count' ? number :
Q extends 'optimistic_l2_txn_batch' ? OptimismL2TxnBatch :
Q extends 'optimistic_l2_txn_batch_celestia' ? OptimismL2TxnBatch :
Q extends 'optimistic_l2_txn_batch_txs' ? OptimismL2BatchTxs :
Q extends 'optimistic_l2_txn_batch_blocks' ? OptimismL2BatchBlocks :
Q extends 'optimistic_l2_dispute_games' ? OptimisticL2DisputeGamesResponse :
Q extends 'optimistic_l2_output_roots_count' ? number :
Q extends 'optimistic_l2_withdrawals_count' ? number :
Q extends 'optimistic_l2_deposits_count' ? number :
Q extends 'optimistic_l2_dispute_games_count' ? number :
never;
// !!! IMPORTANT !!!
// See comment above
Expand All @@ -1422,34 +1412,6 @@ Q extends 'validators_blackfort' ? ValidatorsBlackfortResponse :
Q extends 'validators_blackfort_counters' ? ValidatorsBlackfortCountersResponse :
Q extends 'validators_zilliqa' ? ValidatorsZilliqaResponse :
Q extends 'validator_zilliqa' ? ValidatorZilliqa :
Q extends 'shibarium_withdrawals' ? ShibariumWithdrawalsResponse :
Q extends 'shibarium_deposits' ? ShibariumDepositsResponse :
Q extends 'shibarium_withdrawals_count' ? number :
Q extends 'shibarium_deposits_count' ? number :
Q extends 'arbitrum_l2_messages' ? ArbitrumL2MessagesResponse :
Q extends 'arbitrum_l2_messages_count' ? number :
Q extends 'arbitrum_l2_txn_batches' ? ArbitrumL2TxnBatchesResponse :
Q extends 'arbitrum_l2_txn_batches_count' ? number :
Q extends 'arbitrum_l2_txn_batch' ? ArbitrumL2TxnBatch :
Q extends 'arbitrum_l2_txn_batch_celestia' ? ArbitrumL2TxnBatch :
Q extends 'arbitrum_l2_txn_batch_txs' ? ArbitrumL2BatchTxs :
Q extends 'arbitrum_l2_txn_batch_blocks' ? ArbitrumL2BatchBlocks :
Q extends 'arbitrum_l2_txn_withdrawals' ? ArbitrumL2TxnWithdrawalsResponse :
Q extends 'arbitrum_l2_message_claim' ? ArbitrumL2MessageClaimResponse :
Q extends 'zkevm_l2_deposits' ? ZkEvmL2DepositsResponse :
Q extends 'zkevm_l2_deposits_count' ? number :
Q extends 'zkevm_l2_withdrawals' ? ZkEvmL2WithdrawalsResponse :
Q extends 'zkevm_l2_withdrawals_count' ? number :
Q extends 'zkevm_l2_txn_batches' ? ZkEvmL2TxnBatchesResponse :
Q extends 'zkevm_l2_txn_batches_count' ? number :
Q extends 'zkevm_l2_txn_batch' ? ZkEvmL2TxnBatch :
Q extends 'zkevm_l2_txn_batch_txs' ? ZkEvmL2TxnBatchTxs :
Q extends 'zksync_l2_txn_batches' ? ZkSyncBatchesResponse :
Q extends 'zksync_l2_txn_batches_count' ? number :
Q extends 'zksync_l2_txn_batch' ? ZkSyncBatch :
Q extends 'zksync_l2_txn_batch_txs' ? ZkSyncBatchTxs :
Q extends 'scroll_l2_txn_batch_txs' ? ScrollL2TxnBatchTxs :
Q extends 'scroll_l2_txn_batch_blocks' ? ScrollL2TxnBatchBlocks :
Q extends 'contract_security_audits' ? SmartContractSecurityAudits :
Q extends 'addresses_lookup' ? bens.LookupAddressResponse :
Q extends 'address_domain' ? bens.GetAddressResponse :
Expand Down Expand Up @@ -1485,13 +1447,6 @@ Q extends 'rewards_user_daily_claim' ? RewardsUserDailyClaimResponse :
Q extends 'rewards_user_referrals' ? RewardsUserReferralsResponse :
Q extends 'token_transfers_all' ? TokenTransferResponse :
Q extends 'address_xstar_score' ? AddressXStarResponse :
Q extends 'scroll_l2_txn_batches' ? ScrollL2BatchesResponse :
Q extends 'scroll_l2_txn_batches_count' ? number :
Q extends 'scroll_l2_txn_batch' ? ScrollL2TxnBatch :
Q extends 'scroll_l2_deposits' ? ScrollL2MessagesResponse :
Q extends 'scroll_l2_deposits_count' ? number :
Q extends 'scroll_l2_withdrawals' ? ScrollL2MessagesResponse :
Q extends 'scroll_l2_withdrawals_count' ? number :
Q extends 'advanced_filter' ? AdvancedFilterResponse :
Q extends 'advanced_filter_methods' ? AdvancedFilterMethodsResponse :
Q extends 'pools' ? PoolsResponse :
Expand All @@ -1502,10 +1457,66 @@ Q extends 'stats_contracts' ? stats.ContractsPageStats :
never;
/* eslint-enable @stylistic/indent */

export type ResourcePayload<Q extends ResourceName> = ResourcePayloadA<Q> | ResourcePayloadB<Q>;
export type PaginatedResponseItems<Q extends ResourceName> = Q extends PaginatedResources ? ResourcePayloadA<Q>['items'] | ResourcePayloadB<Q>['items'] : never;
/* eslint-disable @stylistic/indent */
export type ResourcePayloadRollups<Q extends ResourceName> =
Q extends 'optimistic_l2_output_roots' ? OptimisticL2OutputRootsResponse :
Q extends 'optimistic_l2_withdrawals' ? OptimisticL2WithdrawalsResponse :
Q extends 'optimistic_l2_deposits' ? OptimisticL2DepositsResponse :
Q extends 'optimistic_l2_txn_batches' ? OptimisticL2TxnBatchesResponse :
Q extends 'optimistic_l2_txn_batches_count' ? number :
Q extends 'optimistic_l2_txn_batch' ? OptimismL2TxnBatch :
Q extends 'optimistic_l2_txn_batch_celestia' ? OptimismL2TxnBatch :
Q extends 'optimistic_l2_txn_batch_txs' ? OptimismL2BatchTxs :
Q extends 'optimistic_l2_txn_batch_blocks' ? OptimismL2BatchBlocks :
Q extends 'optimistic_l2_dispute_games' ? OptimisticL2DisputeGamesResponse :
Q extends 'optimistic_l2_output_roots_count' ? number :
Q extends 'optimistic_l2_withdrawals_count' ? number :
Q extends 'optimistic_l2_deposits_count' ? number :
Q extends 'optimistic_l2_dispute_games_count' ? number :
Q extends 'shibarium_withdrawals' ? ShibariumWithdrawalsResponse :
Q extends 'shibarium_deposits' ? ShibariumDepositsResponse :
Q extends 'shibarium_withdrawals_count' ? number :
Q extends 'shibarium_deposits_count' ? number :
Q extends 'arbitrum_l2_messages' ? ArbitrumL2MessagesResponse :
Q extends 'arbitrum_l2_messages_count' ? number :
Q extends 'arbitrum_l2_txn_batches' ? ArbitrumL2TxnBatchesResponse :
Q extends 'arbitrum_l2_txn_batches_count' ? number :
Q extends 'arbitrum_l2_txn_batch' ? ArbitrumL2TxnBatch :
Q extends 'arbitrum_l2_txn_batch_celestia' ? ArbitrumL2TxnBatch :
Q extends 'arbitrum_l2_txn_batch_txs' ? ArbitrumL2BatchTxs :
Q extends 'arbitrum_l2_txn_batch_blocks' ? ArbitrumL2BatchBlocks :
Q extends 'arbitrum_l2_txn_withdrawals' ? ArbitrumL2TxnWithdrawalsResponse :
Q extends 'arbitrum_l2_message_claim' ? ArbitrumL2MessageClaimResponse :
Q extends 'zkevm_l2_deposits' ? ZkEvmL2DepositsResponse :
Q extends 'zkevm_l2_deposits_count' ? number :
Q extends 'zkevm_l2_withdrawals' ? ZkEvmL2WithdrawalsResponse :
Q extends 'zkevm_l2_withdrawals_count' ? number :
Q extends 'zkevm_l2_txn_batches' ? ZkEvmL2TxnBatchesResponse :
Q extends 'zkevm_l2_txn_batches_count' ? number :
Q extends 'zkevm_l2_txn_batch' ? ZkEvmL2TxnBatch :
Q extends 'zkevm_l2_txn_batch_txs' ? ZkEvmL2TxnBatchTxs :
Q extends 'zksync_l2_txn_batches' ? ZkSyncBatchesResponse :
Q extends 'zksync_l2_txn_batches_count' ? number :
Q extends 'zksync_l2_txn_batch' ? ZkSyncBatch :
Q extends 'zksync_l2_txn_batch_txs' ? ZkSyncBatchTxs :
Q extends 'scroll_l2_txn_batch_txs' ? ScrollL2TxnBatchTxs :
Q extends 'scroll_l2_txn_batch_blocks' ? ScrollL2TxnBatchBlocks :
Q extends 'scroll_l2_txn_batches' ? ScrollL2BatchesResponse :
Q extends 'scroll_l2_txn_batches_count' ? number :
Q extends 'scroll_l2_txn_batch' ? ScrollL2TxnBatch :
Q extends 'scroll_l2_deposits' ? ScrollL2MessagesResponse :
Q extends 'scroll_l2_deposits_count' ? number :
Q extends 'scroll_l2_withdrawals' ? ScrollL2MessagesResponse :
Q extends 'scroll_l2_withdrawals_count' ? number :
never;
/* eslint-enable @stylistic/indent */

export type ResourcePayload<Q extends ResourceName> = ResourcePayloadA<Q> | ResourcePayloadB<Q> | ResourcePayloadRollups<Q>;
export type PaginatedResponseItems<Q extends ResourceName> = Q extends PaginatedResources ?
ResourcePayloadA<Q>['items'] | ResourcePayloadB<Q>['items'] | ResourcePayloadRollups<Q>['items'] :
never;
export type PaginatedResponseNextPageParams<Q extends ResourceName> = Q extends PaginatedResources ?
ResourcePayloadA<Q>['next_page_params'] | ResourcePayloadB<Q>['next_page_params'] :
ResourcePayloadA<Q>['next_page_params'] | ResourcePayloadB<Q>['next_page_params'] | ResourcePayloadRollups<Q>['next_page_params'] :
never;

/* eslint-disable @stylistic/indent */
Expand Down
10 changes: 10 additions & 0 deletions lib/hooks/useNavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export default function useNavItems(): ReturnType {
icon: 'transactions',
isActive: pathname === '/txs' || pathname === '/tx/[hash]',
};
const internalTxs: NavItem | null = {
text: 'Internal transactions',
nextRoute: { pathname: '/internal-txs' as const },
icon: 'internal_txns',
isActive: pathname === '/internal-txs',
};
const userOps: NavItem | null = config.features.userOps.isEnabled ? {
text: 'User operations',
nextRoute: { pathname: '/ops' as const },
Expand Down Expand Up @@ -118,6 +124,7 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
internalTxs,
rollupDeposits,
rollupWithdrawals,
],
Expand All @@ -140,6 +147,7 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
internalTxs,
rollupDeposits,
rollupWithdrawals,
],
Expand All @@ -155,6 +163,7 @@ export default function useNavItems(): ReturnType {
blockchainNavItems = [
[
txs,
internalTxs,
userOps,
blocks,
rollupTxnBatches,
Expand All @@ -169,6 +178,7 @@ export default function useNavItems(): ReturnType {
} else {
blockchainNavItems = [
txs,
internalTxs,
userOps,
blocks,
topAccounts,
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/getPageOgType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type OGPageType = 'Homepage' | 'Root page' | 'Regular page';
const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/': 'Homepage',
'/txs': 'Root page',
'/internal-txs': 'Root page',
'/txs/kettle/[hash]': 'Regular page',
'/tx/[hash]': 'Regular page',
'/blocks': 'Root page',
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/templates/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const DEFAULT_TEMPLATE = 'Open-source block explorer by Blockscout. Search trans
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': DEFAULT_TEMPLATE,
'/txs': DEFAULT_TEMPLATE,
'/internal-txs': DEFAULT_TEMPLATE,
'/txs/kettle/[hash]': DEFAULT_TEMPLATE,
'/tx/[hash]': 'View transaction %hash% on %network_title%',
'/blocks': DEFAULT_TEMPLATE,
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/templates/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import config from 'configs/app';
const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/': '%network_name% blockchain explorer - View %network_name% stats',
'/txs': '%network_name% transactions - %network_name% explorer',
'/internal-txs': '%network_name% internal transactions - %network_name% explorer',
'/txs/kettle/[hash]': '%network_name% kettle %hash% transactions',
'/tx/[hash]': '%network_name% transaction %hash%',
'/blocks': '%network_name% blocks',
Expand Down
1 change: 1 addition & 0 deletions lib/mixpanel/getPageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Route } from 'nextjs-routes';
export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/': 'Homepage',
'/txs': 'Transactions',
'/internal-txs': 'Internal transactions',
'/txs/kettle/[hash]': 'Kettle transactions',
'/tx/[hash]': 'Transaction details',
'/blocks': 'Blocks',
Expand Down
1 change: 1 addition & 0 deletions nextjs/nextjs-routes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ declare module "nextjs-routes" {
| StaticRoute<"/gas-tracker">
| StaticRoute<"/graphiql">
| StaticRoute<"/">
| StaticRoute<"/internal-txs">
| StaticRoute<"/login">
| StaticRoute<"/mud-worlds">
| DynamicRoute<"/name-domains/[name]", { "name": string }>
Expand Down
19 changes: 19 additions & 0 deletions pages/internal-txs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';

import PageNextJs from 'nextjs/PageNextJs';

const InternalTxs = dynamic(() => import('ui/pages/InternalTxs'), { ssr: false });

const Page: NextPage = () => {
return (
<PageNextJs pathname="/internal-txs">
<InternalTxs/>
</PageNextJs>
);
};

export default Page;

export { base as getServerSideProps } from 'nextjs/getServerSideProps';
1 change: 1 addition & 0 deletions public/icons/name.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
| "info"
| "integration/full"
| "integration/partial"
| "internal_txns"
| "key"
| "lightning_navbar"
| "lightning"
Expand Down
8 changes: 4 additions & 4 deletions ui/address/AddressInternalTxs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { apos } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString';
import { INTERNAL_TX } from 'stubs/internalTx';
import { generateListStub } from 'stubs/utils';
import AddressIntTxsTable from 'ui/address/internals/AddressIntTxsTable';
import InternalTxsList from 'ui/internalTxs/InternalTxsList';
import InternalTxsTable from 'ui/internalTxs/InternalTxsTable';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';

import AddressCsvExportLink from './AddressCsvExportLink';
import AddressTxsFilter from './AddressTxsFilter';
import AddressIntTxsList from './internals/AddressIntTxsList';

const getFilterValue = (getFilterValueFromQuery<AddressFromToFilter>).bind(null, AddressFromToFilterValues);

Expand Down Expand Up @@ -71,10 +71,10 @@ const AddressInternalTxs = ({ scrollRef, shouldRender = true, isQueryEnabled = t
const content = data?.items ? (
<>
<Show below="lg" ssr={ false }>
<AddressIntTxsList data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
<InternalTxsList data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
</Show>
<Hide below="lg" ssr={ false }>
<AddressIntTxsTable data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
<InternalTxsTable data={ data.items } currentAddress={ hash } isLoading={ isPlaceholderData }/>
</Hide>
</>
) : null ;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import React from 'react';

import type { InternalTransaction } from 'types/api/internalTransaction';

import AddressIntTxsListItem from 'ui/address/internals/AddressIntTxsListItem';
import InternalTxsListItem from './InternalTxsListItem';

type Props = {
data: Array<InternalTransaction>;
currentAddress: string;
currentAddress?: string;
isLoading?: boolean;
};

const AddressIntTxsList = ({ data, currentAddress, isLoading }: Props) => {
const InternalTxsList = ({ data, currentAddress, isLoading }: Props) => {
return (
<Box>
{ data.map((item, index) => (
<AddressIntTxsListItem
<InternalTxsListItem
key={ item.transaction_hash + '_' + index }
{ ...item }
currentAddress={ currentAddress }
Expand All @@ -26,4 +26,4 @@ const AddressIntTxsList = ({ data, currentAddress, isLoading }: Props) => {
);
};

export default AddressIntTxsList;
export default InternalTxsList;
Loading

0 comments on commit 540d95e

Please sign in to comment.