Commit 38e22ce6 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #568 from blockscout/token-symbol-fixes

token symbol fixes
parents 5cc749a9 217505c2
// some tokens could have symbols like "ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY"
// so in some cases we trim it to max 10 symbols
export default function trimTokenSymbol(symbol: string | null) {
if (!symbol) {
return '';
}
if (symbol.length <= 7) {
return symbol;
}
return symbol.slice(0, 7) + '...';
}
......@@ -26,6 +26,12 @@ export const erc20d: AddressTokenBalance = {
value: '39000000000000000000',
};
export const erc20LongSymbol: AddressTokenBalance = {
token: tokens.tokenInfoERC20LongSymbol,
token_id: null,
value: '39000000000000000000',
};
export const erc721a: AddressTokenBalance = {
token: tokens.tokenInfoERC721a,
token_id: null,
......@@ -44,6 +50,12 @@ export const erc721c: AddressTokenBalance = {
value: '5',
};
export const erc721LongSymbol: AddressTokenBalance = {
token: tokens.tokenInfoERC721LongSymbol,
token_id: null,
value: '5',
};
export const erc1155a: AddressTokenBalance = {
token: tokens.tokenInfoERC1155a,
token_id: '42',
......@@ -62,6 +74,12 @@ export const erc1155withoutName: AddressTokenBalance = {
value: '42',
};
export const erc1155LongId: AddressTokenBalance = {
token: tokens.tokenInfoERC1155b,
token_id: '483200961027732618117991942553110860267520',
value: '42',
};
export const baseList = [
erc20a,
erc20b,
......@@ -73,3 +91,9 @@ export const baseList = [
erc1155a,
erc1155b,
];
export const longValuesList = [
erc20LongSymbol,
erc721LongSymbol,
erc1155LongId,
];
......@@ -60,6 +60,17 @@ export const tokenInfoERC20d: TokenInfo = {
type: 'ERC-20',
};
export const tokenInfoERC20LongSymbol: TokenInfo = {
address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195',
decimals: '18',
exchange_rate: '1328.89',
holders: '102625',
name: 'Zeta',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
total_supply: '2100000000000000000000000000',
type: 'ERC-20',
};
export const tokenInfoERC721a: TokenInfo = {
address: '0xDe7cAc71E072FCBd4453E5FB3558C2684d1F88A0',
decimals: null,
......@@ -93,6 +104,17 @@ export const tokenInfoERC721c: TokenInfo = {
type: 'ERC-721',
};
export const tokenInfoERC721LongSymbol: TokenInfo = {
address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992',
decimals: null,
exchange_rate: null,
holders: '12',
name: 'Puma',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
total_supply: null,
type: 'ERC-721',
};
export const tokenInfoERC1155a: TokenInfo = {
address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e',
decimals: null,
......
......@@ -132,12 +132,13 @@ export const erc1155multiple: TokenTransfer = {
...erc1155,
token: {
...erc1155.token,
name: 'OLYMPIC',
name: 'SastanaNFT',
symbol: 'ipfs://QmUpFUfVKDCWeZQk5pvDFUxnpQP9N6eLSHhNUy49T1JVtY',
},
total: [
{ token_id: '12345678', value: '100000000000000000000', decimals: null },
{ token_id: '483200961027732618117991942553110860267520', value: '200000000000000000000', decimals: null },
{ token_id: '456', value: '42', decimals: null },
{ token_id: '12345678', value: '142', decimals: null },
{ token_id: '1000006457499', value: '11', decimals: null },
],
};
......
......@@ -3,6 +3,7 @@ import React from 'react';
import type { Address } from 'types/api/address';
import link from 'lib/link/link';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import LinkInternal from 'ui/shared/LinkInternal';
......@@ -12,13 +13,14 @@ interface Props {
const AddressNameInfo = ({ data }: Props) => {
if (data.token) {
const symbol = data.token.symbol ? ` (${ trimTokenSymbol(data.token.symbol) })` : '';
return (
<DetailsInfoItem
title="Token name"
hint="Token name and symbol"
>
<LinkInternal href={ link('token_index', { hash: data.token.address }) }>
{ data.token.name } ({ data.token.symbol })
{ data.token.name }{ symbol }
</LinkInternal>
</DetailsInfoItem>
);
......
......@@ -126,6 +126,37 @@ test('filter', async({ mount, page }) => {
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
});
base('long values', async({ mount, page }) => {
await page.route(ASSET_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
await page.route(ADDRESS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify({ hash: '1' }),
}), { times: 1 });
await page.route(TOKENS_API_URL, async(route) => route.fulfill({
status: 200,
body: JSON.stringify(tokenBalanceMock.longValuesList),
}), { times: 1 });
await mount(
<TestApp>
<MockAddressPage>
<Flex>
<TokenSelect/>
</Flex>
</MockAddressPage>
</TestApp>,
{ hooksConfig },
);
await page.getByRole('button', { name: /select/i }).click();
await expect(page).toHaveScreenshot({ clip: CLIPPING_AREA });
});
test.describe('socket', () => {
const testWithSocket = test.extend<socketServer.SocketServerFixture>({
createSocket: socketServer.createSocket,
......
import { Flex, Text, useColorModeValue } from '@chakra-ui/react';
import { chakra, Flex, Text, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
import link from 'lib/link/link';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import HashStringShorten from 'ui/shared/HashStringShorten';
import TokenLogo from 'ui/shared/TokenLogo';
......@@ -20,19 +21,23 @@ const TokenSelectItem = ({ data }: Props) => {
const tokenDecimals = Number(data.token.decimals) || 18;
return (
<>
<Text >{ BigNumber(data.value).dividedBy(10 ** tokenDecimals).toFormat(2) } { data.token.symbol }</Text>
{ data.token.exchange_rate && <Text >@{ data.token.exchange_rate }</Text> }
<span >{ BigNumber(data.value).dividedBy(10 ** tokenDecimals).toFormat(2) } { trimTokenSymbol(data.token.symbol) }</span>
{ data.token.exchange_rate && <span >@{ data.token.exchange_rate }</span> }
</>
);
}
case 'ERC-721': {
return <Text >{ BigNumber(data.value).toFormat() } { data.token.symbol }</Text>;
return <chakra.span textOverflow="ellipsis" overflow="hidden">{ BigNumber(data.value).toFormat() } { data.token.symbol }</chakra.span>;
}
case 'ERC-1155': {
return (
<>
<Text >#{ data.token_id || 0 }</Text>
<Text >{ BigNumber(data.value).toFormat() }</Text>
<chakra.span textOverflow="ellipsis" overflow="hidden" mr={ 6 }>
#{ data.token_id || 0 }
</chakra.span>
<span>
{ BigNumber(data.value).toFormat() }
</span>
</>
);
}
......@@ -64,7 +69,7 @@ const TokenSelectItem = ({ data }: Props) => {
<Text fontWeight={ 700 } ml={ 2 }>{ data.token.name || <HashStringShorten hash={ data.token.address }/> }</Text>
{ data.usd && <Text fontWeight={ 700 } ml="auto">${ data.usd.toFormat(2) }</Text> }
</Flex>
<Flex alignItems="center" justifyContent="space-between" w="100%">
<Flex alignItems="center" justifyContent="space-between" w="100%" whiteSpace="nowrap">
{ secondRow }
</Flex>
</Flex>
......
......@@ -8,6 +8,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import AdBanner from 'ui/shared/ad/AdBanner';
import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page';
......@@ -40,7 +41,8 @@ const TokenPageContent = () => {
useEffect(() => {
if (tokenQuery.data) {
const tokenName = `${ tokenQuery.data.name } (${ tokenQuery.data.symbol })`;
const tokenSymbol = tokenQuery.data.symbol ? ` (${ tokenQuery.data.symbol })` : '';
const tokenName = `${ tokenQuery.data.name || 'Unnamed' }${ tokenSymbol }`;
const title = document.getElementsByTagName('title')[0];
if (title) {
title.textContent = title.textContent?.replace(tokenQuery.data.address, tokenName) || title.textContent;
......@@ -88,6 +90,8 @@ const TokenPageContent = () => {
pagination = holdersQuery.pagination;
}
const tokenSymbolText = tokenQuery.data?.symbol ? ` (${ trimTokenSymbol(tokenQuery.data.symbol) })` : '';
return (
<Page>
{ tokenQuery.isLoading ? (
......@@ -99,7 +103,7 @@ const TokenPageContent = () => {
<>
<TextAd mb={ 6 }/>
<PageTitle
text={ `${ tokenQuery.data?.name } (${ tokenQuery.data?.symbol }) token` }
text={ `${ tokenQuery.data?.name || 'Unnamed' }${ tokenSymbolText } token` }
backLinkUrl={ hasGoBackLink ? appProps.referrer : undefined }
backLinkLabel="Back to tokens list"
additionalsLeft={ (
......
......@@ -7,6 +7,7 @@ import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg';
import highlightText from 'lib/highlightText';
import link from 'lib/link/link';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -25,7 +26,7 @@ const SearchResultListItem = ({ data, searchTerm }: Props) => {
const firstRow = (() => {
switch (data.type) {
case 'token': {
const name = data.name + (data.symbol ? ` (${ data.symbol })` : '');
const name = data.name + (data.symbol ? ` (${ trimTokenSymbol(data.symbol) })` : '');
return (
<Flex alignItems="flex-start">
......
......@@ -7,6 +7,7 @@ import blockIcon from 'icons/block.svg';
import txIcon from 'icons/transactions.svg';
import highlightText from 'lib/highlightText';
import link from 'lib/link/link';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -24,7 +25,7 @@ const SearchResultTableItem = ({ data, searchTerm }: Props) => {
const content = (() => {
switch (data.type) {
case 'token': {
const name = data.name + (data.symbol ? ` (${ data.symbol })` : '');
const name = data.name + (data.symbol ? ` (${ trimTokenSymbol(data.symbol) })` : '');
return (
<>
<Td fontSize="sm">
......
......@@ -103,15 +103,15 @@ const TokenDetails = ({ tokenQuery }: Props) => {
{ `$${ totalValue?.usd }` }
</DetailsInfoItem>
) }
{ totalValue?.valueStr && (
<DetailsInfoItem
title="Max total supply"
hint="The total amount of tokens issued."
alignSelf="center"
>
{ `${ totalValue.valueStr } ${ symbol }` }
</DetailsInfoItem>
) }
<DetailsInfoItem
title="Max total supply"
hint="The total amount of tokens issued."
alignSelf="center"
wordBreak="break-word"
whiteSpace="pre-wrap"
>
{ `${ totalValue?.valueStr || 0 } ${ symbol || '' }` }
</DetailsInfoItem>
<DetailsInfoItem
title="Holders"
hint="Number of accounts holding the token."
......
......@@ -63,7 +63,7 @@ test('erc1155 +@mobile', async({ mount }) => {
<TestApp>
<Box h={{ base: '134px', lg: '100px' }}/>
<TokenTransfer
token={{ ...tokenInfo, type: 'ERC-1155' }}
token={{ ...tokenInfo, type: 'ERC-1155', symbol: tokenTransferMock.erc1155multiple.token.symbol }}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore:
transfersQuery={{
......
......@@ -7,6 +7,7 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
......@@ -69,14 +70,15 @@ const TokenTransferListItem = ({
<AddressLink ml={ 2 } fontWeight="500" hash={ to.hash } type="address_token" tokenHash={ token.address }/>
</Address>
</Flex>
{ value && (
{ value && (token.type === 'ERC-20' || token.type === 'ERC-1155') && (
<Flex columnGap={ 2 } w="100%">
<Text fontWeight={ 500 } flexShrink={ 0 }>Value</Text>
<Text variant="secondary">{ value }</Text>
<Text>{ token.symbol }</Text>
<Text>{ trimTokenSymbol(token.symbol) }</Text>
</Flex>
) }
{ 'token_id' in total && <TokenTransferNft hash={ token.address } id={ total.token_id }/> }
{ 'token_id' in total && (token.type === 'ERC-721' || token.type === 'ERC-1155') &&
<TokenTransferNft hash={ token.address } id={ total.token_id }/> }
</ListItemMobile>
);
};
......
......@@ -4,6 +4,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/tokenInfo';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky';
import TokenTransferTableItem from 'ui/token/TokenTransfer/TokenTransferTableItem';
......@@ -28,7 +29,7 @@ const TokenTransferTable = ({ data, top, token, showSocketInfo, socketInfoAlert,
<Th width="36px" px={ 0 }/>
<Th width="218px" >To</Th>
{ (token.type === 'ERC-721' || token.type === 'ERC-1155') && <Th width="20%" isNumeric={ token.type === 'ERC-721' }>Token ID</Th> }
{ (token.type === 'ERC-20' || token.type === 'ERC-1155') && <Th width="20%" isNumeric>Value { token.symbol }</Th> }
{ (token.type === 'ERC-20' || token.type === 'ERC-1155') && <Th width="20%" isNumeric>Value { trimTokenSymbol(token.symbol) }</Th> }
</Tr>
</Thead>
<Tbody>
......
......@@ -4,6 +4,7 @@ import React from 'react';
import nftIcon from 'icons/nft_shield.svg';
import link from 'lib/link/link';
import AddressLink from 'ui/shared/address/AddressLink';
import HashStringShorten from 'ui/shared/HashStringShorten';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
interface Props {
......@@ -23,7 +24,9 @@ const NftTokenTransferSnippet = ({ value, name, hash, symbol, tokenId }: Props)
<Text fontWeight={ 500 } as="span">For { num } token ID:</Text>
<Box display="inline-flex" alignItems="center">
<Icon as={ nftIcon } boxSize={ 6 } mr={ 1 }/>
<Link href={ url } fontWeight={ 600 }>{ tokenId }</Link>
<Link href={ url } fontWeight={ 600 } overflow="hidden">
{ tokenId.length > 8 ? <HashStringShorten hash={ tokenId }/> : tokenId }
</Link>
</Box>
{ name ? (
<TokenSnippet symbol={ symbol } hash={ hash } name={ name } w="auto" logoSize={ 5 } columnGap={ 1 }/>
......
......@@ -5,6 +5,7 @@ import type { TokenTransfer as TTokenTransfer, Erc20TotalPayload, Erc721TotalPay
import rightArrowIcon from 'icons/arrows/east.svg';
import { space } from 'lib/html-entities';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
......@@ -26,7 +27,7 @@ const TxDetailsTokenTransfer = ({ token, total, to, from }: Props) => {
<CurrencyValue value={ payload.value } exchangeRate={ token.exchange_rate } fontWeight={ 600 }/>
</Text>
<TokenSnippet
symbol={ token.symbol }
symbol={ trimTokenSymbol(token.symbol) }
hash={ token.address }
name={ token.name }
w="auto"
......@@ -46,7 +47,7 @@ const TxDetailsTokenTransfer = ({ token, total, to, from }: Props) => {
tokenId={ payload.token_id }
value="1"
hash={ token.address }
symbol={ token.symbol }
symbol={ trimTokenSymbol(token.symbol) }
/>
);
}
......@@ -61,7 +62,7 @@ const TxDetailsTokenTransfer = ({ token, total, to, from }: Props) => {
tokenId={ item.token_id }
value={ item.value }
hash={ token.address }
symbol={ token.symbol }
symbol={ trimTokenSymbol(token.symbol) }
/>
));
}
......
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