Commit 61e0da60 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #655 from blockscout/reorg-fixes

Reorg fixes
parents d22d85e3 da05548c
...@@ -49,7 +49,7 @@ export const base: Block = { ...@@ -49,7 +49,7 @@ export const base: Block = {
uncles_hashes: [], uncles_hashes: [],
}; };
export const genesis = { export const genesis: Block = {
base_fee_per_gas: null, base_fee_per_gas: null,
burnt_fees: null, burnt_fees: null,
burnt_fees_percentage: null, burnt_fees_percentage: null,
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test, expect } from '@playwright/experimental-ct-react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block';
import type { ResourceError } from 'lib/api/resources';
import * as blockMock from 'mocks/blocks/block'; import * as blockMock from 'mocks/blocks/block';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import BlockDetails from './BlockDetails'; import BlockDetails from './BlockDetails';
const API_URL = buildApiUrl('block', { height: '1' });
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { height: '1' }, query: { height: '1' },
...@@ -15,14 +17,14 @@ const hooksConfig = { ...@@ -15,14 +17,14 @@ const hooksConfig = {
}; };
test('regular block +@mobile +@dark-mode', async({ mount, page }) => { test('regular block +@mobile +@dark-mode', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ const query = {
status: 200, data: blockMock.base,
body: JSON.stringify(blockMock.base), isLoading: false,
})); } as UseQueryResult<Block, ResourceError>;
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<BlockDetails/> <BlockDetails query={ query }/>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
...@@ -33,14 +35,14 @@ test('regular block +@mobile +@dark-mode', async({ mount, page }) => { ...@@ -33,14 +35,14 @@ test('regular block +@mobile +@dark-mode', async({ mount, page }) => {
}); });
test('genesis block', async({ mount, page }) => { test('genesis block', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({ const query = {
status: 200, data: blockMock.genesis,
body: JSON.stringify(blockMock.genesis), isLoading: false,
})); } as UseQueryResult<Block, ResourceError>;
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<BlockDetails/> <BlockDetails query={ query }/>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
......
import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react'; import { Grid, GridItem, Text, Icon, Link, Box, Tooltip } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import capitalize from 'lodash/capitalize'; import capitalize from 'lodash/capitalize';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -6,10 +7,12 @@ import { route } from 'nextjs-routes'; ...@@ -6,10 +7,12 @@ import { route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import type { Block } from 'types/api/block';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import useApiQuery from 'lib/api/useApiQuery'; import type { ResourceError } from 'lib/api/resources';
import getBlockReward from 'lib/block/getBlockReward'; import getBlockReward from 'lib/block/getBlockReward';
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts'; import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -28,15 +31,16 @@ import PrevNext from 'ui/shared/PrevNext'; ...@@ -28,15 +31,16 @@ import PrevNext from 'ui/shared/PrevNext';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
const BlockDetails = () => { interface Props {
query: UseQueryResult<Block, ResourceError>;
}
const BlockDetails = ({ query }: Props) => {
const [ isExpanded, setIsExpanded ] = React.useState(false); const [ isExpanded, setIsExpanded ] = React.useState(false);
const router = useRouter(); const router = useRouter();
const height = getQueryParamString(router.query.height); const heightOrHash = getQueryParamString(router.query.height);
const { data, isLoading, isError, error } = useApiQuery('block', { const { data, isLoading, isError, error } = query;
pathParams: { height },
queryOptions: { enabled: Boolean(height) },
});
const handleCutClick = React.useCallback(() => { const handleCutClick = React.useCallback(() => {
setIsExpanded((flag) => !flag); setIsExpanded((flag) => !flag);
...@@ -47,11 +51,15 @@ const BlockDetails = () => { ...@@ -47,11 +51,15 @@ const BlockDetails = () => {
}, []); }, []);
const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => { const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => {
if (!data) {
return;
}
const increment = direction === 'next' ? +1 : -1; const increment = direction === 'next' ? +1 : -1;
const nextId = String(Number(height) + increment); const nextId = String(data.height + increment);
router.push({ pathname: '/block/[height]', query: { height: nextId } }, undefined); router.push({ pathname: '/block/[height]', query: { height: nextId } }, undefined);
}, [ height, router ]); }, [ data, router ]);
if (isLoading) { if (isLoading) {
return <BlockDetailsSkeleton/>; return <BlockDetailsSkeleton/>;
...@@ -85,7 +93,7 @@ const BlockDetails = () => { ...@@ -85,7 +93,7 @@ const BlockDetails = () => {
return ( return (
<Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"> <Grid columnGap={ 8 } rowGap={{ base: 3, lg: 3 }} templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden">
<DetailsInfoItem <DetailsInfoItem
title="Block height" title={ `${ data.type === 'reorg' ? 'Reorg' : 'Block' } height` }
hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain" hint="The block height of a particular block is defined as the number of blocks preceding it in the blockchain"
> >
{ data.height } { data.height }
...@@ -117,7 +125,7 @@ const BlockDetails = () => { ...@@ -117,7 +125,7 @@ const BlockDetails = () => {
title="Transactions" title="Transactions"
hint="The number of transactions in the block" hint="The number of transactions in the block"
> >
<LinkInternal href={ route({ pathname: '/block/[height]', query: { height, tab: 'txs' } }) }> <LinkInternal href={ route({ pathname: '/block/[height]', query: { height: heightOrHash, tab: 'txs' } }) }>
{ data.tx_count } transaction{ data.tx_count === 1 ? '' : 's' } { data.tx_count } transaction{ data.tx_count === 1 ? '' : 's' }
</LinkInternal> </LinkInternal>
</DetailsInfoItem> </DetailsInfoItem>
......
...@@ -36,7 +36,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -36,7 +36,7 @@ const BlocksListItem = ({ data, isPending, enableTimeIncrement }: Props) => {
{ isPending && <Spinner size="sm"/> } { isPending && <Spinner size="sm"/> }
<LinkInternal <LinkInternal
fontWeight={ 600 } fontWeight={ 600 }
href={ route({ pathname: '/block/[height]', query: { height: String(data.height) } }) } href={ route({ pathname: '/block/[height]', query: { height: data.type === 'reorg' ? String(data.hash) : String(data.height) } }) }
> >
{ data.height } { data.height }
</LinkInternal> </LinkInternal>
......
...@@ -41,7 +41,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => { ...@@ -41,7 +41,7 @@ const BlocksTableItem = ({ data, isPending, enableTimeIncrement }: Props) => {
<Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations"> <Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations">
<LinkInternal <LinkInternal
fontWeight={ 600 } fontWeight={ 600 }
href={ route({ pathname: '/block/[height]', query: { height: String(data.height) } }) } href={ route({ pathname: '/block/[height]', query: { height: data.type === 'reorg' ? String(data.hash) : String(data.height) } }) }
> >
{ data.height } { data.height }
</LinkInternal> </LinkInternal>
......
...@@ -12,7 +12,7 @@ import ContractVerificationFieldOptimization from '../fields/ContractVerificatio ...@@ -12,7 +12,7 @@ import ContractVerificationFieldOptimization from '../fields/ContractVerificatio
const ContractVerificationFlattenSourceCode = () => { const ContractVerificationFlattenSourceCode = () => {
return ( return (
<ContractVerificationMethod title="Contract verification via Solidity (fattened source code)"> <ContractVerificationMethod title="Contract verification via Solidity (flattened source code)">
<ContractVerificationFieldName/> <ContractVerificationFieldName/>
<ContractVerificationFieldIsYul/> <ContractVerificationFieldIsYul/>
<ContractVerificationFieldCompiler/> <ContractVerificationFieldCompiler/>
......
import { Skeleton } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
...@@ -28,6 +30,11 @@ const BlockPageContent = () => { ...@@ -28,6 +30,11 @@ const BlockPageContent = () => {
const height = getQueryParamString(router.query.height); const height = getQueryParamString(router.query.height);
const tab = getQueryParamString(router.query.tab); const tab = getQueryParamString(router.query.tab);
const blockQuery = useApiQuery('block', {
pathParams: { height },
queryOptions: { enabled: Boolean(height) },
});
const blockTxsQuery = useQueryWithPages({ const blockTxsQuery = useQueryWithPages({
resourceName: 'block_txs', resourceName: 'block_txs',
pathParams: { height }, pathParams: { height },
...@@ -40,10 +47,10 @@ const BlockPageContent = () => { ...@@ -40,10 +47,10 @@ const BlockPageContent = () => {
throw new Error('Block not found', { cause: { status: 404 } }); throw new Error('Block not found', { cause: { status: 404 } });
} }
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = React.useMemo(() => ([
{ id: 'index', title: 'Details', component: <BlockDetails/> }, { id: 'index', title: 'Details', component: <BlockDetails query={ blockQuery }/> },
{ id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> }, { id: 'txs', title: 'Transactions', component: <TxsContent query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> },
]; ]), [ blockQuery, blockTxsQuery ]);
const hasPagination = !isMobile && tab === 'txs' && blockTxsQuery.isPaginationVisible; const hasPagination = !isMobile && tab === 'txs' && blockTxsQuery.isPaginationVisible;
...@@ -51,12 +58,16 @@ const BlockPageContent = () => { ...@@ -51,12 +58,16 @@ const BlockPageContent = () => {
return ( return (
<Page> <Page>
<TextAd mb={ 6 }/> { blockQuery.isLoading ? <Skeleton h={{ base: 12, lg: 6 }} mb={ 6 } w="100%" maxW="680px"/> : <TextAd mb={ 6 }/> }
{ blockQuery.isLoading ? (
<Skeleton h={ 10 } w="300px" mb={ 6 }/>
) : (
<PageTitle <PageTitle
text={ `Block #${ height }` } text={ `Block #${ blockQuery.data?.height }` }
backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined } backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined }
backLinkLabel="Back to blocks list" backLinkLabel="Back to blocks list"
/> />
) }
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS } tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
......
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
Flex, Flex,
Tooltip, Tooltip,
chakra, chakra,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
...@@ -59,6 +60,7 @@ const TxDetails = () => { ...@@ -59,6 +60,7 @@ const TxDetails = () => {
smooth: true, smooth: true,
}); });
}, []); }, []);
const executionSuccessIconColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
if (isLoading) { if (isLoading) {
return <TxDetailsSkeleton/>; return <TxDetailsSkeleton/>;
...@@ -94,7 +96,7 @@ const TxDetails = () => { ...@@ -94,7 +96,7 @@ const TxDetails = () => {
const executionSuccessBadge = toAddress.is_contract && data.result === 'success' ? ( const executionSuccessBadge = toAddress.is_contract && data.result === 'success' ? (
<Tooltip label="Contract execution completed"> <Tooltip label="Contract execution completed">
<chakra.span display="inline-flex" ml={ 2 } mr={ 1 }> <chakra.span display="inline-flex" ml={ 2 } mr={ 1 }>
<Icon as={ successIcon } boxSize={ 4 } color="green.500" cursor="pointer"/> <Icon as={ successIcon } boxSize={ 4 } color={ executionSuccessIconColor } cursor="pointer"/>
</chakra.span> </chakra.span>
</Tooltip> </Tooltip>
) : null; ) : null;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment