Skip to content

Commit

Permalink
Merge pull request #1457 from blockscout/fe-1435
Browse files Browse the repository at this point in the history
Transaction interpretations
  • Loading branch information
tom2drum authored Dec 28, 2023
2 parents 9facb82 + b8def50 commit 6bccd6c
Show file tree
Hide file tree
Showing 27 changed files with 513 additions and 28 deletions.
1 change: 1 addition & 0 deletions configs/app/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export { default as sentry } from './sentry';
export { default as sol2uml } from './sol2uml';
export { default as stats } from './stats';
export { default as suave } from './suave';
export { default as txInterpretation } from './txInterpretation';
export { default as web3Wallet } from './web3Wallet';
export { default as verifiedTokens } from './verifiedTokens';
export { default as zkEvmRollup } from './zkEvmRollup';
24 changes: 24 additions & 0 deletions configs/app/features/txInterpretation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Feature } from './types';

import { getEnvValue } from '../utils';

const title = 'Transaction interpretation';

const provider = getEnvValue('NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER') || 'none';

const config: Feature<{ provider: string }> = (() => {
if (provider !== 'none') {
return Object.freeze({
title,
provider,
isEnabled: true,
});
}

return Object.freeze({
title,
isEnabled: false,
});
})();

export default config;
1 change: 1 addition & 0 deletions configs/envs/.env.eth
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout

#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true
1 change: 1 addition & 0 deletions deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ const schema = yup
return isNoneSchema.isValidSync(data) || isArrayOfWalletsSchema.isValidSync(data);
}),
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET: yup.boolean(),
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER: yup.string().oneOf([ 'blockscout', 'none' ]),
NEXT_PUBLIC_AD_TEXT_PROVIDER: yup.string<AdTextProviders>().oneOf(SUPPORTED_AD_TEXT_PROVIDERS),
NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE: yup.boolean(),
NEXT_PUBLIC_OG_DESCRIPTION: yup.string(),
Expand Down
9 changes: 9 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [Solidity to UML diagrams](ENVS.md#solidity-to-uml-diagrams)
- [Blockchain statistics](ENVS.md#blockchain-statistics)
- [Web3 wallet integration](ENVS.md#web3-wallet-integration-add-token-or-network-to-the-wallet) (add token or network to the wallet)
- [Transaction interpretation](ENVS.md#transaction-interpretation)
- [Verified tokens info](ENVS.md#verified-tokens-info)
- [Bridged tokens](ENVS.md#bridged-tokens)
- [Safe{Core} address tags](ENVS.md#safecore-address-tags)
Expand Down Expand Up @@ -473,6 +474,14 @@ This feature is **enabled by default** with the `['metamask']` value. To switch

&nbsp;

### Transaction interpretation

| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER | `blockscout` \| `none` | Transaction interpretation provider that displays human readable transaction description | - | `none` | `blockscout` |

&nbsp;

### Verified tokens info

| Variable | Type| Description | Compulsoriness | Default value | Example value |
Expand Down
3 changes: 3 additions & 0 deletions icons/lightning.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import type {
TransactionsResponseWatchlist,
TransactionsSorting,
} from 'types/api/transaction';
import type { TxInterpretationResponse } from 'types/api/txInterpretation';
import type { TTxsFilters } from 'types/api/txsFilters';
import type { TxStateChanges } from 'types/api/txStateChanges';
import type { VerifiedContractsSorting } from 'types/api/verifiedContracts';
Expand Down Expand Up @@ -246,6 +247,10 @@ export const RESOURCES = {
pathParams: [ 'hash' as const ],
filterFields: [],
},
tx_interpretation: {
path: '/api/v2/transactions/:hash/summary',
pathParams: [ 'hash' as const ],
},
withdrawals: {
path: '/api/v2/withdrawals',
filterFields: [],
Expand Down Expand Up @@ -651,6 +656,7 @@ Q extends 'tx_logs' ? LogsResponseTx :
Q extends 'tx_token_transfers' ? TokenTransferResponse :
Q extends 'tx_raw_trace' ? RawTracesResponse :
Q extends 'tx_state_changes' ? TxStateChanges :
Q extends 'tx_interpretation' ? TxInterpretationResponse :
Q extends 'addresses' ? AddressesResponse :
Q extends 'address' ? Address :
Q extends 'address_counters' ? AddressCounters :
Expand Down
4 changes: 4 additions & 0 deletions lib/mixpanel/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum EventTypes {
CONTRACT_VERIFICATION = 'Contract verification',
QR_CODE = 'QR code',
PAGE_WIDGET = 'Page widget',
TX_INTERPRETATION_INTERACTION = 'Transaction interpratetion interaction'
}

/* eslint-disable @typescript-eslint/indent */
Expand Down Expand Up @@ -78,5 +79,8 @@ Type extends EventTypes.QR_CODE ? {
Type extends EventTypes.PAGE_WIDGET ? {
'Type': 'Tokens dropdown' | 'Tokens show all (icon)' | 'Add to watchlist' | 'Address actions (more button)';
} :
Type extends EventTypes.TX_INTERPRETATION_INTERACTION ? {
'Type': 'Address click' | 'Token click';
} :
undefined;
/* eslint-enable @typescript-eslint/indent */
45 changes: 45 additions & 0 deletions mocks/txs/txInterpretation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { TxInterpretationResponse } from 'types/api/txInterpretation';

export const txInterpretation: TxInterpretationResponse = {
data: {
summaries: [ {
summary_template: `{action_type} {amount} {token} to {to_address} on {timestamp}`,
summary_template_variables: {
action_type: { type: 'string', value: 'Transfer' },
amount: { type: 'currency', value: '100' },
token: {
type: 'token',
value: {
name: 'Duck',
type: 'ERC-20',
symbol: 'DUCK',
address: '0x486a3c5f34cDc4EF133f248f1C81168D78da52e8',
holders: '1152',
decimals: '18',
icon_url: null,
total_supply: '210000000000000000000000000',
exchange_rate: null,
circulating_market_cap: null,
},
},
to_address: {
type: 'address',
value: {
hash: '0x48c04ed5691981C42154C6167398f95e8f38a7fF',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
},
timestamp: {
type: 'timestamp',
value: '1687005431',
},
},
} ],
},
};
1 change: 1 addition & 0 deletions public/icons/name.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
| "graphQL"
| "info"
| "key"
| "lightning"
| "link"
| "lock"
| "minus"
Expand Down
34 changes: 34 additions & 0 deletions stubs/txInterpretation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { TxInterpretationResponse } from 'types/api/txInterpretation';

import { TOKEN_INFO_ERC_20 } from './token';

export const TX_INTERPRETATION: TxInterpretationResponse = {
data: {
summaries: [
{
summary_template: '{action_type} {source_amount} Ether into {destination_amount} {destination_token}',
summary_template_variables: {
action_type: { type: 'string', value: 'Wrap' },
source_amount: { type: 'currency', value: '0.7' },
destination_amount: { type: 'currency', value: '0.7' },
destination_token: {
type: 'token',
value: TOKEN_INFO_ERC_20,
},
},
},
{
summary_template: '{action_type} {source_amount} Ether into {destination_amount} {destination_token}',
summary_template_variables: {
action_type: { type: 'string', value: 'Wrap' },
source_amount: { type: 'currency', value: '0.7' },
destination_amount: { type: 'currency', value: '0.7' },
destination_token: {
type: 'token',
value: TOKEN_INFO_ERC_20,
},
},
},
],
},
};
47 changes: 47 additions & 0 deletions types/api/txInterpretation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { AddressParam } from 'types/api/addressParams';
import type { TokenInfo } from 'types/api/token';

export interface TxInterpretationResponse {
data: {
summaries: Array<TxInterpretationSummary>;
};
}

export type TxInterpretationSummary = {
summary_template: string;
summary_template_variables: Record<string, TxInterpretationVariable>;
}

export type TxInterpretationVariable =
TxInterpretationVariableString |
TxInterpretationVariableCurrency |
TxInterpretationVariableTimestamp |
TxInterpretationVariableToken |
TxInterpretationVariableAddress;

export type TxInterpretationVariableType = 'string' | 'currency' | 'timestamp' | 'token' | 'address';

export type TxInterpretationVariableString = {
type: 'string';
value: string;
}

export type TxInterpretationVariableCurrency = {
type: 'currency';
value: string;
}

export type TxInterpretationVariableTimestamp = {
type: 'timestamp';
value: string;
}

export type TxInterpretationVariableToken = {
type: 'token';
value: TokenInfo;
}

export type TxInterpretationVariableAddress = {
type: 'address';
value: AddressParam;
}
18 changes: 7 additions & 11 deletions ui/pages/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/contexts/app';
import getQueryParamString from 'lib/router/getQueryParamString';
import { TX } from 'stubs/tx';
import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu';
import TextAd from 'ui/shared/ad/TextAd';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
Expand All @@ -23,6 +20,7 @@ import TxInternals from 'ui/tx/TxInternals';
import TxLogs from 'ui/tx/TxLogs';
import TxRawTrace from 'ui/tx/TxRawTrace';
import TxState from 'ui/tx/TxState';
import TxSubHeading from 'ui/tx/TxSubHeading';
import TxTokenTransfer from 'ui/tx/TxTokenTransfer';

const TransactionPageContent = () => {
Expand All @@ -40,7 +38,11 @@ const TransactionPageContent = () => {
});

const tabs: Array<RoutedTab> = [
{ id: 'index', title: config.features.suave.isEnabled && data?.wrapped ? 'Confidential compute tx details' : 'Details', component: <TxDetails/> },
{
id: 'index',
title: config.features.suave.isEnabled && data?.wrapped ? 'Confidential compute tx details' : 'Details',
component: <TxDetails/>,
},
config.features.suave.isEnabled && data?.wrapped ?
{ id: 'wrapped', title: 'Regular tx details', component: <TxDetailsWrapped data={ data.wrapped }/> } :
undefined,
Expand Down Expand Up @@ -73,13 +75,7 @@ const TransactionPageContent = () => {
};
}, [ appProps.referrer ]);

const titleSecondRow = (
<>
<TxEntity hash={ hash } noLink noCopy={ false } fontWeight={ 500 } mr={ 2 } fontFamily="heading"/>
{ !data?.tx_tag && <AccountActionsMenu mr={{ base: 0, lg: 3 }}/> }
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 3, lg: 'auto' }}/>
</>
);
const titleSecondRow = <TxSubHeading hash={ hash } hasTag={ Boolean(data?.tx_tag) }/>;

return (
<>
Expand Down
11 changes: 2 additions & 9 deletions ui/tx/TxDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import TextSeparator from 'ui/shared/TextSeparator';
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import Utilization from 'ui/shared/Utilization/Utilization';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
import TxDetailsActions from 'ui/tx/details/TxDetailsActions';
import TxDetailsActions from 'ui/tx/details/txDetailsActions/TxDetailsActions';
import TxDetailsFeePerGas from 'ui/tx/details/TxDetailsFeePerGas';
import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice';
import TxDetailsOther from 'ui/tx/details/TxDetailsOther';
Expand Down Expand Up @@ -98,8 +98,6 @@ const TxDetails = () => {
...toAddress?.watchlist_names || [],
].map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>);

const actionsExist = data.actions && data.actions.length > 0;

const executionSuccessBadge = toAddress?.is_contract && data.result === 'success' ? (
<Tooltip label="Contract execution completed">
<chakra.span display="inline-flex" ml={ 2 } mr={ 1 }>
Expand Down Expand Up @@ -242,12 +240,7 @@ const TxDetails = () => {

<DetailsInfoItemDivider/>

{ actionsExist && (
<>
<TxDetailsActions actions={ data.actions }/>
<DetailsInfoItemDivider/>
</>
) }
<TxDetailsActions hash={ data.hash } actions={ data.actions } isTxDataLoading={ isPlaceholderData }/>

<DetailsInfoItem
title="From"
Expand Down
54 changes: 54 additions & 0 deletions ui/tx/TxSubHeading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Box, Flex, Link } from '@chakra-ui/react';
import React from 'react';

import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { TX_INTERPRETATION } from 'stubs/txInterpretation';
import AccountActionsMenu from 'ui/shared/AccountActionsMenu/AccountActionsMenu';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import { TX_ACTIONS_BLOCK_ID } from 'ui/tx/details/txDetailsActions/TxDetailsActionsWrapper';
import TxInterpretation from 'ui/tx/interpretation/TxInterpretation';

type Props = {
hash?: string;
hasTag: boolean;
}

const TxSubHeading = ({ hash, hasTag }: Props) => {
const hasInterpretationFeature = config.features.txInterpretation.isEnabled;

const txInterpretationQuery = useApiQuery('tx_interpretation', {
pathParams: { hash },
queryOptions: {
enabled: Boolean(hash) && hasInterpretationFeature,
placeholderData: TX_INTERPRETATION,
},
});

const hasInterpretation = hasInterpretationFeature &&
(txInterpretationQuery.isPlaceholderData || txInterpretationQuery.data?.data.summaries.length);

return (
<Box display={{ base: 'block', lg: 'flex' }} alignItems="center" w="100%">
{ hasInterpretationFeature && (
<Flex mr={{ base: 0, lg: 6 }} flexWrap="wrap" alignItems="center">
<TxInterpretation
summary={ txInterpretationQuery.data?.data.summaries[0] }
isLoading={ txInterpretationQuery.isPlaceholderData }
fontSize="lg"
/>
{ !txInterpretationQuery.isPlaceholderData && txInterpretationQuery.data?.data.summaries && txInterpretationQuery.data?.data.summaries.length > 1 &&
<Link ml={ 3 } href={ `#${ TX_ACTIONS_BLOCK_ID }` }>all actions</Link> }
</Flex>
) }
{ !hasInterpretation && <TxEntity hash={ hash } noLink noCopy={ false } fontWeight={ 500 } mr={{ base: 0, lg: 2 }} fontFamily="heading"/> }
<Flex alignItems="center" justifyContent={{ base: 'start', lg: 'space-between' }} flexGrow={ 1 }>
{ !hasTag && <AccountActionsMenu mr={ 3 } mt={{ base: 3, lg: 0 }}/> }
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 0, lg: 'auto' }} mt={{ base: 3, lg: 0 }}/>
</Flex>
</Box>
);
};

export default TxSubHeading;
File renamed without changes.
Loading

0 comments on commit 6bccd6c

Please sign in to comment.