Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,12 @@ const TxInterpretation = ({ summary, isLoading, addressDataMap, className, chain
/>
);
}
const trimmedChunk = chunk.trim();
const textColor = trimmedChunk.includes('failed to call') ? 'text.error' : 'text.secondary';

return (
<chakra.span key={ chunk + index }>
<chakra.span color="text.secondary">{ chunk.trim() + (chunk.trim() && variablesNames[index] ? ' ' : '') }</chakra.span>
<chakra.span color={ textColor }>{ trimmedChunk + (trimmedChunk && variablesNames[index] ? ' ' : '') }</chakra.span>
{ index < variablesNames.length && content }
</chakra.span>
);
Expand Down
9 changes: 5 additions & 4 deletions src/features/user-ops/components/UserOpStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import React from 'react';

import StatusTag from 'src/shared/tags/status-tag/StatusTag';

type Props = {
import type { BadgeProps } from 'src/toolkit/chakra/badge';

interface Props extends BadgeProps {
status?: boolean;
isLoading?: boolean;
};

const UserOpStatus = ({ status, isLoading }: Props) => {
const UserOpStatus = ({ status, ...rest }: Props) => {
if (status === undefined) {
return null;
}

return (
<StatusTag loading={ isLoading } type={ status === true ? 'ok' : 'error' } text={ status === true ? 'Success' : 'Failed' }/>
<StatusTag type={ status === true ? 'ok' : 'error' } text={ status === true ? 'Success' : 'Failed' } { ...rest }/>
);
};

Expand Down
42 changes: 23 additions & 19 deletions src/features/user-ops/pages/details/UserOpDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,32 @@ const UserOpDetails = ({ query }: Props) => {
>
Status
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<UserOpStatus status={ data.status } isLoading={ isLoading }/>
</DetailedInfo.ItemValue>

{ data.revert_reason && (
<>
<DetailedInfo.ItemLabel
hint="The revert reason of the User operation"
isLoading={ isLoading }
>
Revert reason
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue
wordBreak="break-all"
whiteSpace="normal"
<DetailedInfo.ItemValue flexWrap="wrap" columnGap={ 3 } rowGap={ 1 }>
<UserOpStatus status={ data.status } loading={ isLoading } my={{ base: '3px', lg: 1 }}/>
{ data.revert_reason && (
<CollapsibleDetails
text={ [ 'Show revert reason', 'Hide revert reason' ] }
variant="secondary"
noScroll
isExpanded
id="CollapsibleDetails__revert-reason"
>
<Skeleton loading={ isLoading }>
<Skeleton
loading={ isLoading }
w="100%"
p={{ base: 3, lg: 4 }}
bgColor={{ _light: 'red.50', _dark: 'red.900/80' }}
textStyle="sm"
borderBottomRadius="md"
mb={ 4 }
whiteSpace="normal"
wordBreak="break-all"
>
{ data.revert_reason }
</Skeleton>
</DetailedInfo.ItemValue>
</>
) }
</CollapsibleDetails>
) }
</DetailedInfo.ItemValue>

{ data.timestamp && (
<>
Expand Down
2 changes: 1 addition & 1 deletion src/features/user-ops/pages/index/UserOpsListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const UserOpsListItem = ({ item, isLoading, showTx, showSender, chainData }: Pro

<ListItemMobileGrid.Label isLoading={ isLoading }>Status</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value>
<UserOpStatus status={ item.status } isLoading={ isLoading }/>
<UserOpStatus status={ item.status } loading={ isLoading }/>
</ListItemMobileGrid.Value>

{ showSender && (
Expand Down
2 changes: 1 addition & 1 deletion src/features/user-ops/pages/index/UserOpsTableItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const UserOpsTableItem = ({ item, isLoading, showTx, showSender, chainData, exch
/>
</TableCell>
<TableCell verticalAlign="middle">
<UserOpStatus status={ item.status } isLoading={ isLoading }/>
<UserOpStatus status={ item.status } loading={ isLoading }/>
</TableCell>
{ showSender && (
<TableCell verticalAlign="middle">
Expand Down
6 changes: 4 additions & 2 deletions src/slices/log/components/LogDecodedInputData.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: LicenseRef-Blockscout

import type { JsxStyleProps } from '@chakra-ui/react';
import React from 'react';

import type { schemas } from '@blockscout/api-types';
Expand All @@ -10,9 +11,10 @@ interface Props {
data: schemas['DecodedLogInput'] | schemas['DecodedInput'];
isLoading?: boolean;
rightSlot?: React.ReactNode;
inputsTableProps?: JsxStyleProps;
}

const LogDecodedInputData = ({ data, isLoading, rightSlot }: Props) => {
const LogDecodedInputData = ({ data, isLoading, rightSlot, inputsTableProps }: Props) => {
return (
<>
<LogDecodedInputDataHeader
Expand All @@ -21,7 +23,7 @@ const LogDecodedInputData = ({ data, isLoading, rightSlot }: Props) => {
isLoading={ isLoading }
rightSlot={ rightSlot }
/>
{ data.parameters.length > 0 && <LogDecodedInputDataTable data={ data.parameters } isLoading={ isLoading }/> }
{ data.parameters.length > 0 && <LogDecodedInputDataTable data={ data.parameters } isLoading={ isLoading } { ...inputsTableProps }/> }
</>
);
};
Expand Down
7 changes: 4 additions & 3 deletions src/slices/log/components/LogDecodedInputDataTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: LicenseRef-Blockscout

import { Flex, Grid } from '@chakra-ui/react';
import { Flex, Grid, type JsxStyleProps } from '@chakra-ui/react';
import React from 'react';

import type { schemas } from '@blockscout/api-types';
Expand All @@ -12,7 +12,7 @@ import CopyToClipboard from 'src/shared/texts/CopyToClipboard';
import { Skeleton } from 'src/toolkit/chakra/skeleton';
import { TruncatedText } from 'src/toolkit/components/truncation/TruncatedText';

interface Props {
interface Props extends JsxStyleProps {
data: schemas['DecodedLogInput']['parameters'] | schemas['DecodedInput']['parameters'];
isLoading?: boolean;
}
Expand Down Expand Up @@ -79,7 +79,7 @@ const Row = ({
);
};

const LogDecodedInputDataTable = ({ data, isLoading }: Props) => {
const LogDecodedInputDataTable = ({ data, isLoading, ...rest }: Props) => {
const hasIndexed = data.some((item) => 'indexed' in item && typeof item.indexed === 'boolean');

const gridTemplateColumnsBase = hasIndexed ?
Expand All @@ -101,6 +101,7 @@ const LogDecodedInputDataTable = ({ data, isLoading }: Props) => {
rowGap={ 5 }
borderBottomLeftRadius="md"
borderBottomRightRadius="md"
{ ...rest }
>
<HeaderItem isLoading={ isLoading }>Name</HeaderItem>
<HeaderItem isLoading={ isLoading }>Type</HeaderItem>
Expand Down
20 changes: 20 additions & 0 deletions src/slices/tx/mocks/details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@ export const withRawRevertReason: schemas['TransactionResponse'] = {
},
};

export const withoutRevertReason: schemas['TransactionResponse'] = {
...withRawRevertReason,
revert_reason: {
raw: null,
},
};

export const withRevertReasonParam: schemas['TransactionResponse'] = {
...base,
status: 'error',
result: 'Reverted',
revert_reason: {
method_call: 'Error(string reason)',
method_id: '0x08c379a0',
parameters: [
{ name: 'reason', type: 'string', value: 'Only chairpersons can vote' },
],
},
};

export const pending: schemas['TransactionResponse'] = {
...base,
base_fee_per_gas: null,
Expand Down
2 changes: 1 addition & 1 deletion src/slices/tx/pages/details/TxSubHeading.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ test.describe('blockscout provider', () => {
});

test('no interpretation, has method called', async({ render, mockApiResponse, mockAssetResponse }) => {
const newTxQuery = { ...txQuery, data: txMock.withRecipientContract } as TxQuery;
const newTxQuery = { ...txQuery, data: { ...txMock.withRecipientContract, status: 'error' } } as TxQuery;
const metadataResponse = generateAddressMetadataResponse(protocolTagWithMeta);
await mockApiResponse('metadata:info', metadataResponse, { queryParams: addressMetadataQueryParams });
await mockAssetResponse(protocolTagWithMeta?.meta?.appLogoURL as string, './playwright/mocks/image_s.jpg');
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 0 additions & 18 deletions src/slices/tx/pages/details/info/TxDetails.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,6 @@ test('with token transfer +@mobile', async({ render, page, mockAssetResponse })
});
});

test('with decoded revert reason', async({ render, page }) => {
const component = await render(<TxDetails data={ txMock.withDecodedRevertReason } isLoading={ false }/>);

await expect(component).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ],
maskColor: pwConfig.maskColor,
});
});

test('with decoded raw reason', async({ render, page }) => {
const component = await render(<TxDetails data={ txMock.withRawRevertReason } isLoading={ false }/>);

await expect(component).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ],
maskColor: pwConfig.maskColor,
});
});

test('pending', async({ render, page }) => {
const component = await render(<TxDetails data={ txMock.pending } isLoading={ false }/>);

Expand Down
44 changes: 2 additions & 42 deletions src/slices/tx/pages/details/info/TxDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { currencyUnits } from 'src/slices/chain/units';
import getChainValidatorTitle from 'src/slices/chain/verification-type/utils/get-chain-validator-title';
import LogDecodedInputData from 'src/slices/log/components/LogDecodedInputData';
import TxSocketAlert from 'src/slices/tx/components/TxSocketAlert';
import TxStatus from 'src/slices/tx/components/TxStatus';
import getConfirmationDuration from 'src/slices/tx/utils/get-confirmation-duration';

import TxAllowedPeekers from 'src/features/chain-variants/suave/pages/tx/TxAllowedPeekers';
Expand Down Expand Up @@ -58,7 +57,6 @@ import SpriteIcon from 'src/sprite/SpriteIcon';

import { Badge } from 'src/toolkit/chakra/badge';
import { CollapsibleDetails } from 'src/toolkit/chakra/collapsible';
import { Link } from 'src/toolkit/chakra/link';
import { Skeleton } from 'src/toolkit/chakra/skeleton';
import { Tooltip } from 'src/toolkit/chakra/tooltip';

Expand All @@ -68,10 +66,10 @@ import TxDetailsGasPrice from './parts/TxDetailsGasPrice';
import TxDetailsGasUsage from './parts/TxDetailsGasUsage';
import TxDetailsOther from './parts/TxDetailsOther';
import TxDetailsSetMaxGasLimit from './parts/TxDetailsSetMaxGasLimit';
import TxDetailsStatus from './parts/TxDetailsStatus';
import TxDetailsTokenTransfers from './parts/TxDetailsTokenTransfers';
import TxDetailsTxFee from './parts/TxDetailsTxFee';
import TxHash from './parts/TxHash';
import TxRevertReason from './parts/TxRevertReason';

interface Props {
data: schemas['TransactionResponse'] | undefined;
Expand Down Expand Up @@ -156,32 +154,7 @@ const TxDetails = ({ data, isLoading, socketStatus, noTxActions }: Props) => {

<TxHash hash={ data.hash } isLoading={ isLoading } status={ data.status }/>

<DetailedInfo.ItemLabel
hint="Current transaction state: Success, Failed (Error), or Pending (In Process)"
isLoading={ isLoading }
>
{
rollupFeature.isEnabled &&
(rollupFeature.type === 'zkSync' || rollupFeature.type === 'arbitrum' || rollupFeature.type === 'scroll') ?
`${ layerLabels.current } status and method` :
'Status and method'
}
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue>
<TxStatus status={ data.status } errorText={ data.status === 'error' ? data.result : undefined } isLoading={ isLoading }/>
{ data.method && (
<Badge colorPalette={ data.method === 'Multicall' ? 'teal' : 'gray' } loading={ isLoading } truncated ml={ 3 }>
{ data.method }
</Badge>
) }
{ data.arbitrum?.contains_message && (
<Skeleton loading={ isLoading } onClick={ showAssociatedL1Tx }>
<Link truncate ml={ 3 }>
{ data.arbitrum?.contains_message === 'incoming' ? 'Incoming message' : 'Outgoing message' }
</Link>
</Skeleton>
) }
</DetailedInfo.ItemValue>
<TxDetailsStatus data={ data } isLoading={ isLoading } onShowDetailsClick={ showAssociatedL1Tx }/>

{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 &&
!config.slices.tx.hiddenFields?.L1_status && (
Expand Down Expand Up @@ -226,19 +199,6 @@ const TxDetails = ({ data, isLoading, socketStatus, noTxActions }: Props) => {
</>
) }

{ data.revert_reason && (
<>
<DetailedInfo.ItemLabel
hint="The revert reason of the transaction"
>
Revert reason
</DetailedInfo.ItemLabel>
<DetailedInfo.ItemValue flexWrap="wrap" mt={{ base: '5px', lg: '4px' }}>
<TxRevertReason { ...data.revert_reason }/>
</DetailedInfo.ItemValue>
</>
) }

{ !config.slices.tx.hiddenFields?.L1_status && data.zksync?.status && (
<>
<DetailedInfo.ItemLabel
Expand Down
Binary file not shown.
Binary file not shown.
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
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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions src/slices/tx/pages/details/info/parts/TxDetailsStatus.pw.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';

import * as txMock from 'src/slices/tx/mocks/details';

import { Container } from 'src/shared/detailed-info/DetailedInfo';

import { test, expect } from 'playwright/lib';

import TxDetailsStatus from './TxDetailsStatus';

test.describe('error', () => {
test('with decoded revert reason', async({ render }) => {
const component = await render(
<Container>
<TxDetailsStatus data={ txMock.withDecodedRevertReason } isLoading={ false }/>
</Container>,
);
await component.getByText('Show revert reason').click();

await expect(component).toHaveScreenshot();
});

test('with decoded raw reason', async({ render }) => {
const component = await render(
<Container>
<TxDetailsStatus data={ txMock.withRawRevertReason } isLoading={ false }/>
</Container>,
);
await component.getByText('Show revert reason').click();

await expect(component).toHaveScreenshot();
});

test('without reason', async({ render }) => {
const component = await render(
<Container>
<TxDetailsStatus data={ txMock.withoutRevertReason } isLoading={ false }/>
</Container>,
);
await component.getByText('Show revert reason').click();

await expect(component).toHaveScreenshot();
});

test('with revert reason param', async({ render }) => {
const component = await render(
<Container>
<TxDetailsStatus data={ txMock.withRevertReasonParam } isLoading={ false }/>
</Container>,
);
await component.getByText('Show revert reason').click();
await component.getByText('failed').hover();

await expect(component).toHaveScreenshot();
});
});
Loading
Loading