Commit c4f6b563 authored by tom goriunov's avatar tom goriunov Committed by GitHub

404 for unknown transaction (#1697)

* 404 for unknown transaction

Fixes #1689

* add helper for displaying custom error screen
parent 089e6674
...@@ -6,6 +6,7 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; ...@@ -6,6 +6,7 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import AddressCounterItem from 'ui/address/details/AddressCounterItem'; import AddressCounterItem from 'ui/address/details/AddressCounterItem';
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
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';
...@@ -59,15 +60,16 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -59,15 +60,16 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
has_validated_blocks: false, has_validated_blocks: false,
}), [ addressHash ]); }), [ addressHash ]);
const is404Error = addressQuery.isError && 'status' in addressQuery.error && addressQuery.error.status === 404; // error handling (except 404 codes)
const is422Error = addressQuery.isError && 'status' in addressQuery.error && addressQuery.error.status === 422; if (addressQuery.isError) {
if (isCustomAppError(addressQuery.error)) {
if (addressQuery.isError && is422Error) { const is404Error = addressQuery.isError && 'status' in addressQuery.error && addressQuery.error.status === 404;
throwOnResourceLoadError(addressQuery); if (!is404Error) {
} throwOnResourceLoadError(addressQuery);
}
if (addressQuery.isError && !is404Error) { } else {
return <DataFetchAlert/>; return <DataFetchAlert/>;
}
} }
const data = addressQuery.isError ? error404Data : addressQuery.data; const data = addressQuery.isError ? error404Data : addressQuery.data;
......
...@@ -7,6 +7,7 @@ import getQueryParamString from 'lib/router/getQueryParamString'; ...@@ -7,6 +7,7 @@ import getQueryParamString from 'lib/router/getQueryParamString';
import { BLOB } from 'stubs/blobs'; import { BLOB } from 'stubs/blobs';
import BlobInfo from 'ui/blob/BlobInfo'; import BlobInfo from 'ui/blob/BlobInfo';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import BlobEntity from 'ui/shared/entities/blob/BlobEntity'; import BlobEntity from 'ui/shared/entities/blob/BlobEntity';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -25,7 +26,7 @@ const BlobPageContent = () => { ...@@ -25,7 +26,7 @@ const BlobPageContent = () => {
const content = (() => { const content = (() => {
if (isError) { if (isError) {
if (error?.status === 422 || error?.status === 404) { if (isCustomAppError(error)) {
throwOnResourceLoadError({ resource: 'blob', error, isError: true }); throwOnResourceLoadError({ resource: 'blob', error, isError: true });
} }
......
...@@ -9,6 +9,7 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; ...@@ -9,6 +9,7 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { publicClient } from 'lib/web3/client'; import { publicClient } from 'lib/web3/client';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
...@@ -103,7 +104,7 @@ const TransactionPageContent = () => { ...@@ -103,7 +104,7 @@ const TransactionPageContent = () => {
})(); })();
if (isError && !showDegradedView) { if (isError && !showDegradedView) {
if (error?.status === 422 || error?.status === 404) { if (isCustomAppError(error)) {
throwOnResourceLoadError({ resource: 'tx', error, isError: true }); throwOnResourceLoadError({ resource: 'tx', error, isError: true });
} }
} }
......
...@@ -36,8 +36,8 @@ test('status code 500', async({ mount }) => { ...@@ -36,8 +36,8 @@ test('status code 500', async({ mount }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('invalid tx hash', async({ mount }) => { test('tx not found', async({ mount }) => {
const error = { message: 'Invalid tx hash', cause: { status: 422, resource: 'tx' } } as Error; const error = { message: 'Not found', cause: { status: 404, resource: 'tx' } } as Error;
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<AppError error={ error }/> <AppError error={ error }/>
......
...@@ -11,8 +11,8 @@ import getResourceErrorPayload from 'lib/errors/getResourceErrorPayload'; ...@@ -11,8 +11,8 @@ import getResourceErrorPayload from 'lib/errors/getResourceErrorPayload';
import AppErrorIcon from './AppErrorIcon'; import AppErrorIcon from './AppErrorIcon';
import AppErrorTitle from './AppErrorTitle'; import AppErrorTitle from './AppErrorTitle';
import AppErrorBlockConsensus from './custom/AppErrorBlockConsensus'; import AppErrorBlockConsensus from './custom/AppErrorBlockConsensus';
import AppErrorInvalidTxHash from './custom/AppErrorInvalidTxHash';
import AppErrorTooManyRequests from './custom/AppErrorTooManyRequests'; import AppErrorTooManyRequests from './custom/AppErrorTooManyRequests';
import AppErrorTxNotFound from './custom/AppErrorTxNotFound';
interface Props { interface Props {
className?: string; className?: string;
...@@ -47,11 +47,11 @@ const AppError = ({ error, className }: Props) => { ...@@ -47,11 +47,11 @@ const AppError = ({ error, className }: Props) => {
undefined; undefined;
const statusCode = getErrorCauseStatusCode(error) || getErrorObjStatusCode(error); const statusCode = getErrorCauseStatusCode(error) || getErrorObjStatusCode(error);
const isInvalidTxHash = cause && 'resource' in cause && cause.resource === 'tx' && statusCode === 422; const isInvalidTxHash = cause && 'resource' in cause && cause.resource === 'tx' && statusCode === 404;
const isBlockConsensus = messageInPayload?.includes('Block lost consensus'); const isBlockConsensus = messageInPayload?.includes('Block lost consensus');
if (isInvalidTxHash) { if (isInvalidTxHash) {
return <AppErrorInvalidTxHash/>; return <AppErrorTxNotFound/>;
} }
if (isBlockConsensus) { if (isBlockConsensus) {
......
/* eslint-disable max-len */ /* eslint-disable max-len */
import { Box, OrderedList, ListItem, useColorModeValue, Flex } from '@chakra-ui/react'; import { Box, OrderedList, ListItem, useColorModeValue, Flex, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import AppErrorTitle from '../AppErrorTitle'; import AppErrorTitle from '../AppErrorTitle';
const AppErrorInvalidTxHash = () => { const AppErrorTxNotFound = () => {
const textColor = useColorModeValue('gray.500', 'gray.400');
const snippet = { const snippet = {
borderColor: useColorModeValue('blackAlpha.300', 'whiteAlpha.300'), borderColor: useColorModeValue('blackAlpha.300', 'whiteAlpha.300'),
iconBg: useColorModeValue('blackAlpha.800', 'whiteAlpha.800'), iconBg: useColorModeValue('blackAlpha.800', 'whiteAlpha.800'),
...@@ -36,7 +35,7 @@ const AppErrorInvalidTxHash = () => { ...@@ -36,7 +35,7 @@ const AppErrorInvalidTxHash = () => {
</Flex> </Flex>
</Box> </Box>
<AppErrorTitle title="Sorry, we are unable to locate this transaction hash"/> <AppErrorTitle title="Sorry, we are unable to locate this transaction hash"/>
<OrderedList color={ textColor } mt={ 3 } spacing={ 3 }> <OrderedList mt={ 3 } spacing={ 3 }>
<ListItem> <ListItem>
If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page. If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page.
</ListItem> </ListItem>
...@@ -47,11 +46,13 @@ const AppErrorInvalidTxHash = () => { ...@@ -47,11 +46,13 @@ const AppErrorInvalidTxHash = () => {
During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it. During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it.
</ListItem> </ListItem>
<ListItem> <ListItem>
If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information. <span>If it still does not show up after 1 hour, please check with your </span>
<chakra.span fontWeight={ 600 }>sender/exchange/wallet/transaction provider</chakra.span>
<span> for additional information.</span>
</ListItem> </ListItem>
</OrderedList> </OrderedList>
</> </>
); );
}; };
export default AppErrorInvalidTxHash; export default AppErrorTxNotFound;
import type { ResourceError } from 'lib/api/resources';
// status codes when custom error screen should be shown
const CUSTOM_STATUS_CODES = [ 404, 422, 429 ];
export default function isCustomAppError(error: ResourceError<unknown>) {
return CUSTOM_STATUS_CODES.includes(error.status);
}
...@@ -7,12 +7,14 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -7,12 +7,14 @@ import type { Transaction } from 'types/api/transaction';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import hexToDecimal from 'lib/hexToDecimal'; import hexToDecimal from 'lib/hexToDecimal';
import { publicClient } from 'lib/web3/client'; import { publicClient } from 'lib/web3/client';
import { GET_BLOCK, GET_TRANSACTION, GET_TRANSACTION_RECEIPT, GET_TRANSACTION_CONFIRMATIONS } from 'stubs/RPC'; import { GET_BLOCK, GET_TRANSACTION, GET_TRANSACTION_RECEIPT, GET_TRANSACTION_CONFIRMATIONS } from 'stubs/RPC';
import { unknownAddress } from 'ui/shared/address/utils'; import { unknownAddress } from 'ui/shared/address/utils';
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
import TestnetWarning from 'ui/shared/alerts/TestnetWarning'; import TestnetWarning from 'ui/shared/alerts/TestnetWarning';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxInfo from './details/TxInfo'; import TxInfo from './details/TxInfo';
...@@ -141,8 +143,8 @@ const TxDetailsDegraded = ({ hash, txQuery }: Props) => { ...@@ -141,8 +143,8 @@ const TxDetailsDegraded = ({ hash, txQuery }: Props) => {
}, [ txQuery.setRefetchOnError ]); }, [ txQuery.setRefetchOnError ]);
if (!query.data) { if (!query.data) {
if (originalError?.status === 404) { if (originalError && isCustomAppError(originalError)) {
throw Error('Not found', { cause: { status: 404 } as unknown as Error }); throwOnResourceLoadError({ resource: 'tx', error: originalError, isError: true });
} }
return <DataFetchAlert/>; return <DataFetchAlert/>;
......
...@@ -10,6 +10,7 @@ import { route } from 'nextjs-routes'; ...@@ -10,6 +10,7 @@ import { route } from 'nextjs-routes';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
...@@ -42,7 +43,7 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => { ...@@ -42,7 +43,7 @@ const ZkEvmL2TxnBatchDetails = ({ query }: Props) => {
}, [ data, router ]); }, [ data, router ]);
if (isError) { if (isError) {
if (error?.status === 404 || error?.status === 422) { if (isCustomAppError(error)) {
throwOnResourceLoadError({ isError, error }); throwOnResourceLoadError({ isError, error });
} }
......
...@@ -12,6 +12,7 @@ import { WEI, WEI_IN_GWEI } from 'lib/consts'; ...@@ -12,6 +12,7 @@ import { WEI, WEI_IN_GWEI } from 'lib/consts';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import isCustomAppError from 'ui/shared/AppError/isCustomAppError';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
...@@ -48,7 +49,7 @@ const UserOpDetails = ({ query }: Props) => { ...@@ -48,7 +49,7 @@ const UserOpDetails = ({ query }: Props) => {
}, []); }, []);
if (isError) { if (isError) {
if (error?.status === 400 || error?.status === 404 || error?.status === 422) { if (error?.status === 400 || isCustomAppError(error)) {
throwOnResourceLoadError({ isError, error }); throwOnResourceLoadError({ isError, error });
} }
......
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