Commit 36dae944 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Graceful service degradation: address page (#1580)

* Graceful service degradation: address page

Fixes #1579

* fix ts

* [skip ci] check if public client was initialized
parent 85cc44e1
...@@ -53,6 +53,10 @@ export const GET_TRANSACTION_RECEIPT: TransactionReceipt = { ...@@ -53,6 +53,10 @@ export const GET_TRANSACTION_RECEIPT: TransactionReceipt = {
export const GET_TRANSACTION_CONFIRMATIONS = BigInt(420); export const GET_TRANSACTION_CONFIRMATIONS = BigInt(420);
export const GET_BALANCE = BigInt(42_000_000_000_000);
export const GET_TRANSACTIONS_COUNT = 42;
export const GET_BLOCK: GetBlockReturnType<Chain, false, 'latest'> = { export const GET_BLOCK: GetBlockReturnType<Chain, false, 'latest'> = {
baseFeePerGas: BigInt(11), baseFeePerGas: BigInt(11),
difficulty: BigInt(111), difficulty: BigInt(111),
......
...@@ -39,7 +39,7 @@ export interface Address extends UserTags { ...@@ -39,7 +39,7 @@ export interface Address extends UserTags {
export interface AddressCounters { export interface AddressCounters {
transactions_count: string; transactions_count: string;
token_transfers_count: string; token_transfers_count: string;
gas_usage_count: string; gas_usage_count: string | null;
validations_count: string | null; validations_count: string | 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 { WindowProvider } from 'wagmi'; import type { WindowProvider } from 'wagmi';
import type { Address } from 'types/api/address';
import type { ResourceError } from 'lib/api/resources';
import * as addressMock from 'mocks/address/address'; import * as addressMock from 'mocks/address/address';
import * as countersMock from 'mocks/address/counters'; import * as countersMock from 'mocks/address/counters';
import * as tokensMock from 'mocks/address/tokens'; import * as tokensMock from 'mocks/address/tokens';
...@@ -15,6 +11,7 @@ import * as configs from 'playwright/utils/configs'; ...@@ -15,6 +11,7 @@ import * as configs from 'playwright/utils/configs';
import AddressDetails from './AddressDetails'; import AddressDetails from './AddressDetails';
import MockAddressPage from './testUtils/MockAddressPage'; import MockAddressPage from './testUtils/MockAddressPage';
import type { AddressQuery } from './utils/useAddressQuery';
const ADDRESS_HASH = addressMock.hash; const ADDRESS_HASH = addressMock.hash;
const API_URL_ADDRESS = buildApiUrl('address', { hash: ADDRESS_HASH }); const API_URL_ADDRESS = buildApiUrl('address', { hash: ADDRESS_HASH });
...@@ -40,7 +37,7 @@ test('contract +@mobile', async({ mount, page }) => { ...@@ -40,7 +37,7 @@ test('contract +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AddressDetails addressQuery={{ data: addressMock.contract } as UseQueryResult<Address, ResourceError>}/> <AddressDetails addressQuery={{ data: addressMock.contract } as AddressQuery}/>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
...@@ -82,7 +79,7 @@ test('token', async({ mount, page }) => { ...@@ -82,7 +79,7 @@ test('token', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<MockAddressPage> <MockAddressPage>
<AddressDetails addressQuery={{ data: addressMock.token } as UseQueryResult<Address, ResourceError>}/> <AddressDetails addressQuery={{ data: addressMock.token } as AddressQuery}/>
</MockAddressPage> </MockAddressPage>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
...@@ -106,7 +103,7 @@ test('validator +@mobile', async({ mount, page }) => { ...@@ -106,7 +103,7 @@ test('validator +@mobile', async({ mount, page }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AddressDetails addressQuery={{ data: addressMock.validator } as UseQueryResult<Address, ResourceError>}/> <AddressDetails addressQuery={{ data: addressMock.validator } as AddressQuery}/>
</TestApp>, </TestApp>,
{ hooksConfig }, { hooksConfig },
); );
......
import { Box, Text, Grid } from '@chakra-ui/react'; import { Box, Text, Grid } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { Address as TAddress } from 'types/api/address';
import type { ResourceError } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { ADDRESS_COUNTERS } from 'stubs/address';
import AddressCounterItem from 'ui/address/details/AddressCounterItem'; import AddressCounterItem from 'ui/address/details/AddressCounterItem';
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem'; import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
...@@ -21,9 +16,11 @@ import TxEntity from 'ui/shared/entities/tx/TxEntity'; ...@@ -21,9 +16,11 @@ import TxEntity from 'ui/shared/entities/tx/TxEntity';
import AddressBalance from './details/AddressBalance'; import AddressBalance from './details/AddressBalance';
import AddressNameInfo from './details/AddressNameInfo'; import AddressNameInfo from './details/AddressNameInfo';
import TokenSelect from './tokenSelect/TokenSelect'; import TokenSelect from './tokenSelect/TokenSelect';
import useAddressCountersQuery from './utils/useAddressCountersQuery';
import type { AddressQuery } from './utils/useAddressQuery';
interface Props { interface Props {
addressQuery: UseQueryResult<TAddress, ResourceError>; addressQuery: AddressQuery;
scrollRef?: React.RefObject<HTMLDivElement>; scrollRef?: React.RefObject<HTMLDivElement>;
} }
...@@ -32,12 +29,9 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -32,12 +29,9 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
const addressHash = getQueryParamString(router.query.hash); const addressHash = getQueryParamString(router.query.hash);
const countersQuery = useApiQuery('address_counters', { const countersQuery = useAddressCountersQuery({
pathParams: { hash: addressHash }, hash: addressHash,
queryOptions: { addressQuery,
enabled: Boolean(addressHash) && Boolean(addressQuery.data),
placeholderData: ADDRESS_COUNTERS,
},
}); });
const handleCounterItemClick = React.useCallback(() => { const handleCounterItemClick = React.useCallback(() => {
...@@ -47,7 +41,7 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -47,7 +41,7 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
}, 500); }, 500);
}, [ scrollRef ]); }, [ scrollRef ]);
const errorData = React.useMemo(() => ({ const error404Data = React.useMemo(() => ({
hash: addressHash || '', hash: addressHash || '',
is_contract: false, is_contract: false,
implementation_name: null, implementation_name: null,
...@@ -76,142 +70,151 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -76,142 +70,151 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
const data = addressQuery.isError ? errorData : addressQuery.data; const data = addressQuery.isError ? error404Data : addressQuery.data;
if (!data) { if (!data) {
return null; return null;
} }
return ( return (
<Grid <>
columnGap={ 8 } { addressQuery.isDegradedData && <ServiceDegradationWarning isLoading={ addressQuery.isPlaceholderData } mb={ 6 }/> }
rowGap={{ base: 1, lg: 3 }} <Grid
templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden" columnGap={ 8 }
> rowGap={{ base: 1, lg: 3 }}
<AddressNameInfo data={ data } isLoading={ addressQuery.isPlaceholderData }/> templateColumns={{ base: 'minmax(0, 1fr)', lg: 'auto minmax(0, 1fr)' }} overflow="hidden"
{ data.is_contract && data.creation_tx_hash && data.creator_address_hash && (
<DetailsInfoItem
title="Creator"
hint="Transaction and address of creation"
isLoading={ addressQuery.isPlaceholderData }
>
<AddressEntity
address={{ hash: data.creator_address_hash }}
truncation="constant"
noIcon
/>
<Text whiteSpace="pre"> at txn </Text>
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
</DetailsInfoItem>
) }
{ data.is_contract && data.implementation_address && (
<DetailsInfoItem
title="Implementation"
hint="Implementation address of the proxy contract"
columnGap={ 1 }
>
<AddressEntity
address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
isLoading={ addressQuery.isPlaceholderData }
noIcon
/>
</DetailsInfoItem>
) }
<AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/>
{ data.has_tokens && (
<DetailsInfoItem
title="Tokens"
hint="All tokens in the account and total value"
alignSelf="center"
py={ 0 }
>
{ addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box py="6px">0</Box> }
</DetailsInfoItem>
) }
<DetailsInfoItem
title="Transactions"
hint="Number of transactions related to this address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
> >
{ addressQuery.data ? ( <AddressNameInfo data={ data } isLoading={ addressQuery.isPlaceholderData }/>
<AddressCounterItem { data.is_contract && data.creation_tx_hash && data.creator_address_hash && (
prop="transactions_count" <DetailsInfoItem
query={ countersQuery } title="Creator"
address={ data.hash } hint="Transaction and address of creation"
onClick={ handleCounterItemClick } isLoading={ addressQuery.isPlaceholderData }
isAddressQueryLoading={ addressQuery.isPlaceholderData } >
/> <AddressEntity
) : address={{ hash: data.creator_address_hash }}
0 } truncation="constant"
</DetailsInfoItem> noIcon
{ data.has_token_transfers && (
<DetailsInfoItem
title="Transfers"
hint="Number of transfers to/from this address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
>
{ addressQuery.data ? (
<AddressCounterItem
prop="token_transfers_count"
query={ countersQuery }
address={ data.hash }
onClick={ handleCounterItemClick }
isAddressQueryLoading={ addressQuery.isPlaceholderData }
/> />
) : <Text whiteSpace="pre"> at txn </Text>
0 } <TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
<DetailsInfoItem { data.is_contract && data.implementation_address && (
title="Gas used" <DetailsInfoItem
hint="Gas used by the address" title="Implementation"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData } hint="Implementation address of the proxy contract"
> columnGap={ 1 }
{ addressQuery.data ? ( >
<AddressCounterItem <AddressEntity
prop="gas_usage_count" address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
query={ countersQuery } isLoading={ addressQuery.isPlaceholderData }
address={ data.hash } noIcon
onClick={ handleCounterItemClick } />
isAddressQueryLoading={ addressQuery.isPlaceholderData } </DetailsInfoItem>
/> ) }
) : <AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/>
0 } { data.has_tokens && (
</DetailsInfoItem> <DetailsInfoItem
{ data.has_validated_blocks && ( title="Tokens"
hint="All tokens in the account and total value"
alignSelf="center"
py={ 0 }
>
{ addressQuery.data ? <TokenSelect onClick={ handleCounterItemClick }/> : <Box py="6px">0</Box> }
</DetailsInfoItem>
) }
<DetailsInfoItem <DetailsInfoItem
title="Blocks validated" title="Transactions"
hint="Number of blocks validated by this validator" hint="Number of transactions related to this address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
> >
{ addressQuery.data ? ( { addressQuery.data ? (
<AddressCounterItem <AddressCounterItem
prop="validations_count" prop="transactions_count"
query={ countersQuery } query={ countersQuery }
address={ data.hash } address={ data.hash }
onClick={ handleCounterItemClick } onClick={ handleCounterItemClick }
isAddressQueryLoading={ addressQuery.isPlaceholderData } isAddressQueryLoading={ addressQuery.isPlaceholderData }
isDegradedData={ addressQuery.isDegradedData }
/> />
) : ) :
0 } 0 }
</DetailsInfoItem> </DetailsInfoItem>
) } { data.has_token_transfers && (
{ data.block_number_balance_updated_at && ( <DetailsInfoItem
<DetailsInfoItem title="Transfers"
title="Last balance update" hint="Number of transfers to/from this address"
hint="Block number in which the address was updated" isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
alignSelf="center" >
py={{ base: '2px', lg: 1 }} { addressQuery.data ? (
isLoading={ addressQuery.isPlaceholderData } <AddressCounterItem
> prop="token_transfers_count"
<BlockEntity query={ countersQuery }
number={ data.block_number_balance_updated_at } address={ data.hash }
onClick={ handleCounterItemClick }
isAddressQueryLoading={ addressQuery.isPlaceholderData }
isDegradedData={ addressQuery.isDegradedData }
/>
) :
0 }
</DetailsInfoItem>
) }
{ countersQuery.data?.gas_usage_count && (
<DetailsInfoItem
title="Gas used"
hint="Gas used by the address"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
>
{ addressQuery.data ? (
<AddressCounterItem
prop="gas_usage_count"
query={ countersQuery }
address={ data.hash }
onClick={ handleCounterItemClick }
isAddressQueryLoading={ addressQuery.isPlaceholderData }
isDegradedData={ addressQuery.isDegradedData }
/>
) :
0 }
</DetailsInfoItem>
) }
{ data.has_validated_blocks && (
<DetailsInfoItem
title="Blocks validated"
hint="Number of blocks validated by this validator"
isLoading={ addressQuery.isPlaceholderData || countersQuery.isPlaceholderData }
>
{ addressQuery.data ? (
<AddressCounterItem
prop="validations_count"
query={ countersQuery }
address={ data.hash }
onClick={ handleCounterItemClick }
isAddressQueryLoading={ addressQuery.isPlaceholderData }
isDegradedData={ addressQuery.isDegradedData }
/>
) :
0 }
</DetailsInfoItem>
) }
{ data.block_number_balance_updated_at && (
<DetailsInfoItem
title="Last balance update"
hint="Block number in which the address was updated"
alignSelf="center"
py={{ base: '2px', lg: 1 }}
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
/> >
</DetailsInfoItem> <BlockEntity
) } number={ data.block_number_balance_updated_at }
<DetailsSponsoredItem isLoading={ addressQuery.isPlaceholderData }/> isLoading={ addressQuery.isPlaceholderData }
</Grid> />
</DetailsInfoItem>
) }
<DetailsSponsoredItem isLoading={ addressQuery.isPlaceholderData }/>
</Grid>
</>
); );
}; };
......
...@@ -16,6 +16,7 @@ interface Props { ...@@ -16,6 +16,7 @@ interface Props {
address: string; address: string;
onClick: () => void; onClick: () => void;
isAddressQueryLoading: boolean; isAddressQueryLoading: boolean;
isDegradedData: boolean;
} }
const PROP_TO_TAB = { const PROP_TO_TAB = {
...@@ -24,7 +25,7 @@ const PROP_TO_TAB = { ...@@ -24,7 +25,7 @@ const PROP_TO_TAB = {
validations_count: 'blocks_validated', validations_count: 'blocks_validated',
}; };
const AddressCounterItem = ({ prop, query, address, onClick, isAddressQueryLoading }: Props) => { const AddressCounterItem = ({ prop, query, address, onClick, isAddressQueryLoading, isDegradedData }: Props) => {
if (query.isPlaceholderData || isAddressQueryLoading) { if (query.isPlaceholderData || isAddressQueryLoading) {
return <Skeleton h={ 5 } w="80px" borderRadius="full"/>; return <Skeleton h={ 5 } w="80px" borderRadius="full"/>;
} }
...@@ -44,6 +45,11 @@ const AddressCounterItem = ({ prop, query, address, onClick, isAddressQueryLoadi ...@@ -44,6 +45,11 @@ const AddressCounterItem = ({ prop, query, address, onClick, isAddressQueryLoadi
if (data === '0') { if (data === '0') {
return <span>0</span>; return <span>0</span>;
} }
if (isDegradedData) {
return <span>{ Number(data).toLocaleString() }</span>;
}
return ( return (
<LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: address, tab: PROP_TO_TAB[prop] } }) } onClick={ onClick }> <LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: address, tab: PROP_TO_TAB[prop] } }) } onClick={ onClick }>
{ Number(data).toLocaleString() } { Number(data).toLocaleString() }
......
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import type { AddressCounters } from 'types/api/address';
import type { ResourceError } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery';
import { publicClient } from 'lib/web3/client';
import { ADDRESS_COUNTERS } from 'stubs/address';
import { GET_TRANSACTIONS_COUNT } from 'stubs/RPC';
import type { AddressQuery } from './useAddressQuery';
type RpcResponseType = [
number | null,
];
export type AddressCountersQuery = UseQueryResult<AddressCounters, ResourceError<{ status: number }>> & {
isDegradedData: boolean;
};
interface Params {
hash: string;
addressQuery: AddressQuery;
}
export default function useAddressQuery({ hash, addressQuery }: Params): AddressCountersQuery {
const enabled = Boolean(hash) && !addressQuery.isPlaceholderData;
const apiQuery = useApiQuery<'address_counters', { status: number }>('address_counters', {
pathParams: { hash },
queryOptions: {
enabled: enabled && !addressQuery.isDegradedData,
placeholderData: ADDRESS_COUNTERS,
refetchOnMount: false,
},
});
const rpcQuery = useQuery<RpcResponseType, unknown, AddressCounters | null>({
queryKey: [ 'RPC', 'address_counters', { hash } ],
queryFn: async() => {
const txCount = publicClient.getTransactionCount({ address: hash as `0x${ string }` }).catch(() => null);
return Promise.all([
txCount,
]);
},
select: (response) => {
const [ txCount ] = response;
return {
transactions_count: txCount?.toString() ?? '0',
token_transfers_count: '0',
gas_usage_count: null,
validations_count: null,
};
},
placeholderData: [ GET_TRANSACTIONS_COUNT ],
enabled: enabled && (addressQuery.isDegradedData || apiQuery.isError),
retry: false,
refetchOnMount: false,
});
const isRpcQuery = Boolean((addressQuery.isDegradedData || apiQuery.isError) && rpcQuery.data);
const query = isRpcQuery ? rpcQuery as UseQueryResult<AddressCounters, ResourceError<{ status: number }>> : apiQuery;
return {
...query,
isDegradedData: isRpcQuery,
};
}
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import type { Address } from 'types/api/address';
import type { ResourceError } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery';
import { retry } from 'lib/api/useQueryClientConfig';
import { SECOND } from 'lib/consts';
import { publicClient } from 'lib/web3/client';
import { ADDRESS_INFO } from 'stubs/address';
import { GET_BALANCE } from 'stubs/RPC';
type RpcResponseType = [
bigint | null,
];
export type AddressQuery = UseQueryResult<Address, ResourceError<{ status: number }>> & {
isDegradedData: boolean;
};
interface Params {
hash: string;
}
export default function useAddressQuery({ hash }: Params): AddressQuery {
const [ isRefetchEnabled, setRefetchEnabled ] = React.useState(false);
const apiQuery = useApiQuery<'address', { status: number }>('address', {
pathParams: { hash },
queryOptions: {
enabled: Boolean(hash),
placeholderData: ADDRESS_INFO,
refetchOnMount: false,
retry: (failureCount, error) => {
if (isRefetchEnabled) {
return false;
}
return retry(failureCount, error);
},
refetchInterval: (): number | false => {
return isRefetchEnabled ? 15 * SECOND : false;
},
},
});
const rpcQuery = useQuery<RpcResponseType, unknown, Address | null>({
queryKey: [ 'RPC', 'address', { hash } ],
queryFn: async() => {
if (!publicClient) {
throw new Error('No public RPC client');
}
const balance = publicClient.getBalance({ address: hash as `0x${ string }` }).catch(() => null);
return Promise.all([
balance,
]);
},
select: (response) => {
const [ balance ] = response;
if (!balance) {
return null;
}
return {
hash,
block_number_balance_updated_at: null,
coin_balance: balance.toString(),
creator_address_hash: null,
creation_tx_hash: null,
exchange_rate: null,
ens_domain_name: null,
has_custom_methods_read: false,
has_custom_methods_write: false,
has_decompiled_code: false,
has_logs: false,
has_methods_read: false,
has_methods_read_proxy: false,
has_methods_write: false,
has_methods_write_proxy: false,
has_token_transfers: false,
has_tokens: false,
has_validated_blocks: false,
implementation_address: null,
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
token: null,
watchlist_address_id: null,
private_tags: null,
public_tags: null,
watchlist_names: null,
};
},
placeholderData: [ GET_BALANCE ],
enabled: apiQuery.isError || apiQuery.errorUpdateCount > 0,
retry: false,
refetchOnMount: false,
});
React.useEffect(() => {
if (apiQuery.isPlaceholderData || !publicClient) {
return;
}
if (apiQuery.isError && apiQuery.errorUpdateCount === 1) {
setRefetchEnabled(true);
} else if (!apiQuery.isError) {
setRefetchEnabled(false);
}
}, [ apiQuery.errorUpdateCount, apiQuery.isError, apiQuery.isPlaceholderData ]);
React.useEffect(() => {
if (!rpcQuery.isPlaceholderData && !rpcQuery.data) {
setRefetchEnabled(false);
}
}, [ rpcQuery.data, rpcQuery.isPlaceholderData ]);
const isRpcQuery = Boolean((apiQuery.isError || apiQuery.isPlaceholderData) && apiQuery.errorUpdateCount > 0 && rpcQuery.data && publicClient);
const query = isRpcQuery ? rpcQuery as UseQueryResult<Address, ResourceError<{ status: number }>> : apiQuery;
return {
...query,
isDegradedData: isRpcQuery,
};
}
...@@ -10,7 +10,7 @@ import { useAppContext } from 'lib/contexts/app'; ...@@ -10,7 +10,7 @@ import { useAppContext } from 'lib/contexts/app';
import useContractTabs from 'lib/hooks/useContractTabs'; import useContractTabs from 'lib/hooks/useContractTabs';
import useIsSafeAddress from 'lib/hooks/useIsSafeAddress'; import useIsSafeAddress from 'lib/hooks/useIsSafeAddress';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { ADDRESS_INFO, ADDRESS_TABS_COUNTERS } from 'stubs/address'; import { ADDRESS_TABS_COUNTERS } from 'stubs/address';
import { USER_OPS_ACCOUNT } from 'stubs/userOps'; import { USER_OPS_ACCOUNT } from 'stubs/userOps';
import AddressBlocksValidated from 'ui/address/AddressBlocksValidated'; import AddressBlocksValidated from 'ui/address/AddressBlocksValidated';
import AddressCoinBalance from 'ui/address/AddressCoinBalance'; import AddressCoinBalance from 'ui/address/AddressCoinBalance';
...@@ -27,6 +27,7 @@ import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton'; ...@@ -27,6 +27,7 @@ import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton';
import AddressQrCode from 'ui/address/details/AddressQrCode'; import AddressQrCode from 'ui/address/details/AddressQrCode';
import AddressEnsDomains from 'ui/address/ensDomains/AddressEnsDomains'; import AddressEnsDomains from 'ui/address/ensDomains/AddressEnsDomains';
import SolidityscanReport from 'ui/address/SolidityscanReport'; import SolidityscanReport from 'ui/address/SolidityscanReport';
import useAddressQuery from 'ui/address/utils/useAddressQuery';
import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu'; import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
...@@ -48,13 +49,7 @@ const AddressPageContent = () => { ...@@ -48,13 +49,7 @@ const AddressPageContent = () => {
const tabsScrollRef = React.useRef<HTMLDivElement>(null); const tabsScrollRef = React.useRef<HTMLDivElement>(null);
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const addressQuery = useApiQuery('address', { const addressQuery = useAddressQuery({ hash });
pathParams: { hash },
queryOptions: {
enabled: Boolean(hash),
placeholderData: ADDRESS_INFO,
},
});
const addressTabsCountersQuery = useApiQuery('address_tabs_counters', { const addressTabsCountersQuery = useApiQuery('address_tabs_counters', {
pathParams: { hash }, pathParams: { hash },
...@@ -176,7 +171,7 @@ const AddressPageContent = () => { ...@@ -176,7 +171,7 @@ const AddressPageContent = () => {
/> />
); );
const content = addressQuery.isError ? null : <RoutedTabs tabs={ tabs } tabListProps={{ mt: 8 }}/>; const content = (addressQuery.isError || addressQuery.isDegradedData) ? null : <RoutedTabs tabs={ tabs } tabListProps={{ mt: 8 }}/>;
const backLink = React.useMemo(() => { const backLink = React.useMemo(() => {
const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/accounts'); const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/accounts');
......
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