Commit 97c1a9f3 authored by isstuev's avatar isstuev

add tx actions and change logics

parent 9306b500
...@@ -63,28 +63,53 @@ export const TX_RAW_TRACE: RawTracesResponse = []; ...@@ -63,28 +63,53 @@ export const TX_RAW_TRACE: RawTracesResponse = [];
export const TX_INTERPRETATION: TxInterpretationResponse = { export const TX_INTERPRETATION: TxInterpretationResponse = {
data: { data: {
summaries: [ { summaries: [
summary_template: '{action_type} {source_amount} Ether into {destination_amount} {destination_token}', {
summary_template_variables: { summary_template: '{action_type} {source_amount} Ether into {destination_amount} {destination_token}',
action_type: { type: 'string', value: 'Wrap' }, summary_template_variables: {
source_amount: { type: 'currency', value: '0.7' }, action_type: { type: 'string', value: 'Wrap' },
destination_amount: { type: 'currency', value: '0.7' }, source_amount: { type: 'currency', value: '0.7' },
destination_token: { destination_amount: { type: 'currency', value: '0.7' },
type: 'token', destination_token: {
value: { type: 'token',
name: 'Museion', value: {
type: 'ERC-20', name: 'Museion',
symbol: 'MUSA', type: 'ERC-20',
address: '0x486a3c5f34cDc4EF133f248f1C81168D78da52e8', symbol: 'MUSA',
holders: '1152', address: '0x486a3c5f34cDc4EF133f248f1C81168D78da52e8',
decimals: '18', holders: '1152',
icon_url: null, decimals: '18',
total_supply: '210000000000000000000000000', icon_url: null,
exchange_rate: null, total_supply: '210000000000000000000000000',
circulating_market_cap: null, exchange_rate: null,
circulating_market_cap: null,
},
}, },
}, },
}, },
} ], {
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: {
name: 'Museion',
type: 'ERC-20',
symbol: 'MUSA',
address: '0x486a3c5f34cDc4EF133f248f1C81168D78da52e8',
holders: '1152',
decimals: '18',
icon_url: null,
total_supply: '210000000000000000000000000',
exchange_rate: null,
circulating_market_cap: null,
},
},
},
},
],
}, },
}; };
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex, Link } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -18,8 +18,8 @@ import PageTitle from 'ui/shared/Page/PageTitle'; ...@@ -18,8 +18,8 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton'; import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery'; import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery';
import { TX_ACTIONS_BLOCK_ID } from 'ui/tx/details/txDetailsActions/TxDetailsActionsWrapper';
import TxInterpretation from 'ui/tx/interpretation/TxInterpretation'; import TxInterpretation from 'ui/tx/interpretation/TxInterpretation';
import { checkTemplate as checkInterpretationTemplate } from 'ui/tx/interpretation/utils';
import TxDetails from 'ui/tx/TxDetails'; import TxDetails from 'ui/tx/TxDetails';
import TxDetailsWrapped from 'ui/tx/TxDetailsWrapped'; import TxDetailsWrapped from 'ui/tx/TxDetailsWrapped';
import TxInternals from 'ui/tx/TxInternals'; import TxInternals from 'ui/tx/TxInternals';
...@@ -53,7 +53,11 @@ const TransactionPageContent = () => { ...@@ -53,7 +53,11 @@ const TransactionPageContent = () => {
}); });
const tabs: Array<RoutedTab> = [ 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 txInterpretationQuery={ txInterpretationQuery }/>,
},
config.features.suave.isEnabled && data?.wrapped ? config.features.suave.isEnabled && data?.wrapped ?
{ id: 'wrapped', title: 'Regular tx details', component: <TxDetailsWrapped data={ data.wrapped }/> } : { id: 'wrapped', title: 'Regular tx details', component: <TxDetailsWrapped data={ data.wrapped }/> } :
undefined, undefined,
...@@ -86,13 +90,21 @@ const TransactionPageContent = () => { ...@@ -86,13 +90,21 @@ const TransactionPageContent = () => {
}; };
}, [ appProps.referrer ]); }, [ appProps.referrer ]);
const hasInterpretation = const hasInterpretation = hasInterpretationFeature &&
hasInterpretationFeature && (txInterpretationQuery.isPlaceholderData || (txInterpretationQuery.isPlaceholderData || txInterpretationQuery.data?.data.summaries.length);
(txInterpretationQuery.data?.data.summaries[0] && checkInterpretationTemplate(txInterpretationQuery.data.data.summaries[0])));
const titleSecondRow = ( const titleSecondRow = (
<Box display={{ base: 'block', lg: 'flex' }} alignItems="center" w="100%"> <Box display={{ base: 'block', lg: 'flex' }} alignItems="center" w="100%">
{ hasInterpretationFeature && <TxInterpretation query={ txInterpretationQuery } mr={{ base: 0, lg: 6 }}/> } { hasInterpretationFeature && (
<Flex mr={{ base: 0, lg: 6 }} flexWrap="wrap">
<TxInterpretation
summary={ txInterpretationQuery.data?.data.summaries[0] }
isLoading={ txInterpretationQuery.isPlaceholderData }
/>
{ !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"/> } { !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 }> <Flex alignItems="center" justifyContent={{ base: 'start', lg: 'space-between' }} flexGrow={ 1 }>
{ !data?.tx_tag && <AccountActionsMenu mr={ 3 } mt={{ base: 3, lg: 0 }}/> } { !data?.tx_tag && <AccountActionsMenu mr={ 3 } mt={{ base: 3, lg: 0 }}/> }
......
...@@ -12,15 +12,18 @@ import { ...@@ -12,15 +12,18 @@ import {
Skeleton, Skeleton,
Alert, Alert,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction'; import { ZKEVM_L2_TX_STATUSES } from 'types/api/transaction';
import type { TxInterpretationResponse } from 'types/api/txInterpretation';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import { WEI, WEI_IN_GWEI } from 'lib/consts'; import { WEI, WEI_IN_GWEI } from 'lib/consts';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
...@@ -44,7 +47,8 @@ import TextSeparator from 'ui/shared/TextSeparator'; ...@@ -44,7 +47,8 @@ import TextSeparator from 'ui/shared/TextSeparator';
import TxFeeStability from 'ui/shared/tx/TxFeeStability'; import TxFeeStability from 'ui/shared/tx/TxFeeStability';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps'; import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
import TxDetailsActions from 'ui/tx/details/TxDetailsActions'; import TxDetailsActionsFallback from 'ui/tx/details/txDetailsActions/TxDetailsActionsFallback';
import TxDetailsActionsInterpretation from 'ui/tx/details/txDetailsActions/TxDetailsActionsInterpretation';
import TxDetailsFeePerGas from 'ui/tx/details/TxDetailsFeePerGas'; import TxDetailsFeePerGas from 'ui/tx/details/TxDetailsFeePerGas';
import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice'; import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice';
import TxDetailsOther from 'ui/tx/details/TxDetailsOther'; import TxDetailsOther from 'ui/tx/details/TxDetailsOther';
...@@ -55,7 +59,11 @@ import TxAllowedPeekers from 'ui/tx/TxAllowedPeekers'; ...@@ -55,7 +59,11 @@ import TxAllowedPeekers from 'ui/tx/TxAllowedPeekers';
import TxSocketAlert from 'ui/tx/TxSocketAlert'; import TxSocketAlert from 'ui/tx/TxSocketAlert';
import useFetchTxInfo from 'ui/tx/useFetchTxInfo'; import useFetchTxInfo from 'ui/tx/useFetchTxInfo';
const TxDetails = () => { type Props = {
txInterpretationQuery?: UseQueryResult<TxInterpretationResponse, ResourceError>;
}
const TxDetails = ({ txInterpretationQuery }: Props) => {
const { data, isPlaceholderData, isError, socketStatus, error } = useFetchTxInfo(); const { data, isPlaceholderData, isError, socketStatus, error } = useFetchTxInfo();
const [ isExpanded, setIsExpanded ] = React.useState(false); const [ isExpanded, setIsExpanded ] = React.useState(false);
...@@ -98,8 +106,6 @@ const TxDetails = () => { ...@@ -98,8 +106,6 @@ const TxDetails = () => {
...toAddress?.watchlist_names || [], ...toAddress?.watchlist_names || [],
].map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>); ].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' ? ( 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 }>
...@@ -242,9 +248,22 @@ const TxDetails = () => { ...@@ -242,9 +248,22 @@ const TxDetails = () => {
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
{ actionsExist && ( { config.features.txInterpretation.isEnabled && txInterpretationQuery !== undefined && (
<>
<TxDetailsActionsInterpretation
actions={ txInterpretationQuery.data?.data.summaries }
isLoading={ isPlaceholderData || txInterpretationQuery.isPlaceholderData }
/>
{ (isPlaceholderData ||
txInterpretationQuery.isPlaceholderData ||
(txInterpretationQuery.data?.data.summaries && txInterpretationQuery.data?.data.summaries.length > 1)) &&
<DetailsInfoItemDivider/> }
</>
) }
{ /* if tx interpretation is not configured, show tx actions from tx info */ }
{ !config.features.txInterpretation.isEnabled && data.actions && data.actions.length > 0 && (
<> <>
<TxDetailsActions actions={ data.actions }/> <TxDetailsActionsFallback actions={ data.actions } isLoading={ isPlaceholderData }/>
<DetailsInfoItemDivider/> <DetailsInfoItemDivider/>
</> </>
) } ) }
......
import React from 'react';
import type { TxAction } from 'types/api/txAction';
import TxDetailsAction from './TxDetailsAction';
import TxDetailsActionsWrapper from './TxDetailsActionsWrapper';
interface Props {
actions: Array<TxAction>;
isLoading: boolean;
}
const TxDetailsActionsFallback = ({ actions, isLoading }: Props) => {
return (
<TxDetailsActionsWrapper isLoading={ isLoading }>
{ actions.map((action, index: number) => <TxDetailsAction key={ index } action={ action }/>) }
</TxDetailsActionsWrapper>
);
};
export default TxDetailsActionsFallback;
import React from 'react';
import type { TxInterpretationSummary } from 'types/api/txInterpretation';
import TxInterpretation from 'ui/tx/interpretation/TxInterpretation';
import TxDetailsActionsWrapper from './TxDetailsActionsWrapper';
interface Props {
actions?: Array<TxInterpretationSummary>;
isLoading: boolean;
}
const TxDetailsActionsInterpretation = ({ isLoading, actions }: Props) => {
if (!actions || actions.length < 2) {
return null;
}
return (
<TxDetailsActionsWrapper isLoading={ isLoading }>
{ actions.map((action, index: number) => <TxInterpretation key={ index } summary={ action } isLoading={ isLoading }/>) }
</TxDetailsActionsWrapper>
);
};
export default TxDetailsActionsInterpretation;
import { Flex, useColorModeValue } from '@chakra-ui/react'; import { Flex, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TxAction } from 'types/api/txAction';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TxDetailsAction from './TxDetailsAction';
const SCROLL_GRADIENT_HEIGHT = 48; const SCROLL_GRADIENT_HEIGHT = 48;
interface Props { type Props = {
actions: Array<TxAction>; children: React.ReactNode;
isLoading?: boolean;
} }
const TxDetailsActions = ({ actions }: Props) => { export const TX_ACTIONS_BLOCK_ID = 'tx-actions';
const TxDetailsActions = ({ children, isLoading }: Props) => {
const containerRef = React.useRef<HTMLDivElement>(null); const containerRef = React.useRef<HTMLDivElement>(null);
const [ hasScroll, setHasScroll ] = React.useState(false); const [ hasScroll, setHasScroll ] = React.useState(false);
...@@ -34,8 +33,10 @@ const TxDetailsActions = ({ actions }: Props) => { ...@@ -34,8 +33,10 @@ const TxDetailsActions = ({ actions }: Props) => {
hint="Highlighted events of the transaction" hint="Highlighted events of the transaction"
note={ hasScroll ? 'Scroll to see more' : undefined } note={ hasScroll ? 'Scroll to see more' : undefined }
position="relative" position="relative"
isLoading={ isLoading }
> >
<Flex <Flex
id="tx-actions"
flexDirection="column" flexDirection="column"
alignItems="stretch" alignItems="stretch"
rowGap={ 5 } rowGap={ 5 }
...@@ -55,7 +56,7 @@ const TxDetailsActions = ({ actions }: Props) => { ...@@ -55,7 +56,7 @@ const TxDetailsActions = ({ actions }: Props) => {
pr={ hasScroll ? 5 : 0 } pr={ hasScroll ? 5 : 0 }
pb={ hasScroll ? 10 : 0 } pb={ hasScroll ? 10 : 0 }
> >
{ actions.map((action, index: number) => <TxDetailsAction key={ index } action={ action }/>) } { children }
</Flex> </Flex>
</DetailsInfoItem> </DetailsInfoItem>
); );
......
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 { TxInterpretationResponse } from 'types/api/txInterpretation';
import type { ResourceError } from 'lib/api/resources';
import { txInterpretation as txInterpretationMock } from 'mocks/txs/txInterpretation'; import { txInterpretation as txInterpretationMock } from 'mocks/txs/txInterpretation';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
...@@ -13,7 +9,7 @@ import TxInterpretation from './TxInterpretation'; ...@@ -13,7 +9,7 @@ import TxInterpretation from './TxInterpretation';
test('base view +@mobile +@dark-mode', async({ mount }) => { test('base view +@mobile +@dark-mode', async({ mount }) => {
const component = await mount( const component = await mount(
<TestApp> <TestApp>
<TxInterpretation query={{ data: txInterpretationMock } as UseQueryResult<TxInterpretationResponse, ResourceError>}/> <TxInterpretation summary={ txInterpretationMock.data.summaries[0] } isLoading={ false }/>
</TestApp>, </TestApp>,
); );
......
import { Skeleton, Text, Icon, chakra } from '@chakra-ui/react'; import { Skeleton, Flex, Text, Icon, chakra } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import type { TxInterpretationResponse, TxInterpretationVariable } from 'types/api/txInterpretation'; import type { TxInterpretationSummary, TxInterpretationVariable } from 'types/api/txInterpretation';
import config from 'configs/app'; import config from 'configs/app';
import actionIcon from 'icons/action.svg'; import actionIcon from 'icons/action.svg';
import type { ResourceError } from 'lib/api/resources';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
...@@ -15,53 +13,68 @@ import TokenEntity from 'ui/shared/entities/token/TokenEntity'; ...@@ -15,53 +13,68 @@ import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import { extractVariables, getStringChunks, NATIVE_COIN_SYMBOL_VAR_NAME } from './utils'; import { extractVariables, getStringChunks, NATIVE_COIN_SYMBOL_VAR_NAME } from './utils';
type Props = { type Props = {
query: UseQueryResult<TxInterpretationResponse, ResourceError>; summary?: TxInterpretationSummary;
isLoading?: boolean;
className?: string; className?: string;
} }
const TxInterpretationElementByType = ({ type, value }: TxInterpretationVariable) => { const TxInterpretationElementByType = ({ variable }: { variable?: TxInterpretationVariable }) => {
if (!variable) {
return null;
}
const { type, value } = variable;
switch (type) { switch (type) {
case 'address': case 'address':
return <AddressEntity address={ value } truncation="constant" sx={{ ':not(:first-child)': { marginLeft: 1 } }}/>; return <AddressEntity address={ value } truncation="constant" sx={{ ':not(:first-child)': { marginLeft: 1 } }}/>;
case 'token': case 'token':
return <TokenEntity token={ value } onlySymbol width="fit-content" sx={{ ':not(:first-child)': { marginLeft: 1 } }}/>; return <TokenEntity token={ value } onlySymbol width="fit-content" sx={{ ':not(:first-child)': { marginLeft: 1 } }}/>;
case 'currency': case 'currency': {
return <Text>{ BigNumber(value).toFormat() }</Text>; let numberString = '';
if (BigNumber(value).isLessThan(0.1)) {
numberString = BigNumber(value).toPrecision(2);
} else if (BigNumber(value).isLessThan(10000)) {
numberString = BigNumber(value).dp(2).toFormat();
} else {
numberString = BigNumber(value).dividedBy(1000).toFormat(2) + 'K';
}
return <Text>{ numberString + ' ' }</Text>;
}
case 'timestamp': case 'timestamp':
// timestamp is in unix format // timestamp is in unix format
return <Text>{ dayjs(Number(value) * 1000).format('llll') }</Text>; return <Text>{ dayjs(Number(value) * 1000).format('llll') + ' ' }</Text>;
case 'string': case 'string':
default: { default: {
return <Text>{ value.toString() }</Text>; return <Text>{ value.toString() + ' ' }</Text>;
} }
} }
}; };
const TxInterpretation = ({ query, className }: Props) => { const TxInterpretation = ({ summary, isLoading, className }: Props) => {
if (!query.data?.data.summaries[0]) { if (!summary) {
return null; return null;
} }
const template = query.data.data.summaries[0].summary_template; const template = summary.summary_template;
const variables = query.data.data.summaries[0].summary_template_variables; const variables = summary.summary_template_variables;
const variablesNames = extractVariables(template); const variablesNames = extractVariables(template);
const chunks = getStringChunks(template); const chunks = getStringChunks(template);
return ( return (
<Skeleton display="flex" flexWrap="wrap" alignItems="center" isLoaded={ !query.isPlaceholderData } className={ className }> <Skeleton display="flex" flexWrap="wrap" alignItems="center" isLoaded={ !isLoading } className={ className }>
<Icon as={ actionIcon } boxSize={ 5 } color="text_secondary" mr={ 2 }/> <Icon as={ actionIcon } boxSize={ 5 } color="text_secondary" mr={ 2 }/>
{ chunks.map((chunk, index) => { { chunks.map((chunk, index) => {
return ( return (
<> <Flex whiteSpace="pre" key={ chunk + index }>
<Text whiteSpace="pre">{ chunk.trim() + ' ' }</Text> <Text>{ chunk.trim() + (chunk.trim() && variablesNames[index] ? ' ' : '') }</Text>
{ index < chunks.length - 1 && ( { index < variablesNames.length && (
variablesNames[index] === NATIVE_COIN_SYMBOL_VAR_NAME ? variablesNames[index] === NATIVE_COIN_SYMBOL_VAR_NAME ?
<Text>{ config.chain.currency.symbol }</Text> : <Text>{ config.chain.currency.symbol + ' ' }</Text> :
<TxInterpretationElementByType { ...variables[variablesNames[index]] }/> <TxInterpretationElementByType variable={ variables[variablesNames[index]] }/>
) } ) }
</> </Flex>
); );
}) } }) }
</Skeleton> </Skeleton>
......
import { extractVariables, checkTemplate } from './utils'; import { extractVariables, getStringChunks } from './utils';
const template = '{action_type} {source_amount} {native} into {destination_amount} {destination_token}'; const template = '{action_type} {source_amount} {native} into {destination_amount} {destination_token}';
...@@ -7,37 +7,7 @@ it('extracts variables names', () => { ...@@ -7,37 +7,7 @@ it('extracts variables names', () => {
expect(result).toEqual([ 'action_type', 'source_amount', 'native', 'destination_amount', 'destination_token' ]); expect(result).toEqual([ 'action_type', 'source_amount', 'native', 'destination_amount', 'destination_token' ]);
}); });
it('check template true', () => { it('split string without capturing variables', () => {
const variables = { const result = getStringChunks(template);
action_type: { type: 'string' as const, value: 'Wrap' }, expect(result).toEqual([ '', ' ', ' ', ' into ', ' ', '' ]);
source_amount: { type: 'currency' as const, value: '0.7' },
destination_amount: { type: 'currency' as const, value: '0.7' },
destination_token: {
type: 'token' as const,
value: {
name: 'Duck',
type: 'ERC-20' as const,
symbol: 'DUCK',
address: '0x486a3c5f34cDc4EF133f248f1C81168D78da52e8',
holders: '1152',
decimals: '18',
icon_url: null,
total_supply: '210000000000000000000000000',
exchange_rate: null,
circulating_market_cap: null,
},
},
};
const result = checkTemplate({ summary_template: template, summary_template_variables: variables });
expect(result).toBe(true);
});
it('check template false', () => {
const variables = {
action_type: { type: 'string' as const, value: 'Wrap' },
source_amount: { type: 'currency' as const, value: '0.7' },
destination_amount: { type: 'currency' as const, value: '0.7' },
};
const result = checkTemplate({ summary_template: template, summary_template_variables: variables });
expect(result).toBe(false);
}); });
import type { TxInterpretationSummary } from 'types/api/txInterpretation';
// we use that regex as a separator when splitting template and dont want to capture variables // we use that regex as a separator when splitting template and dont want to capture variables
// eslint-disable-next-line regexp/no-useless-non-capturing-group // eslint-disable-next-line regexp/no-useless-non-capturing-group
export const VAR_REGEXP = /\{(?:[^}]+)\}/g; export const VAR_REGEXP = /\{(?:[^}]+)\}/g;
...@@ -18,16 +16,3 @@ export function extractVariables(templateString: string) { ...@@ -18,16 +16,3 @@ export function extractVariables(templateString: string) {
export function getStringChunks(template: string) { export function getStringChunks(template: string) {
return template.split(VAR_REGEXP); return template.split(VAR_REGEXP);
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function checkTemplate(summary: TxInterpretationSummary) {
const variablesNames = extractVariables(summary.summary_template);
for (const name of variablesNames) {
if (name !== NATIVE_COIN_SYMBOL_VAR_NAME && !summary.summary_template_variables[name]) {
return false;
}
}
return true;
}
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