Commit 077a79e6 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #1125 from blockscout/address-entity

refactor icons for addresses and tokens
parents 1bd65cf3 835c9a54
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.91 3H5.272C4.57 3 4 3.57 4 4.273v8.909h1.273v-8.91h7.636V3Zm1.908 2.545h-7c-.703 0-1.273.57-1.273 1.273v8.91c0 .702.57 1.272 1.273 1.272h7c.703 0 1.273-.57 1.273-1.273V6.818c0-.703-.57-1.273-1.273-1.273Zm0 10.182h-7V6.818h7v8.91Z"/> <g clip-path="url(#copy_svg__a)">
<path d="M8.597 3H1.228C.55 3 0 3.57 0 4.273v8.909h1.228v-8.91h7.369V3Zm1.842 2.545H3.684c-.678 0-1.228.57-1.228 1.273v8.91c0 .702.55 1.272 1.228 1.272h6.755c.678 0 1.228-.57 1.228-1.273V6.818c0-.703-.55-1.273-1.228-1.273Zm0 10.182H3.684V6.818h6.755v8.91Z" fill="currentColor" stroke="currentColor" stroke-width=".3"/>
</g>
<defs>
<clipPath id="copy_svg__a">
<path d="M0 0h20v20H0z"/>
</clipPath>
</defs>
</svg> </svg>
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"> <svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.61 25a2.262 2.262 0 0 1-1.615-.665l-6.318-6.318a2.265 2.265 0 0 1 0-3.23l8.456-8.457c.76-.76 2.138-1.33 3.183-1.33h5.416c1.235 0 2.28 1.045 2.28 2.28v5.416c0 1.045-.57 2.423-1.33 3.183l-8.456 8.456A2.262 2.262 0 0 1 13.61 25Zm3.706-18.575c-.665 0-1.71.428-2.185.903l-8.456 8.456a.832.832 0 0 0 0 1.187l6.318 6.319c.332.332.902.332 1.188 0l8.456-8.456c.475-.475.902-1.473.902-2.185V7.232a.852.852 0 0 0-.855-.855h-5.368v.047Z" stroke="currentColor" stroke-width=".5"/> <path d="M13.61 25a2.262 2.262 0 0 1-1.615-.665l-6.318-6.318a2.265 2.265 0 0 1 0-3.23l8.456-8.457c.76-.76 2.138-1.33 3.183-1.33h5.416c1.235 0 2.28 1.045 2.28 2.28v5.416c0 1.045-.57 2.423-1.33 3.183l-8.456 8.456A2.262 2.262 0 0 1 13.61 25Zm3.706-18.575c-.665 0-1.71.428-2.185.903l-8.456 8.456a.832.832 0 0 0 0 1.187l6.318 6.319c.332.332.902.332 1.188 0l8.456-8.456c.475-.475.902-1.473.902-2.185V7.232a.852.852 0 0 0-.855-.855h-5.368v.047Z" fill="currentColor" stroke="currentColor" stroke-width=".5"/>
<path d="M19.311 13.504a2.808 2.808 0 0 1-2.803-2.803 2.808 2.808 0 0 1 2.803-2.803 2.808 2.808 0 0 1 2.803 2.803 2.808 2.808 0 0 1-2.803 2.803Zm0-4.276a1.48 1.48 0 0 0-1.473 1.473 1.48 1.48 0 0 0 1.473 1.473 1.48 1.48 0 0 0 1.473-1.473 1.48 1.48 0 0 0-1.473-1.473Z" stroke="currentColor" stroke-width=".5"/> <path d="M19.311 13.504a2.808 2.808 0 0 1-2.803-2.803 2.808 2.808 0 0 1 2.803-2.803 2.808 2.808 0 0 1 2.803 2.803 2.808 2.808 0 0 1-2.803 2.803Zm0-4.276a1.48 1.48 0 0 0-1.473 1.473 1.48 1.48 0 0 0 1.473 1.473 1.48 1.48 0 0 0 1.473-1.473 1.48 1.48 0 0 0-1.473-1.473Z" fill="currentColor" stroke="currentColor" stroke-width=".5"/>
</svg> </svg>
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.094 19.3c-.58 0-1.116-.223-1.517-.625l-5.936-5.94a2.13 2.13 0 0 1 0-3.036L9.585 1.75c.714-.714 2.008-1.25 2.99-1.25h5.088c1.16 0 2.142.982 2.142 2.143v5.091c0 .983-.536 2.278-1.25 2.992l-7.944 7.949a2.125 2.125 0 0 1-1.517.625Zm3.481-17.46c-.625 0-1.607.402-2.053.848l-7.944 7.949a.782.782 0 0 0 0 1.116l5.936 5.94c.312.312.848.312 1.115 0l7.944-7.95c.447-.446.848-1.384.848-2.053V2.599a.8.8 0 0 0-.803-.804h-5.043v.045Z" fill="currentColor"/>
<path d="M14.45 8.493a2.639 2.639 0 0 1-2.634-2.634 2.639 2.639 0 0 1 2.633-2.635 2.639 2.639 0 0 1 2.634 2.635 2.639 2.639 0 0 1-2.633 2.634Zm0-4.019a1.39 1.39 0 0 0-1.384 1.385 1.39 1.39 0 0 0 1.383 1.384 1.39 1.39 0 0 0 1.384-1.384 1.39 1.39 0 0 0-1.383-1.385Z" fill="currentColor"/>
</svg>
...@@ -5,22 +5,18 @@ import React from 'react'; ...@@ -5,22 +5,18 @@ import React from 'react';
import type { Address as TAddress } from 'types/api/address'; import type { Address as TAddress } from 'types/api/address';
import { route } from 'nextjs-routes';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { ADDRESS_COUNTERS } from 'stubs/address'; import { ADDRESS_COUNTERS } from 'stubs/address';
import AddressCounterItem from 'ui/address/details/AddressCounterItem'; import AddressCounterItem from 'ui/address/details/AddressCounterItem';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressHeadingInfo from 'ui/shared/AddressHeadingInfo'; import AddressHeadingInfo from 'ui/shared/AddressHeadingInfo';
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';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkInternal from 'ui/shared/LinkInternal';
import AddressBalance from './details/AddressBalance'; import AddressBalance from './details/AddressBalance';
import AddressNameInfo from './details/AddressNameInfo'; import AddressNameInfo from './details/AddressNameInfo';
...@@ -102,9 +98,13 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -102,9 +98,13 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
hint="Transaction and address of creation" hint="Transaction and address of creation"
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
> >
<AddressLink type="address" hash={ data.creator_address_hash } truncation="constant"/> <AddressEntity
address={{ hash: data.creator_address_hash }}
truncation="constant"
noIcon
/>
<Text whiteSpace="pre"> at txn </Text> <Text whiteSpace="pre"> at txn </Text>
<TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon/> <TxEntity hash={ data.creation_tx_hash } truncation="constant" noIcon noCopy={ false }/>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
{ data.is_contract && data.implementation_address && ( { data.is_contract && data.implementation_address && (
...@@ -113,14 +113,11 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => { ...@@ -113,14 +113,11 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
hint="Implementation address of the proxy contract" hint="Implementation address of the proxy contract"
columnGap={ 1 } columnGap={ 1 }
> >
<LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: data.implementation_address } }) } overflow="hidden"> <AddressEntity
{ data.implementation_name || <HashStringShortenDynamic hash={ data.implementation_address }/> } address={{ hash: data.implementation_address, name: data.implementation_name, is_contract: true }}
</LinkInternal> isLoading={ addressQuery.isPlaceholderData }
{ data.implementation_name && ( noIcon
<Text variant="secondary" overflow="hidden"> />
<HashStringShortenDynamic hash={ `(${ data.implementation_address })` }/>
</Text>
) }
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
<AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/> <AddressBalance data={ data } isLoading={ addressQuery.isPlaceholderData }/>
......
...@@ -22,11 +22,11 @@ import TOKEN_TYPE from 'lib/token/tokenTypes'; ...@@ -22,11 +22,11 @@ import TOKEN_TYPE from 'lib/token/tokenTypes';
import { getTokenTransfersStub } from 'stubs/token'; import { getTokenTransfersStub } from 'stubs/token';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TokenLogo from 'ui/shared/TokenLogo';
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter'; import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList'; import TokenTransferList from 'ui/shared/TokenTransfer/TokenTransferList';
import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable'; import TokenTransferTable from 'ui/shared/TokenTransfer/TokenTransferTable';
...@@ -227,19 +227,20 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr ...@@ -227,19 +227,20 @@ const AddressTokenTransfers = ({ scrollRef, overloadCount = OVERLOAD_COUNT }: Pr
address: tokenFilter || '', address: tokenFilter || '',
name: '', name: '',
icon_url: '', icon_url: '',
symbol: '',
}), [ tokenFilter ]); }), [ tokenFilter ]);
const tokenFilterComponent = tokenFilter && ( const tokenFilterComponent = tokenFilter && (
<Flex alignItems="center" flexWrap="wrap" mb={{ base: isActionBarHidden ? 3 : 6, lg: 0 }} mr={ 4 }> <Flex alignItems="center" flexWrap="wrap" mb={{ base: isActionBarHidden ? 3 : 6, lg: 0 }} mr={ 4 }>
<Text whiteSpace="nowrap" mr={ 2 } py={ 1 }>Filtered by token</Text> <Text whiteSpace="nowrap" mr={ 2 } py={ 1 }>Filtered by token</Text>
<Flex alignItems="center" py={ 1 }> <Flex alignItems="center" py={ 1 }>
<TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/> <TokenEntity.Icon token={ tokenData } isLoading={ isPlaceholderData }/>
{ isMobile ? <HashStringShorten hash={ tokenFilter }/> : tokenFilter } { isMobile ? <HashStringShorten hash={ tokenFilter }/> : tokenFilter }
<Tooltip label="Reset filter"> <Tooltip label="Reset filter">
<Flex> <Flex>
<Icon <Icon
as={ crossIcon } as={ crossIcon }
boxSize={ 6 } boxSize={ 5 }
ml={ 1 } ml={ 1 }
color={ resetTokenIconColor } color={ resetTokenIconColor }
cursor="pointer" cursor="pointer"
......
...@@ -47,6 +47,7 @@ base.describe('base view', () => { ...@@ -47,6 +47,7 @@ base.describe('base view', () => {
base.use({ viewport: configs.viewport.xl }); base.use({ viewport: configs.viewport.xl });
base('', async() => { base('', async() => {
base.slow();
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
}); });
......
...@@ -12,10 +12,8 @@ import dayjs from 'lib/date/dayjs'; ...@@ -12,10 +12,8 @@ import dayjs from 'lib/date/dayjs';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import * as stubs from 'stubs/contract'; import * as stubs from 'stubs/contract';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import RawDataSnippet from 'ui/shared/RawDataSnippet'; import RawDataSnippet from 'ui/shared/RawDataSnippet';
...@@ -107,9 +105,14 @@ const ContractCode = ({ addressHash, noSocket }: Props) => { ...@@ -107,9 +105,14 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
const decoded = data.decoded_constructor_args const decoded = data.decoded_constructor_args
.map(([ value, { name, type } ], index) => { .map(([ value, { name, type } ], index) => {
const valueEl = type === 'address' ? const valueEl = type === 'address' ? (
<LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: value } }) }>{ value }</LinkInternal> : <AddressEntity
<span>{ value }</span>; address={{ hash: value }}
noIcon
display="inline-flex"
maxW="100%"
/>
) : <span>{ value }</span>;
return ( return (
<Box key={ index }> <Box key={ index }>
<span>Arg [{ index }] { name || '' } ({ type }): </span> <span>Arg [{ index }] { name || '' } ({ type }): </span>
...@@ -171,10 +174,12 @@ const ContractCode = ({ addressHash, noSocket }: Props) => { ...@@ -171,10 +174,12 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
{ !data?.is_verified && data?.verified_twin_address_hash && !data?.minimal_proxy_address_hash && ( { !data?.is_verified && data?.verified_twin_address_hash && !data?.minimal_proxy_address_hash && (
<Alert status="warning" whiteSpace="pre-wrap" flexWrap="wrap"> <Alert status="warning" whiteSpace="pre-wrap" flexWrap="wrap">
<span>Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB </span> <span>Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB </span>
<Address> <AddressEntity
<AddressIcon address={{ hash: data.verified_twin_address_hash, is_contract: true, implementation_name: null }}/> address={{ hash: data.verified_twin_address_hash, is_contract: true, implementation_name: null }}
<AddressLink type="address" hash={ data.verified_twin_address_hash } truncation="constant" ml={ 2 }/> truncation="constant"
</Address> fontSize="sm"
fontWeight="500"
/>
<chakra.span mt={ 1 }>All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with </chakra.span> <chakra.span mt={ 1 }>All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with </chakra.span>
<LinkInternal href={ route({ pathname: '/address/[hash]/contract-verification', query: { hash: addressHash || '' } }) }> <LinkInternal href={ route({ pathname: '/address/[hash]/contract-verification', query: { hash: addressHash || '' } }) }>
Verify & Publish Verify & Publish
...@@ -185,10 +190,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => { ...@@ -185,10 +190,13 @@ const ContractCode = ({ addressHash, noSocket }: Props) => {
{ data?.minimal_proxy_address_hash && ( { data?.minimal_proxy_address_hash && (
<Alert status="warning" flexWrap="wrap" whiteSpace="pre-wrap"> <Alert status="warning" flexWrap="wrap" whiteSpace="pre-wrap">
<span>Minimal Proxy Contract for </span> <span>Minimal Proxy Contract for </span>
<Address> <AddressEntity
<AddressIcon address={{ hash: data.minimal_proxy_address_hash, is_contract: true, implementation_name: null }}/> address={{ hash: data.minimal_proxy_address_hash, is_contract: true, implementation_name: null }}
<AddressLink type="address" hash={ data.minimal_proxy_address_hash } truncation="constant" ml={ 2 }/> truncation="constant"
</Address> fontSize="sm"
fontWeight="500"
noCopy
/>
<span>. </span> <span>. </span>
<Box> <Box>
<Link href="https://eips.ethereum.org/EIPS/eip-1167">EIP-1167</Link> <Link href="https://eips.ethereum.org/EIPS/eip-1167">EIP-1167</Link>
......
...@@ -4,8 +4,7 @@ import React from 'react'; ...@@ -4,8 +4,7 @@ import React from 'react';
import { useAccount, useDisconnect } from 'wagmi'; import { useAccount, useDisconnect } from 'wagmi';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressLink from 'ui/shared/address/AddressLink';
const ContractConnectWallet = () => { const ContractConnectWallet = () => {
const { open, isOpen } = useWeb3Modal(); const { open, isOpen } = useWeb3Modal();
...@@ -47,8 +46,12 @@ const ContractConnectWallet = () => { ...@@ -47,8 +46,12 @@ const ContractConnectWallet = () => {
<Flex columnGap={ 3 } rowGap={ 3 } alignItems={{ base: 'flex-start', lg: 'center' }} flexDir={{ base: 'column', lg: 'row' }}> <Flex columnGap={ 3 } rowGap={ 3 } alignItems={{ base: 'flex-start', lg: 'center' }} flexDir={{ base: 'column', lg: 'row' }}>
<Flex alignItems="center"> <Flex alignItems="center">
<span>Connected to </span> <span>Connected to </span>
<AddressIcon address={{ hash: address, is_contract: false, implementation_name: null }} mx={ 2 }/> <AddressEntity
<AddressLink type="address" fontWeight={ 600 } hash={ address } truncation={ isMobile ? 'constant' : 'dynamic' }/> address={{ hash: address }}
truncation={ isMobile ? 'constant' : 'dynamic' }
fontWeight={ 600 }
ml={ 2 }
/>
</Flex> </Flex>
<Button onClick={ handleDisconnect } size="sm" variant="outline">Disconnect</Button> <Button onClick={ handleDisconnect } size="sm" variant="outline">Disconnect</Button>
</Flex> </Flex>
......
...@@ -24,10 +24,7 @@ import arrowIcon from 'icons/arrows/east-mini.svg'; ...@@ -24,10 +24,7 @@ import arrowIcon from 'icons/arrows/east-mini.svg';
import iconWarning from 'icons/status/warning.svg'; import iconWarning from 'icons/status/warning.svg';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
interface Props { interface Props {
className?: string; className?: string;
...@@ -38,11 +35,13 @@ const Item = (data: SmartContractExternalLibrary) => { ...@@ -38,11 +35,13 @@ const Item = (data: SmartContractExternalLibrary) => {
return ( return (
<Flex flexDir="column" py={ 2 } w="100%" rowGap={ 1 }> <Flex flexDir="column" py={ 2 } w="100%" rowGap={ 1 }>
<Box>{ data.name }</Box> <Box>{ data.name }</Box>
<Address> <AddressEntity
<AddressIcon address={{ hash: data.address_hash, is_contract: true, implementation_name: null }}/> address={{ hash: data.address_hash, is_contract: true, implementation_name: null }}
<AddressLink hash={ data.address_hash } type="address" ml={ 2 } fontWeight={ 500 } fontSize="sm" target="_blank" query={{ tab: 'contract' }}/> query={{ tab: 'contract' }}
<CopyToClipboard text={ data.address_hash }/> fontSize="sm"
</Address> fontWeight="500"
target="_blank"
/>
</Flex> </Flex>
); );
}; };
......
import { Flex } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { Address as TAddress } from 'types/api/address'; import type { Address as TAddress } from 'types/api/address';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressLink from 'ui/shared/address/AddressLink';
interface Props { interface Props {
hash: string | undefined; hash: string | undefined;
...@@ -22,10 +22,10 @@ const ContractImplementationAddress = ({ hash }: Props) => { ...@@ -22,10 +22,10 @@ const ContractImplementationAddress = ({ hash }: Props) => {
} }
return ( return (
<Address whiteSpace="pre-wrap" flexWrap="wrap" mb={ 6 }> <Flex mb={ 6 } flexWrap="wrap" columnGap={ 2 }>
<span>Implementation address: </span> <span>Implementation address:</span>
<AddressLink type="address" hash={ data.implementation_address }/> <AddressEntity address={{ hash: data.implementation_address, is_contract: true }} noIcon noCopy/>
</Address> </Flex>
); );
}; };
......
...@@ -7,9 +7,7 @@ import type { SmartContractMethodOutput } from 'types/api/contract'; ...@@ -7,9 +7,7 @@ import type { SmartContractMethodOutput } from 'types/api/contract';
import config from 'configs/app'; import config from 'configs/app';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
function castValueToString(value: number | string | boolean | bigint | undefined): string { function castValueToString(value: number | string | boolean | bigint | undefined): string {
switch (typeof value) { switch (typeof value) {
...@@ -49,10 +47,10 @@ const ContractMethodStatic = ({ data }: Props) => { ...@@ -49,10 +47,10 @@ const ContractMethodStatic = ({ data }: Props) => {
const content = (() => { const content = (() => {
if (typeof data.value === 'string' && data.type === 'address' && data.value) { if (typeof data.value === 'string' && data.type === 'address' && data.value) {
return ( return (
<Address> <AddressEntity
<AddressLink type="address" hash={ data.value }/> address={{ hash: data.value }}
<CopyToClipboard text={ data.value }/> noIcon
</Address> />
); );
} }
......
...@@ -10,7 +10,6 @@ import useSocketChannel from 'lib/socket/useSocketChannel'; ...@@ -10,7 +10,6 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props { interface Props {
data: Pick<Address, 'block_number_balance_updated_at' | 'coin_balance' | 'hash' | 'exchange_rate'>; data: Pick<Address, 'block_number_balance_updated_at' | 'coin_balance' | 'hash' | 'exchange_rate'>;
...@@ -64,11 +63,6 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -64,11 +63,6 @@ const AddressBalance = ({ data, isLoading }: Props) => {
handler: handleNewCoinBalanceMessage, handler: handleNewCoinBalanceMessage,
}); });
const tokenData = React.useMemo(() => ({
name: config.chain.currency.name || '',
icon_url: '',
}), [ ]);
return ( return (
<DetailsInfoItem <DetailsInfoItem
title="Balance" title="Balance"
...@@ -77,13 +71,6 @@ const AddressBalance = ({ data, isLoading }: Props) => { ...@@ -77,13 +71,6 @@ const AddressBalance = ({ data, isLoading }: Props) => {
alignItems="flex-start" alignItems="flex-start"
isLoading={ isLoading } isLoading={ isLoading }
> >
<TokenLogo
data={ tokenData }
boxSize={ 5 }
mr={ 2 }
fontSize="sm"
isLoading={ isLoading }
/>
<CurrencyValue <CurrencyValue
value={ data.coin_balance || '0' } value={ data.coin_balance || '0' }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import type { Address } from 'types/api/address'; import type { Address } from 'types/api/address';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
interface Props { interface Props {
data: Pick<Address, 'name' | 'token' | 'is_contract'>; data: Pick<Address, 'name' | 'token' | 'is_contract'>;
...@@ -19,7 +19,12 @@ const AddressNameInfo = ({ data, isLoading }: Props) => { ...@@ -19,7 +19,12 @@ const AddressNameInfo = ({ data, isLoading }: Props) => {
hint="Token name and symbol" hint="Token name and symbol"
isLoading={ isLoading } isLoading={ isLoading }
> >
<TokenSnippet data={ data.token } isLoading={ isLoading } hideIcon/> <TokenEntity
token={ data.token }
isLoading={ isLoading }
noIcon
noCopy
/>
</DetailsInfoItem> </DetailsInfoItem>
); );
} }
......
...@@ -7,12 +7,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; ...@@ -7,12 +7,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
...@@ -40,7 +37,7 @@ const TxInternalsListItem = ({ ...@@ -40,7 +37,7 @@ const TxInternalsListItem = ({
const toData = to ? to : createdContract; const toData = to ? to : createdContract;
const isOut = Boolean(currentAddress && currentAddress === from.hash); const isOut = Boolean(currentAddress && currentAddress === from.hash);
const isIn = Boolean(currentAddress && currentAddress === to?.hash); const isIn = Boolean(currentAddress && currentAddress === toData?.hash);
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
...@@ -70,21 +67,25 @@ const TxInternalsListItem = ({ ...@@ -70,21 +67,25 @@ const TxInternalsListItem = ({
/> />
</HStack> </HStack>
<Box w="100%" display="flex" columnGap={ 3 }> <Box w="100%" display="flex" columnGap={ 3 }>
<Address width="calc((100% - 48px) / 2)"> <AddressEntity
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ isOut } isLoading={ isLoading }/> isLoading={ isLoading }
{ isIn && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> } noLink={ isOut }
</Address> noCopy={ isOut }
width="calc((100% - 48px) / 2)"
/>
{ (isIn || isOut) ? { (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> : <InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/> <Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
} }
{ toData && ( { toData && (
<Address width="calc((100% - 48px) / 2)"> <AddressEntity
<AddressIcon address={ toData } isLoading={ isLoading }/> address={ toData }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ toData.hash } isDisabled={ isIn } isLoading={ isLoading }/> isLoading={ isLoading }
{ isOut && <CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> } noLink={ isIn }
</Address> noCopy={ isIn }
width="calc((100% - 48px) / 2)"
/>
) } ) }
</Box> </Box>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
......
...@@ -7,12 +7,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; ...@@ -7,12 +7,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
...@@ -39,7 +36,7 @@ const AddressIntTxsTableItem = ({ ...@@ -39,7 +36,7 @@ const AddressIntTxsTableItem = ({
const toData = to ? to : createdContract; const toData = to ? to : createdContract;
const isOut = Boolean(currentAddress && currentAddress === from.hash); const isOut = Boolean(currentAddress && currentAddress === from.hash);
const isIn = Boolean(currentAddress && currentAddress === to?.hash); const isIn = Boolean(currentAddress && currentAddress === toData?.hash);
const timeAgo = useTimeAgoIncrement(timestamp, true); const timeAgo = useTimeAgoIncrement(timestamp, true);
...@@ -81,34 +78,27 @@ const AddressIntTxsTableItem = ({ ...@@ -81,34 +78,27 @@ const AddressIntTxsTableItem = ({
/> />
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink
type="address"
ml={ 2 }
fontWeight="500"
hash={ from.hash }
alias={ from.name }
flexGrow={ 1 }
isDisabled={ isOut }
isLoading={ isLoading } isLoading={ isLoading }
noLink={ isOut }
noCopy={ isOut }
/> />
{ isIn && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address>
</Td> </Td>
<Td px={ 0 } verticalAlign="middle"> <Td px={ 0 } verticalAlign="middle">
{ (isIn || isOut) ? { (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading }/> : <InOutTag isIn={ isIn } isOut={ isOut } isLoading={ isLoading } w="100%"/> :
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/> <Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
} }
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
{ toData && ( { toData && (
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ toData } isLoading={ isLoading }/> address={ toData }
<AddressLink type="address" hash={ toData.hash } alias={ toData.name } fontWeight="500" ml={ 2 } isDisabled={ isIn } isLoading={ isLoading }/> isLoading={ isLoading }
{ isOut && <CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> } noLink={ isIn }
</Address> noCopy={ isIn }
/>
) } ) }
</Td> </Td>
<Td isNumeric verticalAlign="middle"> <Td isNumeric verticalAlign="middle">
......
...@@ -4,7 +4,7 @@ import React from 'react'; ...@@ -4,7 +4,7 @@ import React from 'react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
import type { TokenEnhancedData } from '../utils/tokenUtils'; import type { TokenEnhancedData } from '../utils/tokenUtils';
...@@ -68,7 +68,13 @@ const TokenSelectItem = ({ data }: Props) => { ...@@ -68,7 +68,13 @@ const TokenSelectItem = ({ data }: Props) => {
href={ url } href={ url }
> >
<Flex alignItems="center" w="100%" overflow="hidden"> <Flex alignItems="center" w="100%" overflow="hidden">
<TokenSnippet data={ data.token } hideSymbol fontWeight={ 700 } isDisabled/> <TokenEntity
token={ data.token }
noSymbol
noCopy
noLink
fontWeight={ 700 }
/>
{ data.usd && <Text fontWeight={ 700 } ml="auto">${ data.usd.toFormat(2) }</Text> } { data.usd && <Text fontWeight={ 700 } ml="auto">${ data.usd.toFormat(2) }</Text> }
</Flex> </Flex>
<Flex alignItems="center" justifyContent="space-between" w="100%" whiteSpace="nowrap"> <Flex alignItems="center" justifyContent="space-between" w="100%" whiteSpace="nowrap">
......
...@@ -5,17 +5,14 @@ import type { AddressTokenBalance } from 'types/api/address'; ...@@ -5,17 +5,14 @@ import type { AddressTokenBalance } from 'types/api/address';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenLogo from 'ui/shared/TokenLogo';
type Props = AddressTokenBalance & { isLoading: boolean}; type Props = AddressTokenBalance & { isLoading: boolean};
const ERC20TokensListItem = ({ token, value, isLoading }: Props) => { const ERC20TokensListItem = ({ token, value, isLoading }: Props) => {
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
const { const {
valueStr: tokenQuantity, valueStr: tokenQuantity,
usd: tokenValue, usd: tokenValue,
...@@ -24,12 +21,21 @@ const ERC20TokensListItem = ({ token, value, isLoading }: Props) => { ...@@ -24,12 +21,21 @@ const ERC20TokensListItem = ({ token, value, isLoading }: Props) => {
return ( return (
<ListItemMobile rowGap={ 2 }> <ListItemMobile rowGap={ 2 }>
<Flex alignItems="center" width="100%"> <Flex alignItems="center" width="100%">
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> <TokenEntity
<AddressLink fontWeight="700" hash={ token.address } type="token" alias={ tokenString } isLoading={ isLoading }/> token={ token }
isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
/>
</Flex> </Flex>
<Flex alignItems="center" pl={ 8 }> <Flex alignItems="center" pl={ 8 }>
<AddressLink hash={ token.address } type="address" truncation="constant" isLoading={ isLoading }/> <AddressEntity
<CopyToClipboard text={ token.address } isLoading={ isLoading }/> address={{ hash: token.address }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/> <AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Flex> </Flex>
{ token.exchange_rate !== undefined && token.exchange_rate !== null && ( { token.exchange_rate !== undefined && token.exchange_rate !== null && (
......
...@@ -5,9 +5,8 @@ import type { AddressTokenBalance } from 'types/api/address'; ...@@ -5,9 +5,8 @@ import type { AddressTokenBalance } from 'types/api/address';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TokenLogo from 'ui/shared/TokenLogo';
type Props = AddressTokenBalance & { isLoading: boolean }; type Props = AddressTokenBalance & { isLoading: boolean };
...@@ -17,8 +16,6 @@ const ERC20TokensTableItem = ({ ...@@ -17,8 +16,6 @@ const ERC20TokensTableItem = ({
isLoading, isLoading,
}: Props) => { }: Props) => {
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
const { const {
valueStr: tokenQuantity, valueStr: tokenQuantity,
usd: tokenValue, usd: tokenValue,
...@@ -27,17 +24,21 @@ const ERC20TokensTableItem = ({ ...@@ -27,17 +24,21 @@ const ERC20TokensTableItem = ({
return ( return (
<Tr> <Tr>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Flex alignItems="center"> <TokenEntity
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> token={ token }
<AddressLink fontWeight="700" hash={ token.address } type="token" alias={ tokenString } isLoading={ isLoading }/> isLoading={ isLoading }
</Flex> noCopy
jointSymbol
fontWeight="700"
/>
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Flex alignItems="center" width="150px" justifyContent="space-between"> <Flex alignItems="center" width="150px" justifyContent="space-between">
<Flex alignItems="center"> <AddressEntity
<AddressLink hash={ token.address } type="address" truncation="constant" isLoading={ isLoading }/> address={{ hash: token.address }}
<CopyToClipboard text={ token.address } isLoading={ isLoading }/> isLoading={ isLoading }
</Flex> noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/> <AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/>
</Flex> </Flex>
</Td> </Td>
......
...@@ -5,10 +5,9 @@ import React from 'react'; ...@@ -5,10 +5,9 @@ import React from 'react';
import type { AddressTokenBalance } from 'types/api/address'; import type { AddressTokenBalance } from 'types/api/address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import TokenEntityWithAddressFilter from 'ui/shared/entities/token/TokenEntityWithAddressFilter';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenLogo from 'ui/shared/TokenLogo';
type Props = AddressTokenBalance & { isLoading: boolean}; type Props = AddressTokenBalance & { isLoading: boolean};
...@@ -17,17 +16,23 @@ const ERC721TokensListItem = ({ token, value, isLoading }: Props) => { ...@@ -17,17 +16,23 @@ const ERC721TokensListItem = ({ token, value, isLoading }: Props) => {
const hash = router.query.hash?.toString() || ''; const hash = router.query.hash?.toString() || '';
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
return ( return (
<ListItemMobile rowGap={ 2 }> <ListItemMobile rowGap={ 2 }>
<Flex alignItems="center" width="100%"> <TokenEntityWithAddressFilter
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> token={ token }
<AddressLink fontWeight="700" hash={ hash } tokenHash={ token.address } type="address_token" alias={ tokenString } isLoading={ isLoading }/> isLoading={ isLoading }
</Flex> addressHash={ hash }
noCopy
jointSymbol
fontWeight={ 700 }
/>
<Flex alignItems="center" pl={ 8 }> <Flex alignItems="center" pl={ 8 }>
<AddressLink hash={ token.address } type="address" truncation="constant" isLoading={ isLoading }/> <AddressEntity
<CopyToClipboard text={ token.address } isLoading={ isLoading }/> address={{ hash: token.address }}
isLoading={ isLoading }
truncation="constant"
noIcon
/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/> <AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Flex> </Flex>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
......
...@@ -5,9 +5,8 @@ import React from 'react'; ...@@ -5,9 +5,8 @@ import React from 'react';
import type { AddressTokenBalance } from 'types/api/address'; import type { AddressTokenBalance } from 'types/api/address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import TokenEntityWithAddressFilter from 'ui/shared/entities/token/TokenEntityWithAddressFilter';
import TokenLogo from 'ui/shared/TokenLogo';
type Props = AddressTokenBalance & { isLoading: boolean}; type Props = AddressTokenBalance & { isLoading: boolean};
...@@ -19,22 +18,26 @@ const ERC721TokensTableItem = ({ ...@@ -19,22 +18,26 @@ const ERC721TokensTableItem = ({
const router = useRouter(); const router = useRouter();
const hash = router.query.hash?.toString() || ''; const hash = router.query.hash?.toString() || '';
const tokenString = [ token.name, token.symbol && `(${ token.symbol })` ].filter(Boolean).join(' ');
return ( return (
<Tr> <Tr>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Flex alignItems="center"> <TokenEntityWithAddressFilter
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> token={ token }
<AddressLink fontWeight="700" hash={ hash } tokenHash={ token.address } type="address_token" alias={ tokenString } isLoading={ isLoading }/> addressHash={ hash }
</Flex> isLoading={ isLoading }
noCopy
jointSymbol
fontWeight="700"
/>
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Flex alignItems="center" width="150px" justifyContent="space-between"> <Flex alignItems="center" width="150px" justifyContent="space-between">
<Flex alignItems="center"> <AddressEntity
<AddressLink hash={ token.address } type="address" truncation="dynamic" isLoading={ isLoading }/> address={{ hash: token.address }}
<CopyToClipboard text={ token.address } isLoading={ isLoading }/> isLoading={ isLoading }
</Flex> noIcon
/>
<AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/> <AddressAddToWallet token={ token } ml={ 4 } isLoading={ isLoading }/>
</Flex> </Flex>
</Td> </Td>
......
...@@ -5,10 +5,9 @@ import type { AddressTokenBalance } from 'types/api/address'; ...@@ -5,10 +5,9 @@ import type { AddressTokenBalance } from 'types/api/address';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import NftMedia from 'ui/shared/nft/NftMedia'; import NftMedia from 'ui/shared/nft/NftMedia';
import TokenLogo from 'ui/shared/TokenLogo';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TruncatedValue from 'ui/shared/TruncatedValue';
type Props = AddressTokenBalance & { isLoading: boolean }; type Props = AddressTokenBalance & { isLoading: boolean };
...@@ -53,12 +52,12 @@ const NFTItem = ({ token, token_id: tokenId, token_instance: tokenInstance, isLo ...@@ -53,12 +52,12 @@ const NFTItem = ({ token, token_id: tokenId, token_instance: tokenInstance, isLo
</TruncatedTextTooltip> </TruncatedTextTooltip>
</Flex> </Flex>
) } ) }
{ token.name && ( <TokenEntity
<Flex alignItems="center"> token={ token }
<TokenLogo data={ token } boxSize={ 6 } ml={ 1 } mr={ 1 } isLoading={ isLoading }/> isLoading={ isLoading }
<TruncatedValue isLoading={ isLoading } value={ token.name } color="text_secondary"/> noCopy
</Flex> noSymbol
) } />
</LinkBox> </LinkBox>
); );
}; };
......
...@@ -5,11 +5,8 @@ import React from 'react'; ...@@ -5,11 +5,8 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses'; import type { AddressesItem } from 'types/api/addresses';
import config from 'configs/app'; import config from 'configs/app';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
type Props = { type Props = {
...@@ -31,19 +28,12 @@ const AddressesListItem = ({ ...@@ -31,19 +28,12 @@ const AddressesListItem = ({
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
<Flex alignItems="center" justifyContent="space-between" w="100%"> <Flex alignItems="center" justifyContent="space-between" w="100%">
<Address maxW="100%" mr={ 8 }> <AddressEntity
<AddressIcon address={ item } mr={ 2 } isLoading={ isLoading }/> address={ item }
<AddressLink
fontWeight={ 700 }
flexGrow={ 1 }
w="calc(100% - 32px)"
hash={ item.hash }
alias={ item.name }
type="address"
isLoading={ isLoading } isLoading={ isLoading }
fontWeight={ 700 }
mr={ 2 }
/> />
<CopyToClipboard text={ item.hash } isLoading={ isLoading }/>
</Address>
<Skeleton isLoaded={ !isLoading } fontSize="sm" ml="auto" minW={ 6 } color="text_secondary"> <Skeleton isLoaded={ !isLoading } fontSize="sm" ml="auto" minW={ 6 } color="text_secondary">
<span>{ index }</span> <span>{ index }</span>
</Skeleton> </Skeleton>
......
...@@ -5,11 +5,8 @@ import React from 'react'; ...@@ -5,11 +5,8 @@ import React from 'react';
import type { AddressesItem } from 'types/api/addresses'; import type { AddressesItem } from 'types/api/addresses';
import config from 'configs/app'; import config from 'configs/app';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
type Props = { type Props = {
item: AddressesItem; item: AddressesItem;
...@@ -37,20 +34,12 @@ const AddressesTableItem = ({ ...@@ -37,20 +34,12 @@ const AddressesTableItem = ({
{ index } { index }
</Skeleton> </Skeleton>
</Td> </Td>
<Td> <Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ item } mr={ 2 } isLoading={ isLoading }/> address={ item }
<AddressLink
fontWeight={ 700 }
flexGrow={ 1 }
w="calc(100% - 32px)"
hash={ item.hash }
alias={ item.name }
type="address"
isLoading={ isLoading } isLoading={ isLoading }
fontWeight={ 700 }
/> />
<CopyToClipboard text={ item.hash } isLoading={ isLoading }/>
</Address>
</Td> </Td>
<Td pl={ 10 }> <Td pl={ 10 }>
{ item.public_tags && item.public_tags.length ? item.public_tags.map(tag => ( { item.public_tags && item.public_tags.length ? item.public_tags.map(tag => (
......
...@@ -20,11 +20,11 @@ import dayjs from 'lib/date/dayjs'; ...@@ -20,11 +20,11 @@ import dayjs from 'lib/date/dayjs';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
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';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -206,8 +206,10 @@ const BlockDetails = ({ query }: Props) => { ...@@ -206,8 +206,10 @@ const BlockDetails = ({ query }: Props) => {
columnGap={ 1 } columnGap={ 1 }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
<AddressLink type="address" hash={ data.miner.hash } isLoading={ isPlaceholderData }/> <AddressEntity
{ data.miner.name && <Text>{ `(${ capitalize(validatorTitle) }: ${ data.miner.name })` }</Text> } address={ data.miner }
isLoading={ isPlaceholderData }
/>
{ /* api doesn't return the block processing time yet */ } { /* api doesn't return the block processing time yet */ }
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ } { /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ }
</DetailsInfoItem> </DetailsInfoItem>
......
...@@ -13,8 +13,8 @@ import getBlockTotalReward from 'lib/block/getBlockTotalReward'; ...@@ -13,8 +13,8 @@ import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -55,9 +55,12 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -55,9 +55,12 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
<span>{ data.size.toLocaleString() } bytes</span> <span>{ data.size.toLocaleString() } bytes</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 } w="100%">
<Text fontWeight={ 500 }>{ capitalize(getNetworkValidatorTitle()) }</Text> <Text fontWeight={ 500 }>{ capitalize(getNetworkValidatorTitle()) }</Text>
<AddressLink type="address" alias={ data.miner.name } hash={ data.miner.hash } truncation="constant" isLoading={ isLoading }/> <AddressEntity
address={ data.miner }
isLoading={ isLoading }
/>
</Flex> </Flex>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Txn</Text> <Text fontWeight={ 500 }>Txn</Text>
......
...@@ -25,9 +25,9 @@ const BlocksTable = ({ data, isLoading, top, page }: Props) => { ...@@ -25,9 +25,9 @@ const BlocksTable = ({ data, isLoading, top, page }: Props) => {
<Tr> <Tr>
<Th width="125px">Block</Th> <Th width="125px">Block</Th>
<Th width="120px">Size, bytes</Th> <Th width="120px">Size, bytes</Th>
<Th width={ config.features.rollup.isEnabled ? '37%' : '21%' } minW="144px">{ capitalize(getNetworkValidatorTitle()) }</Th> <Th width={ config.features.rollup.isEnabled ? '37%' : '23%' } minW="160px">{ capitalize(getNetworkValidatorTitle()) }</Th>
<Th width="64px" isNumeric>Txn</Th> <Th width="64px" isNumeric>Txn</Th>
<Th width={ config.features.rollup.isEnabled ? '63%' : '35%' }>Gas used</Th> <Th width={ config.features.rollup.isEnabled ? '63%' : '33%' }>Gas used</Th>
{ !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward && { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.total_reward &&
<Th width="22%">Reward { config.chain.currency.symbol }</Th> } <Th width="22%">Reward { config.chain.currency.symbol }</Th> }
{ !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.burnt_fees && { !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.burnt_fees &&
......
...@@ -12,8 +12,8 @@ import flameIcon from 'icons/flame.svg'; ...@@ -12,8 +12,8 @@ import flameIcon from 'icons/flame.svg';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio'; import GasUsedToTargetRatio from 'ui/shared/GasUsedToTargetRatio';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -65,13 +65,8 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -65,13 +65,8 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
</Skeleton> </Skeleton>
</Td> </Td>
<Td fontSize="sm"> <Td fontSize="sm">
<AddressLink <AddressEntity
type="address" address={ data.miner }
alias={ data.miner.name }
hash={ data.miner.hash }
truncation="constant"
display="inline-flex"
maxW="100%"
isLoading={ isLoading } isLoading={ isLoading }
/> />
</Td> </Td>
......
import { Box, Skeleton } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { CustomAbi } from 'types/api/account'; import type { CustomAbi } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -25,7 +26,16 @@ const CustomAbiListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Prop ...@@ -25,7 +26,16 @@ const CustomAbiListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Prop
return ( return (
<ListItemMobile> <ListItemMobile>
<AddressSnippet address={ item.contract_address } subtitle={ item.name } isLoading={ isLoading }/> <Box maxW="100%">
<AddressEntity
address={ item.contract_address }
fontWeight="600"
isLoading={ isLoading }
/>
<Skeleton fontSize="sm" color="text_secondary" mt={ 0.5 } ml={ 8 } display="inline-block" isLoaded={ !isLoading }>
<span>{ item.name }</span>
</Skeleton>
</Box>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/> <TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
</ListItemMobile> </ListItemMobile>
); );
......
import { import {
Tr, Tr,
Td, Td,
Box,
Skeleton,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { CustomAbi } from 'types/api/account'; import type { CustomAbi } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props { interface Props {
...@@ -29,7 +31,16 @@ const CustomAbiTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Pro ...@@ -29,7 +31,16 @@ const CustomAbiTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Pro
return ( return (
<Tr alignItems="top" key={ item.id }> <Tr alignItems="top" key={ item.id }>
<Td> <Td>
<AddressSnippet address={ item.contract_address } subtitle={ item.name } isLoading={ isLoading }/> <Box maxW="100%">
<AddressEntity
address={ item.contract_address }
fontWeight="600"
isLoading={ isLoading }
/>
<Skeleton fontSize="sm" color="text_secondary" mt={ 0.5 } ml={ 8 } display="inline-block" isLoaded={ !isLoading }>
<span>{ item.name }</span>
</Skeleton>
</Box>
</Td> </Td>
<Td> <Td>
<TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/> <TableItemActionButtons onDeleteClick={ onItemDeleteClick } onEditClick={ onItemEditClick } isLoading={ isLoading }/>
......
...@@ -13,7 +13,7 @@ import config from 'configs/app'; ...@@ -13,7 +13,7 @@ import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlockTimestamp from 'ui/blocks/BlockTimestamp'; import BlockTimestamp from 'ui/blocks/BlockTimestamp';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
type Props = { type Props = {
...@@ -66,7 +66,12 @@ const LatestBlocksItem = ({ block, h, isLoading }: Props) => { ...@@ -66,7 +66,12 @@ const LatestBlocksItem = ({ block, h, isLoading }: Props) => {
<Skeleton isLoaded={ !isLoading }>Reward</Skeleton> <Skeleton isLoaded={ !isLoading }>Reward</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ totalReward.toFixed() }</span></Skeleton> <Skeleton isLoaded={ !isLoading } color="text_secondary"><span>{ totalReward.toFixed() }</span></Skeleton>
<Skeleton isLoaded={ !isLoading } textTransform="capitalize">{ getNetworkValidatorTitle() }</Skeleton> <Skeleton isLoaded={ !isLoading } textTransform="capitalize">{ getNetworkValidatorTitle() }</Skeleton>
<AddressLink type="address" alias={ block.miner.name } hash={ block.miner.hash } truncation="constant" maxW="100%" isLoading={ isLoading }/> <AddressEntity
address={ block.miner }
isLoading={ isLoading }
noIcon
noCopy
/>
</> </>
) } ) }
</Grid> </Grid>
......
...@@ -14,10 +14,8 @@ import config from 'configs/app'; ...@@ -14,10 +14,8 @@ import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
...@@ -83,31 +81,20 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -83,31 +81,20 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
isLoading={ isLoading } isLoading={ isLoading }
/> />
<Box overflow="hidden" ml={ 1 }> <Box overflow="hidden" ml={ 1 }>
<Address mb={ 2 }> <AddressEntity
<AddressIcon address={ tx.from } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
fontSize="sm"
isLoading={ isLoading } isLoading={ isLoading }
address={ tx.from }
fontSize="sm"
fontWeight="500"
mb={ 2 }
/> />
</Address>
{ dataTo && ( { dataTo && (
<Address> <AddressEntity
<AddressIcon address={ dataTo } isLoading={ isLoading }/>
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
fontSize="sm"
isLoading={ isLoading } isLoading={ isLoading }
address={ dataTo }
fontSize="sm"
fontWeight="500"
/> />
</Address>
) } ) }
</Box> </Box>
</Grid> </Grid>
......
...@@ -13,10 +13,8 @@ import config from 'configs/app'; ...@@ -13,10 +13,8 @@ import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
...@@ -67,19 +65,14 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -67,19 +65,14 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
) } ) }
</Flex> </Flex>
<Flex alignItems="center" mb={ 3 }> <Flex alignItems="center" mb={ 3 }>
<Address mr={ 2 }> <AddressEntity
<AddressIcon address={ tx.from } isLoading={ isLoading }/> isLoading={ isLoading }
<AddressLink address={ tx.from }
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
truncation="constant" truncation="constant"
fontSize="sm" fontSize="sm"
isLoading={ isLoading } fontWeight="500"
mr={ 2 }
/> />
</Address>
<Icon <Icon
as={ rightArrowIcon } as={ rightArrowIcon }
boxSize={ 6 } boxSize={ 6 }
...@@ -87,19 +80,13 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => { ...@@ -87,19 +80,13 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
isLoading={ isLoading } isLoading={ isLoading }
/> />
{ dataTo && ( { dataTo && (
<Address ml={ 2 }> <AddressEntity
<AddressIcon address={ dataTo } isLoading={ isLoading }/> isLoading={ isLoading }
<AddressLink address={ dataTo }
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
truncation="constant" truncation="constant"
fontSize="sm" fontSize="sm"
isLoading={ isLoading } fontWeight="500"
/> />
</Address>
) } ) }
</Flex> </Flex>
<Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content"> <Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content">
......
...@@ -7,7 +7,7 @@ import config from 'configs/app'; ...@@ -7,7 +7,7 @@ import config from 'configs/app';
import globeIcon from 'icons/globe.svg'; import globeIcon from 'icons/globe.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import { sortByDateDesc } from 'ui/shared/chart/utils/sorts'; import { sortByDateDesc } from 'ui/shared/chart/utils/sorts';
import TokenLogo from 'ui/shared/TokenLogo'; import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = { const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = {
id: 'daily_txs', id: 'daily_txs',
...@@ -30,13 +30,15 @@ const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = { ...@@ -30,13 +30,15 @@ const dailyTxsIndicator: TChainIndicator<'homepage_chart_txs'> = {
const nativeTokenData = { const nativeTokenData = {
name: config.chain.currency.name || '', name: config.chain.currency.name || '',
icon_url: '', icon_url: '',
symbol: '',
address: '',
}; };
const coinPriceIndicator: TChainIndicator<'homepage_chart_market'> = { const coinPriceIndicator: TChainIndicator<'homepage_chart_market'> = {
id: 'coin_price', id: 'coin_price',
title: `${ config.chain.currency.symbol } price`, title: `${ config.chain.currency.symbol } price`,
value: (stats) => '$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }), value: (stats) => '$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
icon: <TokenLogo data={ nativeTokenData } boxSize={ 6 }/>, icon: <TokenEntity.Icon token={ nativeTokenData } boxSize={ 6 } marginRight={ 0 }/>,
hint: `${ config.chain.currency.symbol } token daily price in USD.`, hint: `${ config.chain.currency.symbol } token daily price in USD.`,
api: { api: {
resourceName: 'homepage_chart_market', resourceName: 'homepage_chart_market',
......
...@@ -4,16 +4,12 @@ import React from 'react'; ...@@ -4,16 +4,12 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressEntityL1 from 'ui/shared/entities/address/AddressEntityL1';
import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
const feature = config.features.rollup; const feature = config.features.rollup;
...@@ -31,7 +27,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -31,7 +27,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
<ListItemMobileGrid.Container> <ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 block No</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 block No</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<BlockEntityL1 <BlockEntityL1
number={ item.l1_block_number } number={ item.l1_block_number }
isLoading={ isLoading } isLoading={ isLoading }
...@@ -42,7 +38,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -42,7 +38,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<TxEntity <TxEntity
isLoading={ isLoading } isLoading={ isLoading }
hash={ item.l2_tx_hash } hash={ item.l2_tx_hash }
...@@ -57,7 +53,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -57,7 +53,7 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<TxEntityL1 <TxEntityL1
isLoading={ isLoading } isLoading={ isLoading }
hash={ item.l1_tx_hash } hash={ item.l1_tx_hash }
...@@ -67,19 +63,12 @@ const DepositsListItem = ({ item, isLoading }: Props) => { ...@@ -67,19 +63,12 @@ const DepositsListItem = ({ item, isLoading }: Props) => {
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn origin</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn origin</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<LinkExternal <AddressEntityL1
href={ feature.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) } address={{ hash: item.l1_tx_origin, name: '', is_contract: false, is_verified: false, implementation_name: '' }}
maxW="100%"
display="flex"
overflow="hidden"
isLoading={ isLoading } isLoading={ isLoading }
> noCopy
<AddressIcon address={{ hash: item.l1_tx_origin, is_contract: false, implementation_name: '' }} isLoading={ isLoading }/> />
<Skeleton isLoaded={ !isLoading } w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap" ml={ 2 }>
<HashStringShortenDynamic hash={ item.l1_tx_origin }/>
</Skeleton>
</LinkExternal>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
<ListItemMobileGrid.Label isLoading={ isLoading }>Gas limit</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Gas limit</ListItemMobileGrid.Label>
......
...@@ -4,16 +4,12 @@ import React from 'react'; ...@@ -4,16 +4,12 @@ import React from 'react';
import type { L2DepositsItem } from 'types/api/l2Deposits'; import type { L2DepositsItem } from 'types/api/l2Deposits';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressEntityL1 from 'ui/shared/entities/address/AddressEntityL1';
import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShorten from 'ui/shared/HashStringShorten';
import LinkExternal from 'ui/shared/LinkExternal';
const feature = config.features.rollup; const feature = config.features.rollup;
...@@ -59,18 +55,12 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => { ...@@ -59,18 +55,12 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
/> />
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<LinkExternal <AddressEntityL1
href={ feature.L1BaseUrl + route({ pathname: '/address/[hash]', query: { hash: item.l1_tx_origin } }) } address={{ hash: item.l1_tx_origin, name: '', is_contract: false, is_verified: false, implementation_name: '' }}
maxW="100%"
display="inline-flex"
overflow="hidden"
isLoading={ isLoading } isLoading={ isLoading }
> truncation="constant"
<AddressIcon address={{ hash: item.l1_tx_origin, is_contract: false, implementation_name: '' }} isLoading={ isLoading }/> noCopy
<Skeleton isLoaded={ !isLoading } w="calc(100% - 44px)" overflow="hidden" whiteSpace="nowrap" ml={ 2 }> />
<HashStringShorten hash={ item.l1_tx_origin }/>
</Skeleton>
</LinkExternal>
</Td> </Td>
<Td verticalAlign="middle" isNumeric> <Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block"> <Skeleton isLoaded={ !isLoading } color="text_secondary" display="inline-block">
......
...@@ -5,9 +5,7 @@ import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals'; ...@@ -5,9 +5,7 @@ import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
...@@ -38,17 +36,17 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => { ...@@ -38,17 +36,17 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => {
{ item.from && ( { item.from && (
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>From</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>From</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<Address> <AddressEntity
<AddressIcon address={ item.from } isLoading={ isLoading }/> address={ item.from }
<AddressLink hash={ item.from.hash } type="address" truncation="dynamic" ml={ 2 } isLoading={ isLoading }/> isLoading={ isLoading }
</Address> />
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
</> </>
) } ) }
<ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L2 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<TxEntity <TxEntity
isLoading={ isLoading } isLoading={ isLoading }
hash={ item.l2_tx_hash } hash={ item.l2_tx_hash }
...@@ -78,7 +76,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => { ...@@ -78,7 +76,7 @@ const WithdrawalsListItem = ({ item, isLoading }: Props) => {
{ item.l1_tx_hash && ( { item.l1_tx_hash && (
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>L1 txn hash</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<TxEntityL1 <TxEntityL1
isLoading={ isLoading } isLoading={ isLoading }
hash={ item.l1_tx_hash } hash={ item.l1_tx_hash }
......
...@@ -5,9 +5,7 @@ import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals'; ...@@ -5,9 +5,7 @@ import type { L2WithdrawalsItem } from 'types/api/l2Withdrawals';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
...@@ -31,10 +29,11 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => { ...@@ -31,10 +29,11 @@ const WithdrawalsTableItem = ({ item, isLoading }: Props) => {
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
{ item.from ? ( { item.from ? (
<Address> <AddressEntity
<AddressIcon address={ item.from } isLoading={ isLoading }/> address={ item.from }
<AddressLink hash={ item.from.hash } type="address" truncation="constant" ml={ 2 } isLoading={ isLoading }/> isLoading={ isLoading }
</Address> truncation="constant"
/>
) : 'N/A' } ) : 'N/A' }
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
......
import { Text } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -9,12 +8,9 @@ import { useAppContext } from 'lib/contexts/app'; ...@@ -9,12 +8,9 @@ import { useAppContext } from 'lib/contexts/app';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm'; import ContractVerificationForm from 'ui/contractVerification/ContractVerificationForm';
import { isValidVerificationMethod, sortVerificationMethods } from 'ui/contractVerification/utils'; import { isValidVerificationMethod, sortVerificationMethods } from 'ui/contractVerification/utils';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const ContractVerification = () => { const ContractVerification = () => {
...@@ -100,15 +96,14 @@ const ContractVerification = () => { ...@@ -100,15 +96,14 @@ const ContractVerification = () => {
title="New smart contract verification" title="New smart contract verification"
backLink={ backLink } backLink={ backLink }
/> />
{ hash && ( <AddressEntity
<Address mb={ 12 }> address={{ hash, is_contract: true, implementation_name: null }}
<AddressIcon address={{ hash, is_contract: true, implementation_name: null }} flexShrink={ 0 }/> noLink
<Text fontFamily="heading" ml={ 2 } fontWeight={ 500 } fontSize="lg" w={{ base: '100%', lg: 'auto' }} whiteSpace="nowrap" overflow="hidden"> fontFamily="heading"
<HashStringShortenDynamic hash={ hash }/> fontSize="lg"
</Text> fontWeight={ 500 }
<CopyToClipboard text={ hash }/> mb={ 12 }
</Address> />
) }
{ content } { content }
</> </>
); );
......
...@@ -11,11 +11,9 @@ import { useAppContext } from 'lib/contexts/app'; ...@@ -11,11 +11,9 @@ import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import CsvExportForm from 'ui/csvExport/CsvExportForm'; import CsvExportForm from 'ui/csvExport/CsvExportForm';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import DataFetchAlert from 'ui/shared/DataFetchAlert'; import DataFetchAlert from 'ui/shared/DataFetchAlert';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
interface ExportTypeEntity { interface ExportTypeEntity {
...@@ -60,8 +58,8 @@ const isCorrectExportType = (type: string): type is CsvExportParams['type'] => O ...@@ -60,8 +58,8 @@ const isCorrectExportType = (type: string): type is CsvExportParams['type'] => O
const CsvExport = () => { const CsvExport = () => {
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile();
const appProps = useAppContext(); const appProps = useAppContext();
const isMobile = useIsMobile();
const addressHash = router.query.address?.toString() || ''; const addressHash = router.query.address?.toString() || '';
const exportType = router.query.type?.toString() || ''; const exportType = router.query.type?.toString() || '';
...@@ -133,10 +131,11 @@ const CsvExport = () => { ...@@ -133,10 +131,11 @@ const CsvExport = () => {
/> />
<Flex mb={ 10 } whiteSpace="pre-wrap" flexWrap="wrap"> <Flex mb={ 10 } whiteSpace="pre-wrap" flexWrap="wrap">
<span>Export { EXPORT_TYPES[exportType].text } for address </span> <span>Export { EXPORT_TYPES[exportType].text } for address </span>
<Address> <AddressEntity
<AddressIcon address={{ hash: addressHash, is_contract: true, implementation_name: null }}/> address={{ hash: addressHash, is_contract: true, implementation_name: null }}
<AddressLink hash={ addressHash } type="address" ml={ 2 } truncation={ isMobile ? 'constant' : 'dynamic' }/> truncation={ isMobile ? 'constant' : 'dynamic' }
</Address> noCopy
/>
<span>{ nbsp }</span> <span>{ nbsp }</span>
{ filterType && filterValue && <span>with applied filter by { filterType } ({ filterValue }) </span> } { filterType && filterValue && <span>with applied filter by { filterType } ({ filterValue }) </span> }
<span>to CSV file. </span> <span>to CSV file. </span>
......
...@@ -3,16 +3,12 @@ import { useRouter } from 'next/router'; ...@@ -3,16 +3,12 @@ import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useIsMobile from 'lib/hooks/useIsMobile'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram'; import Sol2UmlDiagram from 'ui/sol2uml/Sol2UmlDiagram';
const Sol2Uml = () => { const Sol2Uml = () => {
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile();
const appProps = useAppContext(); const appProps = useAppContext();
const addressHash = router.query.address?.toString() || ''; const addressHash = router.query.address?.toString() || '';
...@@ -36,12 +32,11 @@ const Sol2Uml = () => { ...@@ -36,12 +32,11 @@ const Sol2Uml = () => {
title="Solidity UML diagram" title="Solidity UML diagram"
backLink={ backLink } backLink={ backLink }
/> />
<Flex mb={ 10 }> <Flex mb={ 10 } flexWrap="wrap" columnGap={ 3 }>
<span>For contract</span> <span>For contract</span>
<Address ml={ 3 }> <AddressEntity
<AddressIcon address={{ hash: addressHash, is_contract: true, implementation_name: null }}/> address={{ hash: addressHash, is_contract: true, implementation_name: null }}
<AddressLink hash={ addressHash } type="address" ml={ 2 } truncation={ isMobile ? 'constant' : 'dynamic' }/> />
</Address>
</Flex> </Flex>
<Sol2UmlDiagram addressHash={ addressHash }/> <Sol2UmlDiagram addressHash={ addressHash }/>
</> </>
......
...@@ -24,6 +24,7 @@ import * as tokenStubs from 'stubs/token'; ...@@ -24,6 +24,7 @@ import * as tokenStubs from 'stubs/token';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import AddressContract from 'ui/address/AddressContract'; import AddressContract from 'ui/address/AddressContract';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -31,7 +32,6 @@ import Pagination from 'ui/shared/pagination/Pagination'; ...@@ -31,7 +32,6 @@ import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
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 TokenLogo from 'ui/shared/TokenLogo';
import TokenContractInfo from 'ui/token/TokenContractInfo'; import TokenContractInfo from 'ui/token/TokenContractInfo';
import TokenDetails from 'ui/token/TokenDetails'; import TokenDetails from 'ui/token/TokenDetails';
import TokenHolders from 'ui/token/TokenHolders/TokenHolders'; import TokenHolders from 'ui/token/TokenHolders/TokenHolders';
...@@ -270,14 +270,13 @@ const TokenPageContent = () => { ...@@ -270,14 +270,13 @@ const TokenPageContent = () => {
title={ `${ tokenQuery.data?.name || 'Unnamed token' }${ tokenSymbolText }` } title={ `${ tokenQuery.data?.name || 'Unnamed token' }${ tokenSymbolText }` }
isLoading={ tokenQuery.isPlaceholderData } isLoading={ tokenQuery.isPlaceholderData }
backLink={ backLink } backLink={ backLink }
beforeTitle={ ( beforeTitle={ tokenQuery.data ? (
<TokenLogo <TokenEntity.Icon
data={ tokenQuery.data } token={ tokenQuery.data }
boxSize={ 6 }
isLoading={ tokenQuery.isPlaceholderData } isLoading={ tokenQuery.isPlaceholderData }
mr={ 2 } iconSize="lg"
/> />
) } ) : null }
contentAfter={ titleContentAfter } contentAfter={ titleContentAfter }
/> />
<TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/> <TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/>
......
...@@ -3,7 +3,7 @@ import React, { useCallback } from 'react'; ...@@ -3,7 +3,7 @@ import React, { useCallback } from 'react';
import type { AddressTag } from 'types/api/account'; import type { AddressTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -26,7 +26,12 @@ const AddressTagListItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pro ...@@ -26,7 +26,12 @@ const AddressTagListItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pro
return ( return (
<ListItemMobile> <ListItemMobile>
<Flex alignItems="flex-start" flexDirection="column" maxW="100%"> <Flex alignItems="flex-start" flexDirection="column" maxW="100%">
<AddressSnippet address={ item.address } isLoading={ isLoading }/> <AddressEntity
address={ item.address }
isLoading={ isLoading }
fontWeight="600"
w="100%"
/>
<HStack spacing={ 3 } mt={ 4 }> <HStack spacing={ 3 } mt={ 4 }>
<Text fontSize="sm" fontWeight={ 500 }>Private tag</Text> <Text fontSize="sm" fontWeight={ 500 }>Private tag</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm"> <Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm">
......
...@@ -6,8 +6,8 @@ import React, { useCallback } from 'react'; ...@@ -6,8 +6,8 @@ import React, { useCallback } from 'react';
import type { AddressTag } from 'types/api/account'; import type { AddressTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props { interface Props {
...@@ -29,7 +29,12 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pr ...@@ -29,7 +29,12 @@ const AddressTagTableItem = ({ item, onEditClick, onDeleteClick, isLoading }: Pr
return ( return (
<Tr alignItems="top" key={ item.id }> <Tr alignItems="top" key={ item.id }>
<Td> <Td>
<AddressSnippet address={ item.address } isLoading={ isLoading }/> <AddressEntity
address={ item.address }
isLoading={ isLoading }
fontWeight="600"
py="2px"
/>
</Td> </Td>
<Td whiteSpace="nowrap"> <Td whiteSpace="nowrap">
<Tag isLoading={ isLoading } isTruncated>{ item.name }</Tag> <Tag isLoading={ isLoading } isTruncated>{ item.name }</Tag>
......
...@@ -3,8 +3,8 @@ import React, { useCallback } from 'react'; ...@@ -3,8 +3,8 @@ import React, { useCallback } from 'react';
import type { PublicTag } from 'types/api/account'; import type { PublicTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
...@@ -28,7 +28,15 @@ const PublicTagListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Prop ...@@ -28,7 +28,15 @@ const PublicTagListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Prop
<ListItemMobile> <ListItemMobile>
<VStack spacing={ 3 } alignItems="flex-start" maxW="100%"> <VStack spacing={ 3 } alignItems="flex-start" maxW="100%">
<VStack spacing={ 4 } alignItems="unset" maxW="100%"> <VStack spacing={ 4 } alignItems="unset" maxW="100%">
{ item.addresses_with_info.map((address) => <AddressSnippet key={ address.hash } address={ address } isLoading={ isLoading }/>) } { item.addresses_with_info.map((address) => (
<AddressEntity
key={ address.hash }
address={ address }
isLoading={ isLoading }
fontWeight="600"
w="100%"
/>
)) }
</VStack> </VStack>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
<Text fontSize="sm" fontWeight={ 500 }>Public tags</Text> <Text fontSize="sm" fontWeight={ 500 }>Public tags</Text>
......
...@@ -9,8 +9,8 @@ import React, { useCallback } from 'react'; ...@@ -9,8 +9,8 @@ import React, { useCallback } from 'react';
import type { PublicTag } from 'types/api/account'; import type { PublicTag } from 'types/api/account';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TableItemActionButtons from 'ui/shared/TableItemActionButtons'; import TableItemActionButtons from 'ui/shared/TableItemActionButtons';
interface Props { interface Props {
...@@ -33,7 +33,15 @@ const PublicTagTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Pro ...@@ -33,7 +33,15 @@ const PublicTagTableItem = ({ item, isLoading, onEditClick, onDeleteClick }: Pro
<Tr alignItems="top" key={ item.id }> <Tr alignItems="top" key={ item.id }>
<Td> <Td>
<VStack spacing={ 4 } alignItems="unset"> <VStack spacing={ 4 } alignItems="unset">
{ item.addresses_with_info.map((address) => <AddressSnippet key={ address.hash } address={ address } isLoading={ isLoading }/>) } { item.addresses_with_info.map((address) => (
<AddressEntity
key={ address.hash }
address={ address }
isLoading={ isLoading }
fontWeight="600"
py="2px"
/>
)) }
</VStack> </VStack>
</Td> </Td>
<Td> <Td>
......
...@@ -6,17 +6,16 @@ import type { SearchResultItem } from 'types/api/search'; ...@@ -6,17 +6,16 @@ import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import labelIcon from 'icons/publictags.svg'; import labelIcon from 'icons/publictags_slim.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import verifiedToken from 'icons/verified_token.svg'; import verifiedToken from 'icons/verified_token.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import Address from 'ui/shared/address/Address'; import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import * as BlockEntity from 'ui/shared/entities/block/BlockEntity'; import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import * as TxEntity from 'ui/shared/entities/tx/TxEntity'; import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
...@@ -24,7 +23,6 @@ import LinkInternal from 'ui/shared/LinkInternal'; ...@@ -24,7 +23,6 @@ import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils'; import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props { interface Props {
data: SearchResultItem | SearchResultAppItem; data: SearchResultItem | SearchResultAppItem;
...@@ -52,9 +50,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -52,9 +50,8 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
return ( return (
<Flex alignItems="center" overflow="hidden"> <Flex alignItems="center" overflow="hidden">
<TokenLogo boxSize={ 6 } data={ data } flexShrink={ 0 } isLoading={ isLoading }/> <TokenEntity.Icon token={ data } isLoading={ isLoading }/>
<LinkInternal <LinkInternal
ml={ 2 }
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
...@@ -79,14 +76,31 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -79,14 +76,31 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
case 'contract': case 'contract':
case 'address': { case 'address': {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase(); const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
const address = {
hash: data.address,
is_contract: data.type === 'contract',
is_verified: data.is_smart_contract_verified,
name: null,
implementation_name: null,
};
return ( return (
<Address> <AddressEntity.Container>
<AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} mr={ 2 } flexShrink={ 0 }/> <AddressEntity.Icon address={ address }/>
<Box as={ shouldHighlightHash ? 'mark' : 'span' } display="block" whiteSpace="nowrap" overflow="hidden"> <AddressEntity.Link
<AddressLink type="address" hash={ data.address } fontWeight={ 700 } display="block" w="100%" onClick={ handleLinkClick }/> address={ address }
</Box> onClick={ handleLinkClick }
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> } >
</Address> <AddressEntity.Content
asProp={ shouldHighlightHash ? 'mark' : 'span' }
address={ address }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 700 }
/>
</AddressEntity.Link>
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
); );
} }
......
...@@ -6,24 +6,22 @@ import type { SearchResultItem } from 'types/api/search'; ...@@ -6,24 +6,22 @@ import type { SearchResultItem } from 'types/api/search';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import labelIcon from 'icons/publictags.svg'; import labelIcon from 'icons/publictags_slim.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import verifiedToken from 'icons/verified_token.svg'; import verifiedToken from 'icons/verified_token.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords'; import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import Address from 'ui/shared/address/Address'; import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import * as BlockEntity from 'ui/shared/entities/block/BlockEntity'; import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import * as TxEntity from 'ui/shared/entities/tx/TxEntity'; import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import type { SearchResultAppItem } from 'ui/shared/search/utils'; import type { SearchResultAppItem } from 'ui/shared/search/utils';
import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils'; import { getItemCategory, searchItemTitles } from 'ui/shared/search/utils';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props { interface Props {
data: SearchResultItem | SearchResultAppItem; data: SearchResultItem | SearchResultAppItem;
...@@ -53,9 +51,8 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -53,9 +51,8 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
<> <>
<Td fontSize="sm"> <Td fontSize="sm">
<Flex alignItems="center"> <Flex alignItems="center">
<TokenLogo boxSize={ 6 } data={ data } flexShrink={ 0 } isLoading={ isLoading }/> <TokenEntity.Icon token={ data } isLoading={ isLoading }/>
<LinkInternal <LinkInternal
ml={ 2 }
href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) } href={ route({ pathname: '/token/[hash]', query: { hash: data.address } }) }
fontWeight={ 700 } fontWeight={ 700 }
wordBreak="break-all" wordBreak="break-all"
...@@ -98,25 +95,33 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -98,25 +95,33 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
case 'address': { case 'address': {
if (data.name) { if (data.name) {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase(); const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
const address = {
hash: data.address,
is_contract: data.type === 'contract',
is_verified: data.is_smart_contract_verified,
name: null,
implementation_name: null,
};
return ( return (
<> <>
<Td fontSize="sm"> <Td fontSize="sm">
<Flex alignItems="center" overflow="hidden"> <AddressEntity.Container>
<AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} mr={ 2 } flexShrink={ 0 }/> <AddressEntity.Icon address={ address }/>
<LinkInternal <AddressEntity.Link
href={ route({ pathname: '/address/[hash]', query: { hash: data.address } }) } address={ address }
fontWeight={ 700 }
overflow="hidden"
whiteSpace="nowrap"
onClick={ handleLinkClick } onClick={ handleLinkClick }
> >
<Box as={ shouldHighlightHash ? 'mark' : 'span' } display="block"> <AddressEntity.Content
<HashStringShortenDynamic hash={ data.address }/> asProp={ shouldHighlightHash ? 'mark' : 'span' }
</Box> address={ address }
</LinkInternal> fontSize="sm"
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> } lineHeight={ 5 }
</Flex> fontWeight={ 700 }
/>
</AddressEntity.Link>
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
</Td> </Td>
<Td colSpan={ 2 } fontSize="sm" verticalAlign="middle"> <Td colSpan={ 2 } fontSize="sm" verticalAlign="middle">
<span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: shouldHighlightHash ? xss(data.name) : highlightText(data.name, searchTerm) }}/>
...@@ -125,15 +130,32 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -125,15 +130,32 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
); );
} }
const address = {
hash: data.address,
is_contract: data.type === 'contract',
is_verified: data.is_smart_contract_verified,
name: null,
implementation_name: null,
};
return ( return (
<Td colSpan={ 3 } fontSize="sm"> <Td colSpan={ 3 } fontSize="sm">
<Address> <AddressEntity.Container>
<AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} mr={ 2 } flexShrink={ 0 }/> <AddressEntity.Icon address={ address }/>
<mark> <AddressEntity.Link
<AddressLink hash={ data.address } type="address" fontWeight={ 700 } onClick={ handleLinkClick }/> address={ address }
</mark> onClick={ handleLinkClick }
{ data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/> } >
</Address> <AddressEntity.Content
asProp="mark"
address={ address }
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 700 }
/>
</AddressEntity.Link>
<AddressEntity.Copy address={ address }/>
</AddressEntity.Container>
</Td> </Td>
); );
} }
......
...@@ -8,10 +8,8 @@ import config from 'configs/app'; ...@@ -8,10 +8,8 @@ import config from 'configs/app';
import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton'; import AddressFavoriteButton from 'ui/address/details/AddressFavoriteButton';
import AddressQrCode from 'ui/address/details/AddressQrCode'; import AddressQrCode from 'ui/address/details/AddressQrCode';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import AddressActionsMenu from 'ui/shared/AddressActions/Menu'; import AddressActionsMenu from 'ui/shared/AddressActions/Menu';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props { interface Props {
address: Pick<Address, 'hash' | 'is_contract' | 'implementation_name' | 'watchlist_names' | 'watchlist_address_id'>; address: Pick<Address, 'hash' | 'is_contract' | 'implementation_name' | 'watchlist_names' | 'watchlist_address_id'>;
...@@ -23,18 +21,14 @@ interface Props { ...@@ -23,18 +21,14 @@ interface Props {
const AddressHeadingInfo = ({ address, token, isLinkDisabled, isLoading }: Props) => { const AddressHeadingInfo = ({ address, token, isLinkDisabled, isLoading }: Props) => {
return ( return (
<Flex alignItems="center"> <Flex alignItems="center">
<AddressIcon address={ address } isLoading={ isLoading }/> <AddressEntity
<AddressLink address={{ ...address, name: '' }}
type="address" isLoading={ isLoading }
hash={ address.hash }
ml={ 2 }
fontFamily="heading" fontFamily="heading"
fontSize="lg" fontSize="lg"
fontWeight={ 500 } fontWeight={ 500 }
isDisabled={ isLinkDisabled } noLink={ isLinkDisabled }
isLoading={ isLoading }
/> />
<CopyToClipboard text={ address.hash } isLoading={ isLoading }/>
{ !isLoading && address.is_contract && token && <AddressAddToWallet ml={ 2 } token={ token }/> } { !isLoading && address.is_contract && token && <AddressAddToWallet ml={ 2 } token={ token }/> }
{ !isLoading && !address.is_contract && config.features.account.isEnabled && ( { !isLoading && !address.is_contract && config.features.account.isEnabled && (
<AddressFavoriteButton hash={ address.hash } watchListId={ address.watchlist_address_id } ml={ 3 }/> <AddressFavoriteButton hash={ address.hash } watchListId={ address.watchlist_address_id } ml={ 3 }/>
......
import { Box, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { AddressParam } from 'types/api/addressParams';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
interface Props {
address: Pick<AddressParam, 'hash' | 'is_contract' | 'implementation_name'>;
subtitle?: string;
isLoading?: boolean;
}
const AddressSnippet = ({ address, subtitle, isLoading }: Props) => {
return (
<Box maxW="100%">
<Address>
<AddressIcon address={ address } isLoading={ isLoading }/>
<AddressLink type="address" hash={ address.hash } fontWeight="600" ml={ 2 } isLoading={ isLoading }/>
<CopyToClipboard text={ address.hash } isLoading={ isLoading }/>
</Address>
{ subtitle && (
<Skeleton fontSize="sm" color="text_secondary" mt={ 0.5 } ml={ 8 } display="inline-block" isLoaded={ !isLoading }>
<span>{ subtitle }</span>
</Skeleton>
) }
</Box>
);
};
export default React.memo(AddressSnippet);
...@@ -24,7 +24,7 @@ const CopyToClipboard = ({ text, className, isLoading }: Props) => { ...@@ -24,7 +24,7 @@ const CopyToClipboard = ({ text, className, isLoading }: Props) => {
}, [ hasCopied ]); }, [ hasCopied ]);
if (isLoading) { if (isLoading) {
return <Skeleton boxSize={ 5 } className={ className } borderRadius="sm" flexShrink={ 0 } ml={ 1 }/>; return <Skeleton boxSize={ 5 } className={ className } borderRadius="sm" flexShrink={ 0 } ml={ 2 }/>;
} }
return ( return (
...@@ -42,7 +42,8 @@ const CopyToClipboard = ({ text, className, isLoading }: Props) => { ...@@ -42,7 +42,8 @@ const CopyToClipboard = ({ text, className, isLoading }: Props) => {
className={ className } className={ className }
onMouseEnter={ onOpen } onMouseEnter={ onOpen }
onMouseLeave={ onClose } onMouseLeave={ onClose }
ml={ 1 } ml={ 2 }
borderRadius={ 0 }
/> />
</Tooltip> </Tooltip>
); );
......
...@@ -5,9 +5,9 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -5,9 +5,9 @@ import type { TokenInfo } from 'types/api/token';
import iconVerifiedToken from 'icons/verified_token.svg'; import iconVerifiedToken from 'icons/verified_token.svg';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import TokenLogo from 'ui/shared/TokenLogo';
import PageTitle from '../PageTitle'; import PageTitle from '../PageTitle';
...@@ -49,7 +49,10 @@ const DefaultView = () => { ...@@ -49,7 +49,10 @@ const DefaultView = () => {
<PageTitle <PageTitle
title="Shavukha Token (SHVKH) token" title="Shavukha Token (SHVKH) token"
beforeTitle={ ( beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/> <TokenEntity.Icon
token={ tokenData }
iconSize="lg"
/>
) } ) }
backLink={ backLink } backLink={ backLink }
contentAfter={ contentAfter } contentAfter={ contentAfter }
......
...@@ -7,9 +7,9 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -7,9 +7,9 @@ import type { TokenInfo } from 'types/api/token';
import iconVerifiedToken from 'icons/verified_token.svg'; import iconVerifiedToken from 'icons/verified_token.svg';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import { publicTag, privateTag, watchlistName } from 'mocks/address/tag'; import { publicTag, privateTag, watchlistName } from 'mocks/address/tag';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import TokenLogo from 'ui/shared/TokenLogo';
import PageTitle from '../PageTitle'; import PageTitle from '../PageTitle';
...@@ -57,7 +57,10 @@ const LongNameAndManyTags = () => { ...@@ -57,7 +57,10 @@ const LongNameAndManyTags = () => {
<PageTitle <PageTitle
title={ `${ tokenData?.name }${ tokenSymbolText } token` } title={ `${ tokenData?.name }${ tokenSymbolText } token` }
beforeTitle={ ( beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } mr={ 2 }/> <TokenEntity.Icon
token={ tokenData }
iconSize="lg"
/>
) } ) }
contentAfter={ contentAfter } contentAfter={ contentAfter }
/> />
......
import { Image, chakra, Skeleton } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
export interface Props {
data?: Pick<TokenInfo, | 'icon_url' | 'name'>;
className?: string;
isLoading?: boolean;
}
const TokenLogo = ({ className, isLoading, data }: Props) => {
if (isLoading) {
return <Skeleton className={ className } borderRadius="base" flexShrink={ 0 }/>;
}
return (
<Image
borderRadius="base"
className={ className }
src={ data?.icon_url ?? undefined }
alt={ `${ data?.name || 'token' } logo` }
fallback={ <TokenLogoPlaceholder className={ className }/> }
/>
);
};
export default React.memo(chakra(TokenLogo));
import { test, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import TestApp from 'playwright/TestApp';
import TokenSnippet from './TokenSnippet';
test.use(devices['iPhone 13 Pro']);
test('unnamed', async({ mount }) => {
const data: TokenInfo = {
address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
circulating_market_cap: '117629601.61913824',
type: 'ERC-20',
symbol: 'xDAI',
name: null,
decimals: '18',
holders: '1',
exchange_rate: null,
total_supply: null,
icon_url: null,
};
const component = await mount(
<TestApp>
<TokenSnippet data={ data }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('named', async({ mount }) => {
const data: TokenInfo = {
address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
circulating_market_cap: '117629601.61913824',
type: 'ERC-20',
symbol: 'SHA',
name: 'Shavuha token',
decimals: '18',
holders: '1',
exchange_rate: null,
total_supply: null,
icon_url: null,
};
const component = await mount(
<TestApp>
<TokenSnippet data={ data }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('with logo and long symbol', async({ mount, page }) => {
const API_URL = 'https://example.com/logo.png';
const data: TokenInfo = {
address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
circulating_market_cap: '117629601.61913824',
type: 'ERC-20',
symbol: 'SHAAAAAAAAAAAAA',
name: null,
decimals: '18',
holders: '1',
exchange_rate: null,
total_supply: null,
icon_url: API_URL,
};
await page.route(API_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/mocks/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<TokenSnippet data={ data }/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import type { StyleProps } from '@chakra-ui/react';
import { Flex, chakra, Skeleton, shouldForwardProp } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import AddressLink from 'ui/shared/address/AddressLink';
import TokenLogo from 'ui/shared/TokenLogo';
import TruncatedValue from 'ui/shared/TruncatedValue';
interface Props {
data?: Pick<TokenInfo, 'address' | 'icon_url' | 'name' | 'symbol'>;
className?: string;
logoSize?: number;
isDisabled?: boolean;
isLoading?: boolean;
hideSymbol?: boolean;
hideIcon?: boolean;
maxW: StyleProps['maxW'];
}
/**
* @deprecated use `ui/shared/entities/token/**` instead
*/
const TokenSnippet = ({ data, className, logoSize = 6, isDisabled, hideSymbol, hideIcon, isLoading, maxW }: Props) => {
const withSymbol = data && data.symbol && !hideSymbol;
const columnGap = 2;
return (
<Flex className={ className } alignItems="center" columnGap={ columnGap } w="100%" overflow="hidden">
{ !hideIcon && <TokenLogo boxSize={ logoSize } data={ data } isLoading={ isLoading }/> }
<AddressLink
flexShrink={ 0 }
hash={ data?.address || '' }
alias={ data?.name || 'Unnamed token' }
type="token"
isDisabled={ isDisabled }
isLoading={ isLoading }
maxW={ withSymbol ?
`calc(80% - ${ (logoSize + columnGap * 2) * 4 }px)` :
`calc(${ maxW || '100%' } - ${ (logoSize + columnGap) * 4 }px)`
}
overflow="hidden"
textOverflow="ellipsis"
/>
{ withSymbol && (
<Skeleton isLoaded={ !isLoading } color="text_secondary" maxW="20%" display="flex">
<div>(</div>
<TruncatedValue value={ data.symbol } display="block" wordBreak="break-all"/>
<div>)</div>
</Skeleton>
) }
</Flex>
);
};
export default chakra(TokenSnippet, {
shouldForwardProp: (prop) => {
const isChakraProp = !shouldForwardProp(prop);
if (isChakraProp && prop !== 'maxW') {
return false;
}
return true;
},
});
...@@ -6,16 +6,13 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; ...@@ -6,16 +6,13 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
...@@ -55,7 +52,13 @@ const TokenTransferListItem = ({ ...@@ -55,7 +52,13 @@ const TokenTransferListItem = ({
<ListItemMobile rowGap={ 3 } isAnimated> <ListItemMobile rowGap={ 3 } isAnimated>
<Flex w="100%" justifyContent="space-between"> <Flex w="100%" justifyContent="space-between">
<Flex flexWrap="wrap" rowGap={ 1 } mr={ showTxInfo && txHash ? 2 : 0 } columnGap={ 2 } overflow="hidden"> <Flex flexWrap="wrap" rowGap={ 1 } mr={ showTxInfo && txHash ? 2 : 0 } columnGap={ 2 } overflow="hidden">
<TokenSnippet data={ token } w="auto" hideSymbol isLoading={ isLoading }/> <TokenEntity
token={ token }
isLoading={ isLoading }
noSymbol
noCopy
w="auto"
/>
<Tag flexShrink={ 0 } isLoading={ isLoading }>{ token.type }</Tag> <Tag flexShrink={ 0 } isLoading={ isLoading }>{ token.type }</Tag>
<Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag> <Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex> </Flex>
...@@ -80,11 +83,14 @@ const TokenTransferListItem = ({ ...@@ -80,11 +83,14 @@ const TokenTransferListItem = ({
</Flex> </Flex>
) } ) }
<Flex w="100%" columnGap={ 3 }> <Flex w="100%" columnGap={ 3 }>
<Address width={ addressWidth } flexShrink={ 0 }> <AddressEntity
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isDisabled={ baseAddress === from.hash } isLoading={ isLoading }/> isLoading={ isLoading }
{ baseAddress !== from.hash && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> } noLink={ baseAddress === from.hash }
</Address> noCopy={ baseAddress === from.hash }
flexShrink={ 0 }
width={ addressWidth }
/>
{ baseAddress ? ( { baseAddress ? (
<InOutTag <InOutTag
isIn={ baseAddress === to.hash } isIn={ baseAddress === to.hash }
...@@ -97,11 +103,14 @@ const TokenTransferListItem = ({ ...@@ -97,11 +103,14 @@ const TokenTransferListItem = ({
) : ) :
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading } flexShrink={ 0 }/> <Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading } flexShrink={ 0 }/>
} }
<Address width={ addressWidth } flexShrink={ 0 }> <AddressEntity
<AddressIcon address={ to } isLoading={ isLoading }/> address={ to }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ to.hash } isDisabled={ baseAddress === to.hash } isLoading={ isLoading }/> isLoading={ isLoading }
{ baseAddress !== to.hash && <CopyToClipboard text={ to.hash } isLoading={ isLoading }/> } noLink={ baseAddress === to.hash }
</Address> noCopy={ baseAddress === to.hash }
flexShrink={ 0 }
width={ addressWidth }
/>
</Flex> </Flex>
{ value && ( { value && (
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
......
...@@ -5,14 +5,11 @@ import React from 'react'; ...@@ -5,14 +5,11 @@ import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer'; import type { TokenTransfer } from 'types/api/tokenTransfer';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers'; import { getTokenTransferTypeText } from 'ui/shared/TokenTransfer/helpers';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
...@@ -50,7 +47,13 @@ const TokenTransferTableItem = ({ ...@@ -50,7 +47,13 @@ const TokenTransferTableItem = ({
) } ) }
<Td> <Td>
<Flex flexDir="column" alignItems="flex-start" my="3px" rowGap={ 2 }> <Flex flexDir="column" alignItems="flex-start" my="3px" rowGap={ 2 }>
<TokenSnippet data={ token } isLoading={ isLoading } hideSymbol/> <TokenEntity
token={ token }
isLoading={ isLoading }
noSymbol
noCopy
my="2px"
/>
<Tag isLoading={ isLoading }>{ token.type }</Tag> <Tag isLoading={ isLoading }>{ token.type }</Tag>
<Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag> <Tag colorScheme="orange" isLoading={ isLoading }>{ getTokenTransferTypeText(type) }</Tag>
</Flex> </Flex>
...@@ -75,19 +78,14 @@ const TokenTransferTableItem = ({ ...@@ -75,19 +78,14 @@ const TokenTransferTableItem = ({
</Td> </Td>
) } ) }
<Td> <Td>
<Address display="inline-flex" maxW="100%" my="3px"> <AddressEntity
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink
type="address" ml={ 2 }
fontWeight="500"
hash={ from.hash }
alias={ from.name }
flexGrow={ 1 }
isDisabled={ baseAddress === from.hash }
isLoading={ isLoading } isLoading={ isLoading }
my="5px"
noLink={ baseAddress === from.hash }
noCopy={ baseAddress === from.hash }
flexGrow={ 1 }
/> />
{ baseAddress !== from.hash && <CopyToClipboard text={ from.hash } isLoading={ isLoading }/> }
</Address>
</Td> </Td>
{ baseAddress && ( { baseAddress && (
<Td px={ 0 }> <Td px={ 0 }>
...@@ -103,20 +101,14 @@ const TokenTransferTableItem = ({ ...@@ -103,20 +101,14 @@ const TokenTransferTableItem = ({
</Td> </Td>
) } ) }
<Td> <Td>
<Address display="inline-flex" maxW="100%" my="3px"> <AddressEntity
<AddressIcon address={ to } isLoading={ isLoading }/> address={ to }
<AddressLink
type="address"
ml={ 2 }
fontWeight="500"
hash={ to.hash }
alias={ to.name }
flexGrow={ 1 }
isDisabled={ baseAddress === to.hash }
isLoading={ isLoading } isLoading={ isLoading }
my="5px"
noLink={ baseAddress === to.hash }
noCopy={ baseAddress === to.hash }
flexGrow={ 1 }
/> />
{ baseAddress !== to.hash && <CopyToClipboard text={ to.hash } isLoading={ isLoading }/> }
</Address>
</Td> </Td>
<Td isNumeric verticalAlign="top"> <Td isNumeric verticalAlign="top">
<Skeleton isLoaded={ !isLoading } display="inline-block" my="7px"> <Skeleton isLoaded={ !isLoading } display="inline-block" my="7px">
......
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
interface Props {
className?: string;
children: React.ReactNode;
}
/**
* @deprecated use `ui/shared/entities/address` instead
*/
const Address = ({ children, className }: Props) => {
return <Flex alignItems="center" overflow="hidden" className={ className }>{ children }</Flex>;
};
const AddressChakra = chakra(Address);
export default React.memo(AddressChakra);
import { Box, chakra, Tooltip, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
type Props = {
className?: string;
}
/**
* @deprecated use `ui/shared/entities/**` instead
*/
const AddressContractIcon = ({ className }: Props) => {
const bgColor = useColorModeValue('gray.200', 'gray.600');
const color = useColorModeValue('gray.400', 'gray.200');
return (
<Tooltip label="Contract">
<Box
className={ className }
width="24px"
minWidth="24px"
height="24px"
borderRadius="12px"
backgroundColor={ bgColor }
color={ color }
display="inline-flex"
alignItems="center"
justifyContent="center"
fontWeight="700"
transitionProperty="background-color,color"
transitionDuration="normal"
transitionTimingFunction="ease"
>
C
</Box>
</Tooltip>
);
};
export default chakra(AddressContractIcon);
import { Box, chakra, Skeleton, Tooltip } from '@chakra-ui/react';
import React from 'react';
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon';
import type { AddressParam } from 'types/api/addressParams';
import AddressContractIcon from 'ui/shared/address/AddressContractIcon';
type Props = {
address: Pick<AddressParam, 'hash' | 'is_contract' | 'implementation_name'>;
className?: string;
isLoading?: boolean;
}
/**
* @deprecated use `ui/shared/entities/**` instead
*/
const AddressIcon = ({ address, className, isLoading }: Props) => {
if (isLoading) {
return <Skeleton boxSize={ 6 } className={ className } borderRadius="full" flexShrink={ 0 }/>;
}
if (address.is_contract) {
return (
<AddressContractIcon className={ className }/>
);
}
return (
<Tooltip label={ address.implementation_name }>
<Box className={ className } boxSize={ 6 } display="inline-flex">
<Jazzicon diameter={ 24 } seed={ jsNumberForAddress(address.hash) }/>
</Box>
</Tooltip>
);
};
export default chakra(AddressIcon);
import { chakra, shouldForwardProp, Tooltip, Box, Skeleton } from '@chakra-ui/react';
import type { HTMLAttributeAnchorTarget } from 'react';
import React from 'react';
import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile';
import HashStringShorten from 'ui/shared/HashStringShorten';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkInternal from 'ui/shared/LinkInternal';
import TruncatedValue from 'ui/shared/TruncatedValue';
type CommonProps = {
className?: string;
truncation?: 'constant' | 'dynamic'| 'none';
target?: HTMLAttributeAnchorTarget;
isDisabled?: boolean;
fontWeight?: string;
alias?: string | null;
isLoading?: boolean;
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
query?: Record<string, string>;
tailLength?: number;
}
type AddressTokenTxProps = {
type: 'address' | 'token';
hash: 'hash';
}
type AddressTokenProps = {
type: 'address_token';
hash: string;
tokenHash: string;
}
type Props = CommonProps & (AddressTokenTxProps | AddressTokenProps);
/**
* @deprecated use `ui/shared/entities/**` instead
*/
const AddressLink = (props: Props) => {
const { alias, type, className, truncation = 'dynamic', hash, fontWeight, target = '_self', isDisabled, isLoading } = props;
const isMobile = useIsMobile();
let url;
if (type === 'token') {
url = route({ pathname: '/token/[hash]', query: { ...props.query, hash } });
} else if (type === 'address_token') {
url = route({ pathname: '/address/[hash]', query: { ...props.query, hash, tab: 'token_transfers', token: props.tokenHash, scroll_to_tabs: 'true' } });
} else {
url = route({ pathname: '/address/[hash]', query: { ...props.query, hash } });
}
const content = (() => {
if (alias) {
const text = <Box overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">{ alias }</Box>;
if (type === 'token' || type === 'address_token') {
return <TruncatedValue value={ alias } display="block"/>;
}
return (
<Tooltip label={ hash } isDisabled={ isMobile }>
{ text }
</Tooltip>
);
}
switch (truncation) {
case 'constant':
return <HashStringShorten hash={ hash } isTooltipDisabled={ isMobile }/>;
case 'dynamic':
return <HashStringShortenDynamic hash={ hash } fontWeight={ fontWeight } isTooltipDisabled={ isMobile } tailLength={ props.tailLength }/>;
case 'none':
return <span>{ hash }</span>;
}
})();
if (isLoading) {
return <Skeleton className={ className } overflow="hidden" whiteSpace="nowrap">{ content }</Skeleton>;
}
if (isDisabled) {
return (
<chakra.span
className={ className }
overflow="hidden"
whiteSpace="nowrap"
>
{ content }
</chakra.span>
);
}
return (
<LinkInternal
className={ className }
href={ url }
target={ target }
overflow="hidden"
whiteSpace="nowrap"
onClick={ props.onClick }
>
{ content }
</LinkInternal>
);
};
const AddressLinkChakra = chakra(AddressLink, {
shouldForwardProp: (prop) => {
const isChakraProp = !shouldForwardProp(prop);
// forward fontWeight to the AddressLink since it's needed for underlying HashStringShortenDynamic component
if (isChakraProp && prop !== 'fontWeight') {
return false;
}
return true;
},
});
export default React.memo(AddressLinkChakra);
...@@ -29,11 +29,15 @@ const Link = chakra((props: LinkProps) => { ...@@ -29,11 +29,15 @@ const Link = chakra((props: LinkProps) => {
); );
}); });
type IconProps = Pick<EntityProps, 'address' | 'isLoading' | 'iconSize'> & { type IconProps = Pick<EntityProps, 'address' | 'isLoading' | 'iconSize' | 'noIcon'> & {
asProp?: As; asProp?: As;
}; };
const Icon = (props: IconProps) => { const Icon = (props: IconProps) => {
if (props.noIcon) {
return null;
}
const styles = { const styles = {
...getIconProps(props.iconSize), ...getIconProps(props.iconSize),
marginRight: 2, marginRight: 2,
...@@ -46,19 +50,29 @@ const Icon = (props: IconProps) => { ...@@ -46,19 +50,29 @@ const Icon = (props: IconProps) => {
if (props.address.is_contract) { if (props.address.is_contract) {
if (props.address.is_verified) { if (props.address.is_verified) {
return ( return (
<Tooltip label="Verified contract">
<span>
<EntityBase.Icon <EntityBase.Icon
{ ...props } { ...props }
asProp={ iconContractVerified } asProp={ iconContractVerified }
color="green.500" color="green.500"
borderRadius={ 0 }
/> />
</span>
</Tooltip>
); );
} }
return ( return (
<Tooltip label="Contract">
<span>
<EntityBase.Icon <EntityBase.Icon
{ ...props } { ...props }
asProp={ iconContract } asProp={ iconContract }
borderRadius={ 0 }
/> />
</span>
</Tooltip>
); );
} }
......
import { chakra } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
import config from 'configs/app';
import * as AddressEntity from './AddressEntity';
const feature = config.features.rollup;
const AddressEntityL1 = (props: AddressEntity.EntityProps) => {
if (!feature.isEnabled) {
return null;
}
const defaultHref = feature.L1BaseUrl + route({
pathname: '/address/[hash]',
query: {
...props.query,
hash: props.address.hash,
},
});
return (
<AddressEntity.default { ...props } href={ props.href ?? defaultHref } isExternal/>
);
};
export default chakra(AddressEntityL1);
import { chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import _omit from 'lodash/omit';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
...@@ -11,8 +10,6 @@ interface Props extends AddressEntity.EntityProps { ...@@ -11,8 +10,6 @@ interface Props extends AddressEntity.EntityProps {
} }
const AddressEntityWithTokenFilter = (props: Props) => { const AddressEntityWithTokenFilter = (props: Props) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
const defaultHref = route({ const defaultHref = route({
pathname: '/address/[hash]', pathname: '/address/[hash]',
query: { query: {
...@@ -25,15 +22,7 @@ const AddressEntityWithTokenFilter = (props: Props) => { ...@@ -25,15 +22,7 @@ const AddressEntityWithTokenFilter = (props: Props) => {
}); });
return ( return (
<AddressEntity.Container className={ props.className }> <AddressEntity.default { ...props } href={ props.href ?? defaultHref }/>
<AddressEntity.Icon { ...partsProps }/>
<AddressEntity.Link
{ ...linkProps }
href={ props.href ?? defaultHref }
>
<AddressEntity.Content { ...partsProps }/>
</AddressEntity.Link>
</AddressEntity.Container>
); );
}; };
......
...@@ -26,6 +26,7 @@ export interface EntityBaseProps { ...@@ -26,6 +26,7 @@ export interface EntityBaseProps {
onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void; onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void;
query?: Record<string, string>; query?: Record<string, string>;
tailLength?: number; tailLength?: number;
target?: React.HTMLAttributeAnchorTarget;
truncation?: Truncation; truncation?: Truncation;
} }
...@@ -77,9 +78,10 @@ const Link = chakra(({ isLoading, children, isExternal, onClick, href, noLink }: ...@@ -77,9 +78,10 @@ const Link = chakra(({ isLoading, children, isExternal, onClick, href, noLink }:
export interface IconBaseProps extends Pick<EntityBaseProps, 'isLoading' | 'iconSize' | 'noIcon'> { export interface IconBaseProps extends Pick<EntityBaseProps, 'isLoading' | 'iconSize' | 'noIcon'> {
asProp: As; asProp: As;
color?: IconProps['color']; color?: IconProps['color'];
borderRadius?: IconProps['borderRadius'];
} }
const Icon = ({ isLoading, iconSize, noIcon, asProp, color }: IconBaseProps) => { const Icon = ({ isLoading, iconSize, noIcon, asProp, color, borderRadius }: IconBaseProps) => {
const defaultColor = useColorModeValue('gray.500', 'gray.400'); const defaultColor = useColorModeValue('gray.500', 'gray.400');
if (noIcon) { if (noIcon) {
...@@ -93,7 +95,7 @@ const Icon = ({ isLoading, iconSize, noIcon, asProp, color }: IconBaseProps) => ...@@ -93,7 +95,7 @@ const Icon = ({ isLoading, iconSize, noIcon, asProp, color }: IconBaseProps) =>
as={ asProp } as={ asProp }
boxSize={ styles.boxSize } boxSize={ styles.boxSize }
isLoading={ isLoading } isLoading={ isLoading }
borderRadius="base" borderRadius={ borderRadius ?? 'base' }
/> />
</Box> </Box>
); );
......
import type { As } from '@chakra-ui/react'; import type { As, ChakraProps } from '@chakra-ui/react';
import { Image, Skeleton, chakra } from '@chakra-ui/react'; import { Image, Skeleton, chakra } from '@chakra-ui/react';
import _omit from 'lodash/omit'; import _omit from 'lodash/omit';
import React from 'react'; import React from 'react';
...@@ -28,8 +28,10 @@ const Link = chakra((props: LinkProps) => { ...@@ -28,8 +28,10 @@ const Link = chakra((props: LinkProps) => {
); );
}); });
type IconProps = Pick<EntityProps, 'token' | 'isLoading' | 'iconSize' | 'noIcon'> & { type IconProps = Pick<EntityProps, 'token' | 'isLoading' | 'iconSize' | 'noIcon' | 'className'> & {
asProp?: As; asProp?: As;
marginRight?: ChakraProps['marginRight'];
boxSize?: ChakraProps['boxSize'];
}; };
const Icon = (props: IconProps) => { const Icon = (props: IconProps) => {
...@@ -38,18 +40,19 @@ const Icon = (props: IconProps) => { ...@@ -38,18 +40,19 @@ const Icon = (props: IconProps) => {
} }
const styles = { const styles = {
...getIconProps(props.iconSize), marginRight: props.marginRight ?? 2,
marginRight: 2, boxSize: props.boxSize ?? getIconProps(props.iconSize).boxSize,
borderRadius: 'base', borderRadius: 'base',
}; };
if (props.isLoading) { if (props.isLoading) {
return <Skeleton { ...styles } flexShrink={ 0 }/>; return <Skeleton { ...styles } className={ props.className } flexShrink={ 0 }/>;
} }
return ( return (
<Image <Image
{ ...styles } { ...styles }
className={ props.className }
src={ props.token.icon_url ?? undefined } src={ props.token.icon_url ?? undefined }
alt={ `${ props.token.name || 'token' } logo` } alt={ `${ props.token.name || 'token' } logo` }
fallback={ <TokenLogoPlaceholder { ...styles }/> } fallback={ <TokenLogoPlaceholder { ...styles }/> }
...@@ -57,13 +60,17 @@ const Icon = (props: IconProps) => { ...@@ -57,13 +60,17 @@ const Icon = (props: IconProps) => {
); );
}; };
type ContentProps = Omit<EntityBase.ContentBaseProps, 'text'> & Pick<EntityProps, 'token'>; type ContentProps = Omit<EntityBase.ContentBaseProps, 'text'> & Pick<EntityProps, 'token' | 'jointSymbol' | 'onlySymbol'>;
const Content = chakra((props: ContentProps) => { const Content = chakra((props: ContentProps) => {
const name = props.token.name ?? 'Unnamed token'; const nameString = [
!props.onlySymbol && (props.token.name ?? 'Unnamed token'),
props.onlySymbol && (props.token.symbol ?? ''),
props.token.symbol && props.jointSymbol && !props.onlySymbol && `(${ props.token.symbol })`,
].filter(Boolean).join(' ');
return ( return (
<TruncatedTextTooltip label={ name }> <TruncatedTextTooltip label={ nameString }>
<Skeleton <Skeleton
isLoaded={ !props.isLoading } isLoaded={ !props.isLoading }
display="inline-block" display="inline-block"
...@@ -72,18 +79,18 @@ const Content = chakra((props: ContentProps) => { ...@@ -72,18 +79,18 @@ const Content = chakra((props: ContentProps) => {
textOverflow="ellipsis" textOverflow="ellipsis"
height="fit-content" height="fit-content"
> >
{ name } { nameString }
</Skeleton> </Skeleton>
</TruncatedTextTooltip> </TruncatedTextTooltip>
); );
}); });
type SymbolProps = Pick<EntityProps, 'token' | 'isLoading' | 'noSymbol'>; type SymbolProps = Pick<EntityProps, 'token' | 'isLoading' | 'noSymbol' | 'jointSymbol' | 'onlySymbol'>;
const Symbol = (props: SymbolProps) => { const Symbol = (props: SymbolProps) => {
const symbol = props.token.symbol; const symbol = props.token.symbol;
if (!symbol || props.noSymbol) { if (!symbol || props.noSymbol || props.jointSymbol || props.onlySymbol) {
return null; return null;
} }
...@@ -129,6 +136,8 @@ const Container = EntityBase.Container; ...@@ -129,6 +136,8 @@ const Container = EntityBase.Container;
export interface EntityProps extends EntityBase.EntityBaseProps { export interface EntityProps extends EntityBase.EntityBaseProps {
token: Pick<TokenInfo, 'address' | 'icon_url' | 'name' | 'symbol'>; token: Pick<TokenInfo, 'address' | 'icon_url' | 'name' | 'symbol'>;
noSymbol?: boolean; noSymbol?: boolean;
jointSymbol?: boolean;
onlySymbol?: boolean;
} }
const TokenEntity = (props: EntityProps) => { const TokenEntity = (props: EntityProps) => {
......
import { chakra } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
import * as TokenEntity from './TokenEntity';
interface Props extends TokenEntity.EntityProps {
addressHash: string;
}
const TokenEntityWithAddressFilter = (props: Props) => {
const defaultHref = route({
pathname: '/address/[hash]',
query: {
...props.query,
hash: props.addressHash,
tab: 'token_transfers',
token: props.token.address,
scroll_to_tabs: 'true',
},
});
return (
<TokenEntity.default { ...props } href={ props.href ?? defaultHref }/>
);
};
export default chakra(TokenEntityWithAddressFilter);
...@@ -4,9 +4,8 @@ import React from 'react'; ...@@ -4,9 +4,8 @@ import React from 'react';
import type { DecodedInput } from 'types/api/decodedInput'; import type { DecodedInput } from 'types/api/decodedInput';
import type { ArrayElement } from 'types/utils'; import type { ArrayElement } from 'types/utils';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TruncatedValue from 'ui/shared/TruncatedValue'; import TruncatedValue from 'ui/shared/TruncatedValue';
interface Props { interface Props {
...@@ -33,10 +32,10 @@ const Row = ({ name, type, indexed, value, isLoading }: ArrayElement<DecodedInpu ...@@ -33,10 +32,10 @@ const Row = ({ name, type, indexed, value, isLoading }: ArrayElement<DecodedInpu
const content = (() => { const content = (() => {
if (type === 'address' && typeof value === 'string') { if (type === 'address' && typeof value === 'string') {
return ( return (
<Address justifyContent="space-between"> <AddressEntity
<AddressLink type="address" hash={ value } isLoading={ isLoading }/> address={{ hash: value, name: '', implementation_name: null, is_contract: false, is_verified: false }}
<CopyToClipboard text={ value } isLoading={ isLoading }/> isLoading={ isLoading }
</Address> />
); );
} }
......
...@@ -7,9 +7,8 @@ import { route } from 'nextjs-routes'; ...@@ -7,9 +7,8 @@ import { route } from 'nextjs-routes';
// import searchIcon from 'icons/search.svg'; // import searchIcon from 'icons/search.svg';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import AddressLink from 'ui/shared/address/AddressLink';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import LogTopic from 'ui/shared/logs/LogTopic'; import LogTopic from 'ui/shared/logs/LogTopic';
...@@ -54,15 +53,19 @@ const LogItem = ({ address, index, topics, data, decoded, type, tx_hash: txHash, ...@@ -54,15 +53,19 @@ const LogItem = ({ address, index, topics, data, decoded, type, tx_hash: txHash,
) } ) }
{ hasTxInfo ? <RowHeader isLoading={ isLoading }>Transaction</RowHeader> : <RowHeader isLoading={ isLoading }>Address</RowHeader> } { hasTxInfo ? <RowHeader isLoading={ isLoading }>Transaction</RowHeader> : <RowHeader isLoading={ isLoading }>Address</RowHeader> }
<GridItem display="flex" alignItems="center"> <GridItem display="flex" alignItems="center">
<Address mr={{ base: 9, lg: 0 }}> { type === 'address' ? (
{ !hasTxInfo && <AddressIcon address={ address } mr={ 2 } isLoading={ isLoading }/> } <TxEntity
<AddressLink hash={ txHash }
hash={ hasTxInfo ? txHash : address.hash }
alias={ hasTxInfo ? undefined : address.name }
type={ type === 'address' ? 'transaction' : 'address' }
isLoading={ isLoading } isLoading={ isLoading }
mr={{ base: 9, lg: 4 }}
/> />
</Address> ) : (
<AddressEntity
address={ address }
isLoading={ isLoading }
mr={{ base: 9, lg: 4 }}
/>
) }
{ /* api doesn't have find topic feature yet */ } { /* api doesn't have find topic feature yet */ }
{ /* <Tooltip label="Find matches topic"> { /* <Tooltip label="Find matches topic">
<Link ml={ 2 } mr={{ base: 9, lg: 0 }} display="inline-flex"> <Link ml={ 2 } mr={{ base: 9, lg: 0 }} display="inline-flex">
......
...@@ -4,9 +4,8 @@ import React from 'react'; ...@@ -4,9 +4,8 @@ import React from 'react';
import hexToAddress from 'lib/hexToAddress'; import hexToAddress from 'lib/hexToAddress';
import hexToUtf8 from 'lib/hexToUtf8'; import hexToUtf8 from 'lib/hexToUtf8';
import Address from 'ui/shared/address/Address';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props { interface Props {
...@@ -51,10 +50,10 @@ const LogTopic = ({ hex, index, isLoading }: Props) => { ...@@ -51,10 +50,10 @@ const LogTopic = ({ hex, index, isLoading }: Props) => {
case 'address': { case 'address': {
return ( return (
<Address> <AddressEntity
<AddressLink type="address" hash={ value } isLoading={ isLoading }/> address={{ hash: value, name: '', implementation_name: null, is_contract: false, is_verified: false }}
<CopyToClipboard text={ value } isLoading={ isLoading }/> isLoading={ isLoading }
</Address> />
); );
} }
} }
......
import { Box, Text, Grid, Flex, Icon } from '@chakra-ui/react'; import { Box, Text, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SearchResultAddressOrContract } from 'types/api/search'; import type { SearchResultAddressOrContract } from 'types/api/search';
import iconSuccess from 'icons/status/success.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import AddressIcon from 'ui/shared/address/AddressIcon'; import * as AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props { interface Props {
...@@ -16,7 +15,11 @@ interface Props { ...@@ -16,7 +15,11 @@ interface Props {
const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => { const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase(); const shouldHighlightHash = data.address.toLowerCase() === searchTerm.toLowerCase();
const icon = <AddressIcon address={{ hash: data.address, is_contract: data.type === 'contract', implementation_name: null }} flexShrink={ 0 }/>; const icon = (
<AddressEntity.Icon
address={{ hash: data.address, is_contract: data.type === 'contract', name: '', is_verified: data.is_smart_contract_verified, implementation_name: null }}
/>
);
const name = data.name && ( const name = data.name && (
<Text <Text
variant="secondary" variant="secondary"
...@@ -27,15 +30,13 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => { ...@@ -27,15 +30,13 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
<span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/> <span dangerouslySetInnerHTML={{ __html: highlightText(data.name, searchTerm) }}/>
</Text> </Text>
); );
const isContractVerified = data.is_smart_contract_verified && <Icon as={ iconSuccess } color="green.500" ml={ 1 }/>;
const address = <HashStringShortenDynamic hash={ data.address } isTooltipDisabled/>; const address = <HashStringShortenDynamic hash={ data.address } isTooltipDisabled/>;
if (isMobile) { if (isMobile) {
return ( return (
<> <>
<Grid templateColumns="24px 1fr" gap={ 2 }> <Flex alignItems="center">
{ icon } { icon }
<Flex alignItems="center" overflow="hidden">
<Box <Box
as={ shouldHighlightHash ? 'mark' : 'span' } as={ shouldHighlightHash ? 'mark' : 'span' }
display="block" display="block"
...@@ -45,18 +46,16 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => { ...@@ -45,18 +46,16 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
> >
{ address } { address }
</Box> </Box>
{ isContractVerified }
</Flex> </Flex>
</Grid>
{ name } { name }
</> </>
); );
} }
return ( return (
<Flex alignItems="center" gap={ 2 }> <Flex alignItems="center">
<Flex alignItems="center" w="450px" mr={ 2 }>
{ icon } { icon }
<Flex alignItems="center" w="450px" overflow="hidden">
<Box <Box
as={ shouldHighlightHash ? 'mark' : 'span' } as={ shouldHighlightHash ? 'mark' : 'span' }
display="block" display="block"
...@@ -66,7 +65,6 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => { ...@@ -66,7 +65,6 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm }: Props) => {
> >
{ address } { address }
</Box> </Box>
{ isContractVerified }
</Flex> </Flex>
{ name } { name }
</Flex> </Flex>
......
...@@ -21,7 +21,7 @@ const SearchBarSuggestApp = ({ data, isMobile, searchTerm, onClick }: Props) => ...@@ -21,7 +21,7 @@ const SearchBarSuggestApp = ({ data, isMobile, searchTerm, onClick }: Props) =>
const logo = ( const logo = (
<Image <Image
borderRadius="base" borderRadius="base"
boxSize={ 6 } boxSize={ 5 }
src={ useColorModeValue(data.logo, data.logoDarkMode || data.logo) } src={ useColorModeValue(data.logo, data.logoDarkMode || data.logo) }
alt={ `${ data.title } app icon` } alt={ `${ data.title } app icon` }
/> />
......
import { Text, Icon, Flex, Grid } from '@chakra-ui/react'; import { Text, Flex, Grid } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SearchResultBlock } from 'types/api/search'; import type { SearchResultBlock } from 'types/api/search';
import blockIcon from 'icons/block.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as BlockEntity from 'ui/shared/entities/block/BlockEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props { interface Props {
...@@ -15,7 +15,7 @@ interface Props { ...@@ -15,7 +15,7 @@ interface Props {
} }
const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => { const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => {
const icon = <Icon as={ blockIcon } boxSize={ 6 } color="gray.500"/>; const icon = <BlockEntity.Icon/>;
const shouldHighlightHash = data.block_hash.toLowerCase() === searchTerm.toLowerCase(); const shouldHighlightHash = data.block_hash.toLowerCase() === searchTerm.toLowerCase();
const blockNumber = ( const blockNumber = (
<Text <Text
...@@ -43,7 +43,7 @@ const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => { ...@@ -43,7 +43,7 @@ const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => {
if (isMobile) { if (isMobile) {
return ( return (
<> <>
<Flex alignItems="center" gap={ 2 }> <Flex alignItems="center">
{ icon } { icon }
{ blockNumber } { blockNumber }
</Flex> </Flex>
...@@ -54,9 +54,11 @@ const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => { ...@@ -54,9 +54,11 @@ const SearchBarSuggestBlock = ({ data, isMobile, searchTerm }: Props) => {
} }
return ( return (
<Grid templateColumns="24px 200px minmax(auto, max-content) auto" gap={ 2 }> <Grid templateColumns="228px minmax(auto, max-content) auto" gap={ 2 }>
<Flex alignItems="center">
{ icon } { icon }
{ blockNumber } { blockNumber }
</Flex>
{ hash } { hash }
<Text variant="secondary" textAlign="end">{ date }</Text> <Text variant="secondary" textAlign="end">{ date }</Text>
</Grid> </Grid>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { SearchResultLabel } from 'types/api/search'; import type { SearchResultLabel } from 'types/api/search';
import labelIcon from 'icons/publictags.svg'; import labelIcon from 'icons/publictags_slim.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
...@@ -15,7 +15,7 @@ interface Props { ...@@ -15,7 +15,7 @@ interface Props {
} }
const SearchBarSuggestLabel = ({ data, isMobile, searchTerm }: Props) => { const SearchBarSuggestLabel = ({ data, isMobile, searchTerm }: Props) => {
const icon = <Icon as={ labelIcon } boxSize={ 6 } color="gray.500"/>; const icon = <Icon as={ labelIcon } boxSize={ 5 } color="gray.500"/>;
const name = ( const name = (
<Text <Text
...@@ -56,9 +56,11 @@ const SearchBarSuggestLabel = ({ data, isMobile, searchTerm }: Props) => { ...@@ -56,9 +56,11 @@ const SearchBarSuggestLabel = ({ data, isMobile, searchTerm }: Props) => {
} }
return ( return (
<Grid alignItems="center" gridTemplateColumns="24px 200px max-content 24px" gap={ 2 }> <Grid alignItems="center" gridTemplateColumns="228px max-content 24px" gap={ 2 }>
<Flex alignItems="center" gap={ 2 }>
{ icon } { icon }
{ name } { name }
</Flex>
<Flex alignItems="center" overflow="hidden" gap={ 1 }> <Flex alignItems="center" overflow="hidden" gap={ 1 }>
{ address } { address }
{ isContractVerified } { isContractVerified }
......
...@@ -6,8 +6,8 @@ import type { SearchResultToken } from 'types/api/search'; ...@@ -6,8 +6,8 @@ import type { SearchResultToken } from 'types/api/search';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import verifiedToken from 'icons/verified_token.svg'; import verifiedToken from 'icons/verified_token.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import TokenLogo from 'ui/shared/TokenLogo';
interface Props { interface Props {
data: SearchResultToken; data: SearchResultToken;
...@@ -16,8 +16,8 @@ interface Props { ...@@ -16,8 +16,8 @@ interface Props {
} }
const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => { const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => {
const icon = <TokenLogo boxSize={ 6 } data={ data }/>; const icon = <TokenEntity.Icon token={ data }/>;
const verifiedIcon = <Icon as={ verifiedToken } boxSize={ 4 } color="green.500"/>; const verifiedIcon = <Icon as={ verifiedToken } boxSize={ 4 } color="green.500" ml={ 1 }/>;
const name = ( const name = (
<Text <Text
fontWeight={ 700 } fontWeight={ 700 }
...@@ -49,13 +49,11 @@ const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => { ...@@ -49,13 +49,11 @@ const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => {
return ( return (
<> <>
<Flex alignItems="center" gap={ 2 }> <Flex alignItems="center">
{ icon } { icon }
<Flex alignItems="center" gap={ 1 }>
{ name } { name }
{ data.is_verified_via_admin_panel && verifiedIcon } { data.is_verified_via_admin_panel && verifiedIcon }
</Flex> </Flex>
</Flex>
<Grid templateColumns={ templateCols } alignItems="center" gap={ 2 }> <Grid templateColumns={ templateCols } alignItems="center" gap={ 2 }>
<Flex alignItems="center" overflow="hidden"> <Flex alignItems="center" overflow="hidden">
{ address } { address }
...@@ -68,9 +66,9 @@ const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => { ...@@ -68,9 +66,9 @@ const SearchBarSuggestToken = ({ data, isMobile, searchTerm }: Props) => {
} }
return ( return (
<Grid templateColumns="24px 200px 1fr auto" gap={ 2 }> <Grid templateColumns="228px 1fr auto" gap={ 2 }>
<Flex alignItems="center">
{ icon } { icon }
<Flex alignItems="center" gap={ 1 }>
{ name } { name }
{ data.is_verified_via_admin_panel && verifiedIcon } { data.is_verified_via_admin_panel && verifiedIcon }
</Flex> </Flex>
......
import { chakra, Grid, Text, Flex, Icon } from '@chakra-ui/react'; import { chakra, Text, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SearchResultTx } from 'types/api/search'; import type { SearchResultTx } from 'types/api/search';
import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import * as TxEntity from 'ui/shared/entities/tx/TxEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
interface Props { interface Props {
...@@ -14,7 +14,7 @@ interface Props { ...@@ -14,7 +14,7 @@ interface Props {
} }
const SearchBarSuggestTx = ({ data, isMobile }: Props) => { const SearchBarSuggestTx = ({ data, isMobile }: Props) => {
const icon = <Icon as={ txIcon } boxSize={ 6 } color="gray.500"/>; const icon = <TxEntity.Icon/>;
const hash = ( const hash = (
<chakra.mark overflow="hidden" whiteSpace="nowrap" fontWeight={ 700 }> <chakra.mark overflow="hidden" whiteSpace="nowrap" fontWeight={ 700 }>
<HashStringShortenDynamic hash={ data.tx_hash } isTooltipDisabled/> <HashStringShortenDynamic hash={ data.tx_hash } isTooltipDisabled/>
...@@ -25,7 +25,7 @@ const SearchBarSuggestTx = ({ data, isMobile }: Props) => { ...@@ -25,7 +25,7 @@ const SearchBarSuggestTx = ({ data, isMobile }: Props) => {
if (isMobile) { if (isMobile) {
return ( return (
<> <>
<Flex alignItems="center" justifyContent="space-between" gap={ 2 }> <Flex alignItems="center">
{ icon } { icon }
{ hash } { hash }
</Flex> </Flex>
...@@ -35,11 +35,13 @@ const SearchBarSuggestTx = ({ data, isMobile }: Props) => { ...@@ -35,11 +35,13 @@ const SearchBarSuggestTx = ({ data, isMobile }: Props) => {
} }
return ( return (
<Grid templateColumns="24px minmax(auto, max-content) auto" gap={ 2 }> <Flex columnGap={ 2 }>
<Flex alignItems="center" minW={ 0 }>
{ icon } { icon }
{ hash } { hash }
<Text variant="secondary" textAlign="end">{ date }</Text> </Flex>
</Grid> <Text variant="secondary" textAlign="end" flexShrink={ 0 } ml="auto">{ date }</Text>
</Flex>
); );
}; };
......
...@@ -4,10 +4,7 @@ import React from 'react'; ...@@ -4,10 +4,7 @@ import React from 'react';
import type { TokenHolder, TokenInfo } from 'types/api/token'; import type { TokenHolder, TokenInfo } from 'types/api/token';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
...@@ -22,19 +19,12 @@ const TokenHoldersListItem = ({ holder, token, isLoading }: Props) => { ...@@ -22,19 +19,12 @@ const TokenHoldersListItem = ({ holder, token, isLoading }: Props) => {
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ holder.address } isLoading={ isLoading }/> address={ holder.address }
<AddressLink
type="address"
ml={ 2 }
fontWeight="700"
hash={ holder.address.hash }
alias={ holder.address.name }
flexGrow={ 1 }
isLoading={ isLoading } isLoading={ isLoading }
fontWeight="700"
maxW="100%"
/> />
<CopyToClipboard text={ holder.address.hash } isLoading={ isLoading }/>
</Address>
<Flex justifyContent="space-between" alignItems="center" width="100%"> <Flex justifyContent="space-between" alignItems="center" width="100%">
<Skeleton isLoaded={ !isLoading } display="inline-block" width="100%"> <Skeleton isLoaded={ !isLoading } display="inline-block" width="100%">
<Box as="span" wordBreak="break-word" mr={ 6 }> <Box as="span" wordBreak="break-word" mr={ 6 }>
......
...@@ -4,10 +4,7 @@ import React from 'react'; ...@@ -4,10 +4,7 @@ import React from 'react';
import type { TokenHolder, TokenInfo } from 'types/api/token'; import type { TokenHolder, TokenInfo } from 'types/api/token';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
type Props = { type Props = {
...@@ -22,20 +19,12 @@ const TokenTransferTableItem = ({ holder, token, isLoading }: Props) => { ...@@ -22,20 +19,12 @@ const TokenTransferTableItem = ({ holder, token, isLoading }: Props) => {
return ( return (
<Tr> <Tr>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ holder.address } isLoading={ isLoading }/> address={ holder.address }
<AddressLink
type="address"
ml={ 2 }
fontWeight="700"
hash={ holder.address.hash }
alias={ holder.address.name }
flexGrow={ 1 }
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant" flexGrow={ 1 }
fontWeight="700"
/> />
<CopyToClipboard text={ holder.address.hash } isLoading={ isLoading }/>
</Address>
</Td> </Td>
<Td verticalAlign="middle" isNumeric> <Td verticalAlign="middle" isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block" wordBreak="break-word"> <Skeleton isLoaded={ !isLoading } display="inline-block" wordBreak="break-word">
......
import { Flex, Text, LinkBox, LinkOverlay, useColorModeValue, Hide, Skeleton } from '@chakra-ui/react'; import { Flex, Text, LinkBox, LinkOverlay, useColorModeValue, Skeleton } from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import React from 'react'; import React from 'react';
import type { TokenInstance } from 'types/api/token'; import type { TokenInstance } from 'types/api/token';
import Address from 'ui/shared/address/Address'; import useIsMobile from 'lib/hooks/useIsMobile';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressLink from 'ui/shared/address/AddressLink';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import NftMedia from 'ui/shared/nft/NftMedia'; import NftMedia from 'ui/shared/nft/NftMedia';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
...@@ -15,6 +14,8 @@ type Props = { item: TokenInstance; isLoading: boolean }; ...@@ -15,6 +14,8 @@ type Props = { item: TokenInstance; isLoading: boolean };
const NFTItem = ({ item, isLoading }: Props) => { const NFTItem = ({ item, isLoading }: Props) => {
const isMobile = useIsMobile();
const mediaElement = ( const mediaElement = (
<NftMedia <NftMedia
mb="18px" mb="18px"
...@@ -67,10 +68,13 @@ const NFTItem = ({ item, isLoading }: Props) => { ...@@ -67,10 +68,13 @@ const NFTItem = ({ item, isLoading }: Props) => {
{ item.owner && ( { item.owner && (
<Flex mb={ 2 } ml={ 1 }> <Flex mb={ 2 } ml={ 1 }>
<Text whiteSpace="pre" variant="secondary" mr={ 2 } lineHeight="24px">Owner</Text> <Text whiteSpace="pre" variant="secondary" mr={ 2 } lineHeight="24px">Owner</Text>
<Address> <AddressEntity
<Hide below="lg" ssr={ false }><AddressIcon address={ item.owner } mr={ 1 } isLoading={ isLoading }/></Hide> address={ item.owner }
<AddressLink hash={ item.owner.hash } alias={ item.owner.name } type="address" truncation="constant" isLoading={ isLoading }/> isLoading={ isLoading }
</Address> truncation="constant"
noCopy
noIcon={ isMobile }
/>
</Flex> </Flex>
) } ) }
</LinkBox> </LinkBox>
......
...@@ -6,12 +6,9 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; ...@@ -6,12 +6,9 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntityWithTokenFilter from 'ui/shared/entities/address/AddressEntityWithTokenFilter';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
...@@ -59,17 +56,21 @@ const TokenTransferListItem = ({ ...@@ -59,17 +56,21 @@ const TokenTransferListItem = ({
</Flex> </Flex>
{ method && <Tag isLoading={ isLoading }>{ method }</Tag> } { method && <Tag isLoading={ isLoading }>{ method }</Tag> }
<Flex w="100%" columnGap={ 3 }> <Flex w="100%" columnGap={ 3 }>
<Address width="50%"> <AddressEntityWithTokenFilter
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink ml={ 2 } fontWeight="500" hash={ from.hash } type="address_token" tokenHash={ token.address } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/> tokenHash={ token.address }
</Address> width="50%"
fontWeight="500"
/>
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/> <Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
<Address width="50%"> <AddressEntityWithTokenFilter
<AddressIcon address={ to } isLoading={ isLoading }/> address={ to }
<AddressLink ml={ 2 } fontWeight="500" hash={ to.hash } type="address_token" tokenHash={ token.address } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ to.hash } isLoading={ isLoading }/> tokenHash={ token.address }
</Address> width="50%"
fontWeight="500"
/>
</Flex> </Flex>
{ value && (token.type === 'ERC-20' || token.type === 'ERC-1155') && ( { value && (token.type === 'ERC-20' || token.type === 'ERC-1155') && (
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
......
...@@ -6,12 +6,9 @@ import type { TokenTransfer } from 'types/api/tokenTransfer'; ...@@ -6,12 +6,9 @@ import type { TokenTransfer } from 'types/api/tokenTransfer';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntityWithTokenFilter from 'ui/shared/entities/address/AddressEntityWithTokenFilter';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
...@@ -57,21 +54,13 @@ const TokenTransferTableItem = ({ ...@@ -57,21 +54,13 @@ const TokenTransferTableItem = ({
) : null } ) : null }
</Td> </Td>
<Td> <Td>
<Address display="inline-flex" maxW="100%" py="3px"> <AddressEntityWithTokenFilter
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink
ml={ 2 }
flexGrow={ 1 }
fontWeight="500"
type="address_token"
hash={ from.hash }
alias={ from.name }
tokenHash={ token.address }
truncation="constant"
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant"
tokenHash={ token.address }
my="5px"
/> />
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/>
</Address>
</Td> </Td>
<Td px={ 0 }> <Td px={ 0 }>
<Box my="3px"> <Box my="3px">
...@@ -79,21 +68,13 @@ const TokenTransferTableItem = ({ ...@@ -79,21 +68,13 @@ const TokenTransferTableItem = ({
</Box> </Box>
</Td> </Td>
<Td> <Td>
<Address display="inline-flex" maxW="100%" py="3px"> <AddressEntityWithTokenFilter
<AddressIcon address={ to } isLoading={ isLoading }/> address={ to }
<AddressLink
ml={ 2 }
flexGrow={ 1 }
fontWeight="500"
type="address_token"
hash={ to.hash }
alias={ to.name }
tokenHash={ token.address }
truncation="constant"
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant"
tokenHash={ token.address }
my="5px"
/> />
<CopyToClipboard text={ to.hash } isLoading={ isLoading }/>
</Address>
</Td> </Td>
{ (token.type === 'ERC-721' || token.type === 'ERC-1155') && ( { (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
<Td> <Td>
......
...@@ -3,15 +3,13 @@ import React from 'react'; ...@@ -3,15 +3,13 @@ import React from 'react';
import type { TokenInstance } from 'types/api/token'; import type { TokenInstance } from 'types/api/token';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem'; import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import NftMedia from 'ui/shared/nft/NftMedia'; import NftMedia from 'ui/shared/nft/NftMedia';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
import TokenInstanceCreatorAddress from './details/TokenInstanceCreatorAddress'; import TokenInstanceCreatorAddress from './details/TokenInstanceCreatorAddress';
import TokenInstanceDivider from './details/TokenInstanceDivider'; import TokenInstanceDivider from './details/TokenInstanceDivider';
...@@ -51,7 +49,11 @@ const TokenInstanceDetails = ({ data, scrollRef, isLoading }: Props) => { ...@@ -51,7 +49,11 @@ const TokenInstanceDetails = ({ data, scrollRef, isLoading }: Props) => {
hint="Token name" hint="Token name"
isLoading={ isLoading } isLoading={ isLoading }
> >
<TokenSnippet data={ data.token } isLoading={ isLoading }/> <TokenEntity
token={ data.token }
isLoading={ isLoading }
noCopy
/>
</DetailsInfoItem> </DetailsInfoItem>
{ data.is_unique && data.owner && ( { data.is_unique && data.owner && (
<DetailsInfoItem <DetailsInfoItem
...@@ -59,11 +61,10 @@ const TokenInstanceDetails = ({ data, scrollRef, isLoading }: Props) => { ...@@ -59,11 +61,10 @@ const TokenInstanceDetails = ({ data, scrollRef, isLoading }: Props) => {
hint="Current owner of this token instance" hint="Current owner of this token instance"
isLoading={ isLoading } isLoading={ isLoading }
> >
<Address> <AddressEntity
<AddressIcon address={ data.owner } isLoading={ isLoading }/> address={ data.owner }
<AddressLink type="address" hash={ data.owner.hash } ml={ 2 } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ data.owner.hash } isLoading={ isLoading }/> />
</Address>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
<TokenInstanceCreatorAddress hash={ isLoading ? '' : data.token.address }/> <TokenInstanceCreatorAddress hash={ isLoading ? '' : data.token.address }/>
......
...@@ -2,11 +2,8 @@ import React from 'react'; ...@@ -2,11 +2,8 @@ import React from 'react';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { ADDRESS_INFO } from 'stubs/address'; import { ADDRESS_INFO } from 'stubs/address';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
interface Props { interface Props {
hash: string; hash: string;
...@@ -41,11 +38,10 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => { ...@@ -41,11 +38,10 @@ const TokenInstanceCreatorAddress = ({ hash }: Props) => {
hint="Address that deployed this token contract" hint="Address that deployed this token contract"
isLoading={ addressQuery.isPlaceholderData } isLoading={ addressQuery.isPlaceholderData }
> >
<Address> <AddressEntity
<AddressIcon address={ creatorAddress } isLoading={ addressQuery.isPlaceholderData }/> address={ creatorAddress }
<AddressLink type="address" hash={ creatorAddress.hash } ml={ 2 } isLoading={ addressQuery.isPlaceholderData }/> isLoading={ addressQuery.isPlaceholderData }
<CopyToClipboard text={ creatorAddress.hash } isLoading={ addressQuery.isPlaceholderData }/> />
</Address>
</DetailsInfoItem> </DetailsInfoItem>
); );
}; };
......
...@@ -5,11 +5,10 @@ import React from 'react'; ...@@ -5,11 +5,10 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TokenLogo from 'ui/shared/TokenLogo';
type Props = { type Props = {
token: TokenInfo; token: TokenInfo;
...@@ -31,14 +30,10 @@ const TokensTableItem = ({ ...@@ -31,14 +30,10 @@ const TokensTableItem = ({
address, address,
exchange_rate: exchangeRate, exchange_rate: exchangeRate,
type, type,
name,
symbol,
holders, holders,
circulating_market_cap: marketCap, circulating_market_cap: marketCap,
} = token; } = token;
const tokenString = [ name, symbol && `(${ symbol })` ].filter(Boolean).join(' ');
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
<Grid <Grid
...@@ -46,25 +41,30 @@ const TokensTableItem = ({ ...@@ -46,25 +41,30 @@ const TokensTableItem = ({
gridTemplateColumns="minmax(0, 1fr)" gridTemplateColumns="minmax(0, 1fr)"
> >
<GridItem display="flex"> <GridItem display="flex">
<Flex overflow="hidden" mr={ 3 } alignItems="center"> <TokenEntity
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> token={ token }
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading } mr={ 3 }/> isLoading={ isLoading }
<Tag flexShrink={ 0 } isLoading={ isLoading }>{ type }</Tag> jointSymbol
</Flex> noCopy
w="auto"
fontSize="sm"
fontWeight="700"
/>
<Tag flexShrink={ 0 } isLoading={ isLoading } ml={ 3 }>{ type }</Tag>
<Skeleton isLoaded={ !isLoading } fontSize="sm" ml="auto" color="text_secondary" minW="24px" textAlign="right" lineHeight={ 6 }> <Skeleton isLoaded={ !isLoading } fontSize="sm" ml="auto" color="text_secondary" minW="24px" textAlign="right" lineHeight={ 6 }>
<span>{ (page - 1) * PAGE_SIZE + index + 1 }</span> <span>{ (page - 1) * PAGE_SIZE + index + 1 }</span>
</Skeleton> </Skeleton>
</GridItem> </GridItem>
</Grid> </Grid>
<Flex justifyContent="space-between" alignItems="center" width="100%"> <Flex justifyContent="space-between" alignItems="center" width="150px" ml={ 7 } mt={ -2 }>
<Flex alignItems="center" width="136px" justifyContent="space-between" ml={ 8 } mt="-8px"> <AddressEntity
<Flex alignItems="center"> address={{ hash: address }}
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ address } isLoading={ isLoading }/> truncation="constant"
</Flex> noIcon
/>
<AddressAddToWallet token={ token } isLoading={ isLoading }/> <AddressAddToWallet token={ token } isLoading={ isLoading }/>
</Flex> </Flex>
</Flex>
{ exchangeRate && ( { exchangeRate && (
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Price</Skeleton> <Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Price</Skeleton>
......
...@@ -4,12 +4,11 @@ import React from 'react'; ...@@ -4,12 +4,11 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import Address from 'ui/shared/address/Address';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import type { EntityProps as AddressEntityProps } from 'ui/shared/entities/address/AddressEntity';
import TokenLogo from 'ui/shared/TokenLogo'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
type Props = { type Props = {
token: TokenInfo; token: TokenInfo;
...@@ -31,13 +30,17 @@ const TokensTableItem = ({ ...@@ -31,13 +30,17 @@ const TokensTableItem = ({
address, address,
exchange_rate: exchangeRate, exchange_rate: exchangeRate,
type, type,
name,
symbol,
holders, holders,
circulating_market_cap: marketCap, circulating_market_cap: marketCap,
} = token; } = token;
const tokenString = [ name, symbol && `(${ symbol })` ].filter(Boolean).join(' '); const tokenAddress: AddressEntityProps['address'] = {
hash: address,
name: '',
implementation_name: null,
is_contract: true,
is_verified: false,
};
return ( return (
<Tr> <Tr>
...@@ -46,7 +49,7 @@ const TokensTableItem = ({ ...@@ -46,7 +49,7 @@ const TokensTableItem = ({
<Skeleton <Skeleton
isLoaded={ !isLoading } isLoaded={ !isLoading }
fontSize="sm" fontSize="sm"
lineHeight="24px" lineHeight="20px"
fontWeight={ 600 } fontWeight={ 600 }
mr={ 3 } mr={ 3 }
minW="28px" minW="28px"
...@@ -54,16 +57,26 @@ const TokensTableItem = ({ ...@@ -54,16 +57,26 @@ const TokensTableItem = ({
{ (page - 1) * PAGE_SIZE + index + 1 } { (page - 1) * PAGE_SIZE + index + 1 }
</Skeleton> </Skeleton>
<Box overflow="hidden"> <Box overflow="hidden">
<Flex alignItems="center"> <TokenEntity
<TokenLogo data={ token } boxSize={ 6 } mr={ 2 } isLoading={ isLoading }/> token={ token }
<AddressLink fontSize="sm" fontWeight="700" hash={ address } type="token" alias={ tokenString } isLoading={ isLoading }/> isLoading={ isLoading }
</Flex> jointSymbol
<Box ml={ 8 } mt={ 2 }> noCopy
<Address> fontSize="sm"
<AddressLink fontSize="sm" hash={ address } type="address" truncation="constant" fontWeight={ 500 } isLoading={ isLoading }/> fontWeight="700"
<CopyToClipboard text={ address } isLoading={ isLoading }/> />
<Box ml={ 7 } mt={ 2 }>
<Flex>
<AddressEntity
address={ tokenAddress }
isLoading={ isLoading }
noIcon
truncation="constant"
fontSize="sm"
fontWeight={ 500 }
/>
<AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/> <AddressAddToWallet token={ token } ml={ 2 } isLoading={ isLoading }/>
</Address> </Flex>
<Box mt={ 3 } > <Box mt={ 3 } >
<Tag isLoading={ isLoading }>{ type }</Tag> <Tag isLoading={ isLoading }>{ type }</Tag>
</Box> </Box>
......
...@@ -6,9 +6,8 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -6,9 +6,8 @@ import type { TokenInfo } from 'types/api/token';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import nftIcon from 'icons/nft_shield.svg'; import nftIcon from 'icons/nft_shield.svg';
import AddressLink from 'ui/shared/address/AddressLink'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet';
interface Props { interface Props {
token: TokenInfo; token: TokenInfo;
...@@ -29,11 +28,10 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => { ...@@ -29,11 +28,10 @@ const NftTokenTransferSnippet = ({ value, token, tokenId }: Props) => {
{ tokenId.length > 8 ? <HashStringShorten hash={ tokenId }/> : tokenId } { tokenId.length > 8 ? <HashStringShorten hash={ tokenId }/> : tokenId }
</Link> </Link>
</Box> </Box>
{ token.name ? ( <TokenEntity
<TokenSnippet data={ token } logoSize={ 5 } columnGap={ 1 }/> token={ token }
) : ( noCopy
<AddressLink hash={ token.address } truncation="constant" type="token"/> />
) }
</Flex> </Flex>
); );
}; };
......
...@@ -26,9 +26,6 @@ import { WEI, WEI_IN_GWEI } from 'lib/consts'; ...@@ -26,9 +26,6 @@ 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';
import getConfirmationDuration from 'lib/tx/getConfirmationDuration'; import getConfirmationDuration from 'lib/tx/getConfirmationDuration';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
...@@ -36,13 +33,12 @@ import CurrencyValue from 'ui/shared/CurrencyValue'; ...@@ -36,13 +33,12 @@ 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';
import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem'; import DetailsSponsoredItem from 'ui/shared/DetailsSponsoredItem';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
// import PrevNext from 'ui/shared/PrevNext';
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData'; import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
import RawInputData from 'ui/shared/RawInputData'; import RawInputData from 'ui/shared/RawInputData';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import TruncatedValue from 'ui/shared/TruncatedValue';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import Utilization from 'ui/shared/Utilization/Utilization'; import Utilization from 'ui/shared/Utilization/Utilization';
import TxDetailsActions from 'ui/tx/details/TxDetailsActions'; import TxDetailsActions from 'ui/tx/details/TxDetailsActions';
...@@ -141,8 +137,6 @@ const TxDetails = () => { ...@@ -141,8 +137,6 @@ const TxDetails = () => {
<HashStringShortenDynamic hash={ data.hash }/> <HashStringShortenDynamic hash={ data.hash }/>
</Skeleton> </Skeleton>
<CopyToClipboard text={ data.hash } isLoading={ isPlaceholderData }/> <CopyToClipboard text={ data.hash } isLoading={ isPlaceholderData }/>
{ /* api doesn't support navigation between certain address account tx */ }
{ /* <PrevNext ml={ 7 }/> */ }
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Status and method" title="Status and method"
...@@ -219,11 +213,10 @@ const TxDetails = () => { ...@@ -219,11 +213,10 @@ const TxDetails = () => {
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
columnGap={ 3 } columnGap={ 3 }
> >
<Address> <AddressEntity
<AddressIcon address={ data.from } isLoading={ isPlaceholderData }/> address={ data.from }
<AddressLink type="address" ml={ 2 } hash={ data.from.hash } isLoading={ isPlaceholderData }/> isLoading={ isPlaceholderData }
<CopyToClipboard text={ data.from.hash } isLoading={ isPlaceholderData }/> />
</Address>
{ data.from.name && <Text>{ data.from.name }</Text> } { data.from.name && <Text>{ data.from.name }</Text> }
{ addressFromTags.length > 0 && ( { addressFromTags.length > 0 && (
<Flex columnGap={ 3 }> <Flex columnGap={ 3 }>
...@@ -241,24 +234,27 @@ const TxDetails = () => { ...@@ -241,24 +234,27 @@ const TxDetails = () => {
{ toAddress ? ( { toAddress ? (
<> <>
{ data.to && data.to.hash ? ( { data.to && data.to.hash ? (
<Address alignItems="center" flexShrink={ 0 } w={{ base: '100%', lg: 'auto' }}> <Flex flexWrap="nowrap" alignItems="center" maxW="100%">
<AddressIcon address={ toAddress } isLoading={ isPlaceholderData }/> <AddressEntity
<AddressLink type="address" ml={ 2 } hash={ toAddress.hash } isLoading={ isPlaceholderData }/> address={ toAddress }
isLoading={ isPlaceholderData }
/>
{ executionSuccessBadge } { executionSuccessBadge }
{ executionFailedBadge } { executionFailedBadge }
<CopyToClipboard text={ toAddress.hash } isLoading={ isPlaceholderData }/> </Flex>
</Address>
) : ( ) : (
<Flex width={{ base: '100%', lg: 'auto' }} whiteSpace="pre" alignItems="center" flexShrink={ 0 }> <Flex width="100%" whiteSpace="pre" alignItems="center" flexShrink={ 0 }>
<span>[Contract </span> <span>[Contract </span>
<AddressLink type="address" hash={ toAddress.hash }/> <AddressEntity
<span> created]</span> address={ toAddress }
isLoading={ isPlaceholderData }
noIcon
/>
<span>created]</span>
{ executionSuccessBadge } { executionSuccessBadge }
{ executionFailedBadge } { executionFailedBadge }
<CopyToClipboard text={ toAddress.hash }/>
</Flex> </Flex>
) } ) }
{ toAddress.name && <TruncatedValue value={ toAddress.name }/> }
{ addressToTags.length > 0 && ( { addressToTags.length > 0 && (
<Flex columnGap={ 3 }> <Flex columnGap={ 3 }>
{ addressToTags } { addressToTags }
...@@ -283,6 +279,7 @@ const TxDetails = () => { ...@@ -283,6 +279,7 @@ const TxDetails = () => {
currency={ config.chain.currency.symbol } currency={ config.chain.currency.symbol }
exchangeRate={ data.exchange_rate } exchangeRate={ data.exchange_rate }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
flexWrap="wrap"
/> />
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
......
...@@ -8,8 +8,8 @@ import { route } from 'nextjs-routes'; ...@@ -8,8 +8,8 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import uniswapIcon from 'icons/uniswap.svg'; import uniswapIcon from 'icons/uniswap.svg';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
interface Props { interface Props {
action: TxAction; action: TxAction;
...@@ -58,34 +58,32 @@ const TxDetailsAction = ({ action }: Props) => { ...@@ -58,34 +58,32 @@ const TxDetailsAction = ({ action }: Props) => {
<Flex flexWrap="wrap" columnGap={ 1 } rowGap={ 2 } alignItems="center"> <Flex flexWrap="wrap" columnGap={ 1 } rowGap={ 2 } alignItems="center">
<chakra.span color="text_secondary">{ text0 }: </chakra.span> <chakra.span color="text_secondary">{ text0 }: </chakra.span>
<chakra.span fontWeight={ 600 }>{ amount0 }</chakra.span> <chakra.span fontWeight={ 600 } mr={ 1 }>{ amount0 }</chakra.span>
<TokenSnippet <TokenEntity
data={ token0 } token={ token0 }
noLink={ data.symbol0 === 'Ether' }
noCopy
noSymbol
w="auto" w="auto"
columnGap={ 1 }
logoSize={ 5 }
isDisabled={ data.symbol0 === 'Ether' }
hideSymbol
maxW="200px" maxW="200px"
flexShrink={ 0 } flexShrink={ 0 }
/> />
<chakra.span color="text_secondary">{ type === 'swap' ? 'For' : 'And' }: </chakra.span> <chakra.span color="text_secondary">{ type === 'swap' ? 'For' : 'And' }: </chakra.span>
<chakra.span fontWeight={ 600 }>{ amount1 }</chakra.span> <chakra.span fontWeight={ 600 } mr={ 1 }>{ amount1 }</chakra.span>
<TokenSnippet <TokenEntity
data={ token1 } token={ token1 }
noLink={ data.symbol1 === 'Ether' }
noCopy
noSymbol
w="auto" w="auto"
columnGap={ 1 }
logoSize={ 5 }
isDisabled={ data.symbol1 === 'Ether' }
hideSymbol
maxW="200px" maxW="200px"
flexShrink={ 0 } flexShrink={ 0 }
/> />
<chakra.span color="text_secondary">{ text1 } </chakra.span> <chakra.span color="text_secondary" mr={ 1 }>{ text1 }</chakra.span>
<Flex columnGap={ 1 }> <Flex columnGap={ 2 }>
<Icon as={ uniswapIcon } boxSize={ 5 } color="white" bgColor="#ff007a" borderRadius="full" p="2px"/> <Icon as={ uniswapIcon } boxSize={ 5 } color="white" bgColor="#ff007a" borderRadius="full" p="2px"/>
<chakra.span color="text_secondary">Uniswap V3</chakra.span> <chakra.span color="text_secondary">Uniswap V3</chakra.span>
</Flex> </Flex>
...@@ -105,16 +103,20 @@ const TxDetailsAction = ({ action }: Props) => { ...@@ -105,16 +103,20 @@ const TxDetailsAction = ({ action }: Props) => {
return ( return (
<div> <div>
<Flex rowGap={ 2 } flexWrap="wrap" alignItems="center" whiteSpace="pre-wrap"> <Flex rowGap={ 2 } flexWrap="wrap" alignItems="center" whiteSpace="pre-wrap">
<chakra.span>Mint of </chakra.span> <chakra.span mr={ 2 }>Mint of</chakra.span>
<TokenSnippet <TokenEntity
data={ token } token={ token }
noCopy
w="auto" w="auto"
columnGap={ 1 }
logoSize={ 5 }
rowGap={ 2 } rowGap={ 2 }
/> />
<chakra.span> to </chakra.span> <chakra.span> to </chakra.span>
<AddressLink hash={ data.to } type="address" truncation="constant"/> <AddressEntity
address={{ hash: data.to }}
truncation="constant"
noIcon
noCopy
/>
</Flex> </Flex>
<Flex columnGap={ 1 } rowGap={ 2 } pl={ 3 } flexDirection="column" mt={ 2 }> <Flex columnGap={ 1 } rowGap={ 2 } pl={ 3 } flexDirection="column" mt={ 2 }>
......
...@@ -5,9 +5,9 @@ import type { TokenTransfer as TTokenTransfer, Erc20TotalPayload, Erc721TotalPay ...@@ -5,9 +5,9 @@ import type { TokenTransfer as TTokenTransfer, Erc20TotalPayload, Erc721TotalPay
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import TokenSnippet from 'ui/shared/TokenSnippet/TokenSnippet'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import NftTokenTransferSnippet from 'ui/tx/NftTokenTransferSnippet'; import NftTokenTransferSnippet from 'ui/tx/NftTokenTransferSnippet';
interface Props { interface Props {
...@@ -25,12 +25,11 @@ const TxDetailsTokenTransfer = ({ data }: Props) => { ...@@ -25,12 +25,11 @@ const TxDetailsTokenTransfer = ({ data }: Props) => {
<Text fontWeight={ 500 } as="span">For:{ space } <Text fontWeight={ 500 } as="span">For:{ space }
<CurrencyValue value={ total.value } exchangeRate={ data.token.exchange_rate } fontWeight={ 600 } decimals={ total.decimals }/> <CurrencyValue value={ total.value } exchangeRate={ data.token.exchange_rate } fontWeight={ 600 } decimals={ total.decimals }/>
</Text> </Text>
<TokenSnippet <TokenEntity
data={ data.token } token={ data.token }
noCopy
w="auto" w="auto"
flexGrow="1" flexGrow="1"
columnGap={ 1 }
logoSize={ 5 }
/> />
</Flex> </Flex>
); );
...@@ -63,17 +62,17 @@ const TxDetailsTokenTransfer = ({ data }: Props) => { ...@@ -63,17 +62,17 @@ const TxDetailsTokenTransfer = ({ data }: Props) => {
return ( return (
<Flex <Flex
alignItems="center" alignItems="flex-start"
flexWrap={{ base: 'wrap', lg: 'nowrap' }} flexWrap={{ base: 'wrap', lg: 'nowrap' }}
columnGap={ 3 } columnGap={ 3 }
rowGap={ 3 } rowGap={ 3 }
flexDir="row" flexDir="row"
w="100%" w="100%"
> >
<Flex alignItems="center"> <Flex alignItems="center" fontWeight="500">
<AddressLink type="address" fontWeight="500" hash={ data.from.hash } truncation="constant"/> <AddressEntity address={ data.from } truncation="constant" noIcon maxW="150px"/>
<Icon as={ rightArrowIcon } boxSize={ 6 } mx={ 2 } color="gray.500"/> <Icon as={ rightArrowIcon } boxSize={ 5 } mx={ 2 } color="gray.500"/>
<AddressLink type="address" fontWeight="500" hash={ data.to.hash } truncation="constant"/> <AddressEntity address={ data.to } truncation="constant" noIcon maxW="150px"/>
</Flex> </Flex>
<Flex flexDir="column" rowGap={ 5 } w="100%" overflow="hidden"> <Flex flexDir="column" rowGap={ 5 } w="100%" overflow="hidden">
{ content } { content }
......
...@@ -6,12 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; ...@@ -6,12 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import eastArrowIcon from 'icons/arrows/east.svg'; import eastArrowIcon from 'icons/arrows/east.svg';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
...@@ -28,19 +25,19 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -28,19 +25,19 @@ const TxInternalsListItem = ({ type, from, to, value, success, error, gas_limit:
{ typeTitle && <Tag colorScheme="cyan" isLoading={ isLoading }>{ typeTitle }</Tag> } { typeTitle && <Tag colorScheme="cyan" isLoading={ isLoading }>{ typeTitle }</Tag> }
<TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/> <TxStatus status={ success ? 'ok' : 'error' } errorText={ error } isLoading={ isLoading }/>
</Flex> </Flex>
<Box w="100%" display="flex" columnGap={ 3 }> <Box w="100%" display="flex" columnGap={ 3 } fontWeight="500">
<Address width="calc((100% - 48px) / 2)"> <AddressEntity
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/> width="calc((100% - 48px) / 2)"
</Address> />
<Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/> <Icon as={ eastArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
{ toData && ( { toData && (
<Address width="calc((100% - 48px) / 2)"> <AddressEntity
<AddressIcon address={ toData } isLoading={ isLoading }/> address={ toData }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ toData.hash } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> width="calc((100% - 48px) / 2)"
</Address> />
) } ) }
</Box> </Box>
<HStack spacing={ 3 }> <HStack spacing={ 3 }>
......
...@@ -6,12 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction'; ...@@ -6,12 +6,9 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app'; import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils'; import { TX_INTERNALS_ITEMS } from 'ui/tx/internals/utils';
...@@ -36,22 +33,20 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit: ...@@ -36,22 +33,20 @@ const TxInternalTableItem = ({ type, from, to, value, success, error, gas_limit:
</Flex> </Flex>
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ from } isLoading={ isLoading }/> address={ from }
<AddressLink type="address" ml={ 2 } fontWeight="500" hash={ from.hash } alias={ from.name } flexGrow={ 1 } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ from.hash } isLoading={ isLoading }/> />
</Address>
</Td> </Td>
<Td px={ 0 } verticalAlign="middle"> <Td px={ 0 } verticalAlign="middle">
<Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/> <Icon as={ rightArrowIcon } boxSize={ 6 } color="gray.500" isLoading={ isLoading }/>
</Td> </Td>
<Td verticalAlign="middle"> <Td verticalAlign="middle">
{ toData && ( { toData && (
<Address display="inline-flex" maxW="100%"> <AddressEntity
<AddressIcon address={ toData } isLoading={ isLoading }/> address={ toData }
<AddressLink type="address" hash={ toData.hash } alias={ toData.name } fontWeight="500" ml={ 2 } isLoading={ isLoading }/> isLoading={ isLoading }
<CopyToClipboard text={ toData.hash } isLoading={ isLoading }/> />
</Address>
) } ) }
</Td> </Td>
<Td isNumeric verticalAlign="middle"> <Td isNumeric verticalAlign="middle">
......
...@@ -2,9 +2,7 @@ import React from 'react'; ...@@ -2,9 +2,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges'; import type { TxStateChange } from 'types/api/txStateChanges';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import { getStateElements } from './utils'; import { getStateElements } from './utils';
...@@ -22,12 +20,12 @@ const TxStateListItem = ({ data, isLoading }: Props) => { ...@@ -22,12 +20,12 @@ const TxStateListItem = ({ data, isLoading }: Props) => {
<ListItemMobileGrid.Container> <ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value py="3px" display="flex" flexWrap="nowrap" columnGap={ 3 }>
<Address flexGrow={ 1 } w="100%" alignSelf="center"> <AddressEntity
<AddressIcon address={ data.address } isLoading={ isLoading }/> address={ data.address }
<AddressLink type="address" hash={ data.address.hash } ml={ 2 } truncation="constant" mr={ 3 } isLoading={ isLoading }/> isLoading={ isLoading }
/>
{ tag } { tag }
</Address>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
{ before && ( { before && (
......
...@@ -3,9 +3,7 @@ import React from 'react'; ...@@ -3,9 +3,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges'; import type { TxStateChange } from 'types/api/txStateChanges';
import Address from 'ui/shared/address/Address'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import { getStateElements } from './utils'; import { getStateElements } from './utils';
...@@ -25,18 +23,12 @@ const TxStateTableItem = ({ data, isLoading }: Props) => { ...@@ -25,18 +23,12 @@ const TxStateTableItem = ({ data, isLoading }: Props) => {
</Box> </Box>
</Td> </Td>
<Td> <Td>
<Address py="3px"> <AddressEntity
<AddressIcon address={ data.address } isLoading={ isLoading }/> address={ data.address }
<AddressLink
type="address"
hash={ data.address.hash }
alias={ data.address.name }
fontWeight="500"
truncation="constant"
ml={ 2 }
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant"
py="7px"
/> />
</Address>
</Td> </Td>
<Td isNumeric><Box py="7px">{ before }</Box></Td> <Td isNumeric><Box py="7px">{ before }</Box></Td>
<Td isNumeric><Box py="7px">{ after }</Box></Td> <Td isNumeric><Box py="7px">{ after }</Box></Td>
......
...@@ -8,8 +8,8 @@ import config from 'configs/app'; ...@@ -8,8 +8,8 @@ import config from 'configs/app';
import { ZERO_ADDRESS } from 'lib/consts'; import { ZERO_ADDRESS } from 'lib/consts';
import { nbsp, space } from 'lib/html-entities'; import { nbsp, space } from 'lib/html-entities';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import AddressLink from 'ui/shared/address/AddressLink';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft'; import TokenTransferNft from 'ui/shared/TokenTransfer/TokenTransferNft';
import TxStateTokenIdList from './TxStateTokenIdList'; import TxStateTokenIdList from './TxStateTokenIdList';
...@@ -77,11 +77,13 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { ...@@ -77,11 +77,13 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
} }
case 'token': { case 'token': {
const tokenLink = ( const tokenLink = (
<AddressLink <TokenEntity
type="token" token={ data.token }
hash={ data.token.address }
alias={ data.token?.symbol || data.token.address }
isLoading={ isLoading } isLoading={ isLoading }
noIcon
noCopy
onlySymbol
w="auto"
/> />
); );
const beforeBn = BigNumber(data.balance_before || '0').div(BigNumber(10 ** (Number(data.token.decimals)))); const beforeBn = BigNumber(data.balance_before || '0').div(BigNumber(10 ** (Number(data.token.decimals))));
...@@ -130,14 +132,14 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) { ...@@ -130,14 +132,14 @@ export function getStateElements(data: TxStateChange, isLoading?: boolean) {
return { return {
before: data.balance_before ? ( before: data.balance_before ? (
<Flex whiteSpace="pre-wrap" justifyContent={{ base: 'flex-start', lg: 'flex-end' }}> <Flex whiteSpace="pre-wrap" justifyContent={{ base: 'flex-start', lg: 'flex-end' }} flexWrap="wrap">
<Skeleton isLoaded={ !isLoading }>{ beforeBn.toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading }>{ beforeBn.toFormat() }</Skeleton>
<span>{ space }</span> <span>{ space }</span>
{ tokenLink } { tokenLink }
</Flex> </Flex>
) : null, ) : null,
after: data.balance_after ? ( after: data.balance_after ? (
<Flex whiteSpace="pre-wrap" justifyContent={{ base: 'flex-start', lg: 'flex-end' }}> <Flex whiteSpace="pre-wrap" justifyContent={{ base: 'flex-start', lg: 'flex-end' }} flexWrap="wrap">
<Skeleton isLoaded={ !isLoading }>{ afterBn.toFormat() }</Skeleton> <Skeleton isLoaded={ !isLoading }>{ afterBn.toFormat() }</Skeleton>
<span>{ space }</span> <span>{ space }</span>
{ tokenLink } { tokenLink }
......
...@@ -12,11 +12,8 @@ import config from 'configs/app'; ...@@ -12,11 +12,8 @@ import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import getValueWithUnit from 'lib/getValueWithUnit'; import getValueWithUnit from 'lib/getValueWithUnit';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
...@@ -91,19 +88,14 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -91,19 +88,14 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Flex> </Flex>
) } ) }
<Flex alignItems="center" height={ 6 } mt={ 6 }> <Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }> <AddressEntity
<AddressIcon address={ tx.from } isLoading={ isLoading }/> address={ tx.from }
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500"
ml={ 2 }
isDisabled={ isOut }
isLoading={ isLoading } isLoading={ isLoading }
noLink={ isOut }
noCopy={ isOut }
w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }
fontWeight="500"
/> />
{ !isOut && <CopyToClipboard text={ tx.from.hash } isLoading={ isLoading }/> }
</Address>
{ (isIn || isOut) ? { (isIn || isOut) ?
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" mx={ 2 } isLoading={ isLoading }/> : ( <InOutTag isIn={ isIn } isOut={ isOut } width="48px" mx={ 2 } isLoading={ isLoading }/> : (
<Box mx={ 2 }> <Box mx={ 2 }>
...@@ -116,19 +108,14 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI ...@@ -116,19 +108,14 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
</Box> </Box>
) } ) }
{ dataTo ? ( { dataTo ? (
<Address w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }> <AddressEntity
<AddressIcon address={ dataTo } isLoading={ isLoading }/> address={ dataTo }
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
isDisabled={ isIn }
isLoading={ isLoading } isLoading={ isLoading }
noLink={ isIn }
noCopy={ isIn }
w={ `calc((100% - ${ currentAddress ? TAG_WIDTH + 16 : ARROW_WIDTH + 8 }px)/2)` }
fontWeight="500"
/> />
{ !isIn && <CopyToClipboard text={ dataTo.hash } isLoading={ isLoading }/> }
</Address>
) : '-' } ) : '-' }
</Flex> </Flex>
<Box mt={ 2 }> <Box mt={ 2 }>
......
...@@ -15,13 +15,10 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -15,13 +15,10 @@ import type { Transaction } from 'types/api/transaction';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement'; import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import InOutTag from 'ui/shared/InOutTag'; import InOutTag from 'ui/shared/InOutTag';
...@@ -46,36 +43,27 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -46,36 +43,27 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
const timeAgo = useTimeAgoIncrement(tx.timestamp, enableTimeIncrement); const timeAgo = useTimeAgoIncrement(tx.timestamp, enableTimeIncrement);
const addressFrom = ( const addressFrom = (
<Address w="100%"> <AddressEntity
<AddressIcon address={ tx.from } isLoading={ isLoading }/> address={ tx.from }
<AddressLink
type="address"
hash={ tx.from.hash }
alias={ tx.from.name }
fontWeight="500" ml={ 2 }
truncation="constant"
isDisabled={ isOut }
isLoading={ isLoading } isLoading={ isLoading }
noCopy={ isOut }
noLink={ isOut }
truncation="constant"
w="100%"
py="2px"
/> />
{ !isOut && <CopyToClipboard text={ tx.from.hash } isLoading={ isLoading }/> }
</Address>
); );
const addressTo = dataTo ? ( const addressTo = dataTo ? (
<Address w="100%"> <AddressEntity
<AddressIcon address={ dataTo } isLoading={ isLoading }/> address={ dataTo }
<AddressLink
type="address"
hash={ dataTo.hash }
alias={ dataTo.name }
fontWeight="500"
ml={ 2 }
truncation="constant"
isDisabled={ isIn }
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant"
noCopy={ isIn }
noLink={ isIn }
w="100%"
py="2px"
/> />
{ !isIn && <CopyToClipboard text={ dataTo.hash } isLoading={ isLoading }/> }
</Address>
) : '-'; ) : '-';
return ( return (
......
...@@ -5,12 +5,12 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account'; ...@@ -5,12 +5,12 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import editIcon from 'icons/edit.svg'; import editIcon from 'icons/edit.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
import VerifiedAddressesStatus from './VerifiedAddressesStatus'; import VerifiedAddressesStatus from './VerifiedAddressesStatus';
import VerifiedAddressesTokenSnippet from './VerifiedAddressesTokenSnippet';
interface Props { interface Props {
item: VerifiedAddress; item: VerifiedAddress;
...@@ -48,9 +48,21 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading ...@@ -48,9 +48,21 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
return <Link onClick={ handleAddClick }>Add details</Link>; return <Link onClick={ handleAddClick }>Add details</Link>;
} }
const token = {
icon_url: application.iconUrl,
address: application.tokenAddress,
name: item.metadata.tokenName,
symbol: '',
};
return ( return (
<> <>
<VerifiedAddressesTokenSnippet application={ application } name={ item.metadata.tokenName }/> <TokenEntity
token={ token }
noLink={ application.status === 'IN_PROCESS' }
noCopy
noSymbol
/>
<Tooltip label="Edit"> <Tooltip label="Edit">
<IconButton <IconButton
aria-label="edit" aria-label="edit"
...@@ -69,14 +81,18 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading ...@@ -69,14 +81,18 @@ const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading
return ( return (
<ListItemMobileGrid.Container> <ListItemMobileGrid.Container>
<ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Address</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py="3px"> <ListItemMobileGrid.Value>
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }} isLoading={ isLoading }/> <AddressEntity
address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}
isLoading={ isLoading }
w="100%"
/>
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
{ item.metadata.tokenName && ( { item.metadata.tokenName && (
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>Token Info</ListItemMobileGrid.Label> <ListItemMobileGrid.Label isLoading={ isLoading }>Token Info</ListItemMobileGrid.Label>
<ListItemMobileGrid.Value py={ application ? '3px' : '5px' } display="flex" alignItems="center"> <ListItemMobileGrid.Value display="flex" alignItems="center">
{ tokenInfo } { tokenInfo }
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
</> </>
......
...@@ -5,11 +5,11 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account'; ...@@ -5,11 +5,11 @@ import type { TokenInfoApplication, VerifiedAddress } from 'types/api/account';
import editIcon from 'icons/edit.svg'; import editIcon from 'icons/edit.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import VerifiedAddressesStatus from './VerifiedAddressesStatus'; import VerifiedAddressesStatus from './VerifiedAddressesStatus';
import VerifiedAddressesTokenSnippet from './VerifiedAddressesTokenSnippet';
interface Props { interface Props {
item: VerifiedAddress; item: VerifiedAddress;
...@@ -48,13 +48,31 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin ...@@ -48,13 +48,31 @@ const VerifiedAddressesTableItem = ({ item, application, onAdd, onEdit, isLoadin
return <Link onClick={ handleAddClick }>Add details</Link>; return <Link onClick={ handleAddClick }>Add details</Link>;
} }
return <VerifiedAddressesTokenSnippet application={ application } name={ item.metadata.tokenName }/>; const token = {
icon_url: application.iconUrl,
address: application.tokenAddress,
name: item.metadata.tokenName,
symbol: '',
};
return (
<TokenEntity
token={ token }
noLink={ application.status === 'IN_PROCESS' }
noCopy
noSymbol
/>
);
})(); })();
return ( return (
<Tr> <Tr>
<Td> <Td>
<AddressSnippet address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }} isLoading={ isLoading }/> <AddressEntity
address={{ hash: item.contractAddress, is_contract: true, implementation_name: null }}
isLoading={ isLoading }
fontWeight="600"
/>
</Td> </Td>
<Td fontSize="sm" verticalAlign="middle" pr={ 1 }> <Td fontSize="sm" verticalAlign="middle" pr={ 1 }>
{ tokenInfo } { tokenInfo }
......
import { Image, Flex } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfoApplication } from 'types/api/account';
import AddressLink from 'ui/shared/address/AddressLink';
import TokenLogoPlaceholder from 'ui/shared/TokenLogoPlaceholder';
interface Props {
application: TokenInfoApplication;
name: string;
}
const VerifiedAddressesTokenSnippet = ({ application, name }: Props) => {
return (
<Flex alignItems="center" columnGap={ 2 } w="100%">
<Image
borderRadius="base"
boxSize={ 6 }
objectFit="cover"
src={ application.iconUrl }
alt="Token logo"
fallback={ <TokenLogoPlaceholder boxSize={ 6 }/> }
/>
<AddressLink
hash={ application.tokenAddress }
alias={ name }
type="token"
isDisabled={ application.status === 'IN_PROCESS' }
fontWeight={ 500 }
/>
</Flex>
);
};
export default React.memo(VerifiedAddressesTokenSnippet);
...@@ -9,11 +9,9 @@ import iconCheck from 'icons/check.svg'; ...@@ -9,11 +9,9 @@ import iconCheck from 'icons/check.svg';
import iconCross from 'icons/cross.svg'; import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
...@@ -29,14 +27,18 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => { ...@@ -29,14 +27,18 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => {
return ( return (
<ListItemMobile rowGap={ 3 }> <ListItemMobile rowGap={ 3 }>
<Address columnGap={ 2 } overflow="hidden" w="100%"> <Flex w="100%">
<AddressIcon address={ data.address } isLoading={ isLoading }/> <AddressEntity
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name } isLoading={ isLoading } query={{ tab: 'contract' }}/> isLoading={ isLoading }
address={ data.address }
query={{ tab: 'contract' }}
noCopy
/>
<Skeleton isLoaded={ !isLoading } color="text_secondary" ml="auto"> <Skeleton isLoaded={ !isLoading } color="text_secondary" ml="auto">
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/> <HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
</Skeleton> </Skeleton>
<CopyToClipboard text={ data.address.hash } ml={ -1 } isLoading={ isLoading }/> <CopyToClipboard text={ data.address.hash } isLoading={ isLoading }/>
</Address> </Flex>
<Flex columnGap={ 3 }> <Flex columnGap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Balance { config.chain.currency.symbol }</Skeleton> <Skeleton isLoaded={ !isLoading } fontWeight={ 500 }>Balance { config.chain.currency.symbol }</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary"> <Skeleton isLoaded={ !isLoading } color="text_secondary">
......
...@@ -9,10 +9,9 @@ import iconCheck from 'icons/check.svg'; ...@@ -9,10 +9,9 @@ import iconCheck from 'icons/check.svg';
import iconCross from 'icons/cross.svg'; import iconCross from 'icons/cross.svg';
import iconSuccess from 'icons/status/success.svg'; import iconSuccess from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import HashStringShorten from 'ui/shared/HashStringShorten'; import HashStringShorten from 'ui/shared/HashStringShorten';
interface Props { interface Props {
...@@ -28,18 +27,19 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => { ...@@ -28,18 +27,19 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => {
return ( return (
<Tr> <Tr>
<Td> <Td>
<Flex columnGap={ 2 }> <AddressEntity
<AddressIcon address={ data.address } isLoading={ isLoading }/> address={ data.address }
<Flex columnGap={ 2 } flexWrap="wrap" w="calc(100% - 32px)"> isLoading={ isLoading }
<AddressLink hash={ data.address.hash } type="address" alias={ data.address.name } isLoading={ isLoading } my={ 1 } query={{ tab: 'contract' }}/> query={{ tab: 'contract' }}
<Flex alignItems="center"> noCopy
mt={ 1 }
/>
<Flex alignItems="center" ml={ 7 }>
<Skeleton isLoaded={ !isLoading } color="text_secondary" my={ 1 }> <Skeleton isLoaded={ !isLoading } color="text_secondary" my={ 1 }>
<HashStringShorten hash={ data.address.hash } isTooltipDisabled/> <HashStringShorten hash={ data.address.hash } isTooltipDisabled/>
</Skeleton> </Skeleton>
<CopyToClipboard text={ data.address.hash } isLoading={ isLoading }/> <CopyToClipboard text={ data.address.hash } isLoading={ isLoading }/>
</Flex> </Flex>
</Flex>
</Flex>
</Td> </Td>
<Td isNumeric> <Td isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block" my={ 1 }> <Skeleton isLoaded={ !isLoading } display="inline-block" my={ 1 }>
......
...@@ -9,10 +9,10 @@ import TokensIcon from 'icons/tokens.svg'; ...@@ -9,10 +9,10 @@ import TokensIcon from 'icons/tokens.svg';
import WalletIcon from 'icons/wallet.svg'; import WalletIcon from 'icons/wallet.svg';
import getCurrencyValue from 'lib/getCurrencyValue'; import getCurrencyValue from 'lib/getCurrencyValue';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import AddressSnippet from 'ui/shared/AddressSnippet';
import Icon from 'ui/shared/chakra/Icon'; import Icon from 'ui/shared/chakra/Icon';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import TokenLogo from 'ui/shared/TokenLogo'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isLoading?: boolean }) => { const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isLoading?: boolean }) => {
const infoItemsPaddingLeft = { base: 1, lg: 8 }; const infoItemsPaddingLeft = { base: 1, lg: 8 };
...@@ -20,19 +20,23 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL ...@@ -20,19 +20,23 @@ const WatchListAddressItem = ({ item, isLoading }: { item: WatchlistAddress; isL
const nativeTokenData = React.useMemo(() => ({ const nativeTokenData = React.useMemo(() => ({
name: config.chain.currency.name || '', name: config.chain.currency.name || '',
icon_url: '', icon_url: '',
symbol: '',
address: '',
}), [ ]); }), [ ]);
const { usdBn: usdNative } = getCurrencyValue({ value: item.address_balance, accuracy: 2, accuracyUsd: 2, exchangeRate: item.exchange_rate }); const { usdBn: usdNative } = getCurrencyValue({ value: item.address_balance, accuracy: 2, accuracyUsd: 2, exchangeRate: item.exchange_rate });
return ( return (
<VStack spacing={ 2 } align="stretch" fontWeight={ 500 }> <VStack spacing={ 2 } align="stretch" fontWeight={ 500 }>
<AddressSnippet address={ item.address } isLoading={ isLoading }/> <AddressEntity
address={ item.address }
isLoading={ isLoading }
fontWeight="600"
py="2px"
/>
<Flex fontSize="sm" pl={ infoItemsPaddingLeft } flexWrap="wrap" alignItems="center" rowGap={ 1 }> <Flex fontSize="sm" pl={ infoItemsPaddingLeft } flexWrap="wrap" alignItems="center" rowGap={ 1 }>
<TokenLogo <TokenEntity.Icon
data={ nativeTokenData } token={ nativeTokenData }
boxSize={ 5 }
borderRadius="sm"
mr={ 2 }
isLoading={ isLoading } isLoading={ isLoading }
/> />
<Skeleton isLoaded={ !isLoading } whiteSpace="pre" display="inline-flex"> <Skeleton isLoaded={ !isLoading } whiteSpace="pre" display="inline-flex">
......
...@@ -7,10 +7,8 @@ import type { WithdrawalsItem } from 'types/api/withdrawals'; ...@@ -7,10 +7,8 @@ import type { WithdrawalsItem } from 'types/api/withdrawals';
import config from 'configs/app'; import config from 'configs/app';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid'; import ListItemMobileGrid from 'ui/shared/ListItemMobile/ListItemMobileGrid';
...@@ -62,10 +60,10 @@ const WithdrawalsListItem = ({ item, isLoading, view }: Props) => { ...@@ -62,10 +60,10 @@ const WithdrawalsListItem = ({ item, isLoading, view }: Props) => {
{ view !== 'address' && ( { view !== 'address' && (
<> <>
<ListItemMobileGrid.Label isLoading={ isLoading }>To</ListItemMobileGrid.Label><ListItemMobileGrid.Value> <ListItemMobileGrid.Label isLoading={ isLoading }>To</ListItemMobileGrid.Label><ListItemMobileGrid.Value>
<Address> <AddressEntity
<AddressIcon address={ item.receiver } isLoading={ isLoading }/> address={ item.receiver }
<AddressLink type="address" hash={ item.receiver.hash } truncation="dynamic" ml={ 2 } isLoading={ isLoading }/> isLoading={ isLoading }
</Address> />
</ListItemMobileGrid.Value> </ListItemMobileGrid.Value>
</> </>
) } ) }
......
...@@ -6,10 +6,8 @@ import type { BlockWithdrawalsItem } from 'types/api/block'; ...@@ -6,10 +6,8 @@ import type { BlockWithdrawalsItem } from 'types/api/block';
import type { WithdrawalsItem } from 'types/api/withdrawals'; import type { WithdrawalsItem } from 'types/api/withdrawals';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue'; import CurrencyValue from 'ui/shared/CurrencyValue';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
type Props = ({ type Props = ({
...@@ -44,10 +42,11 @@ const WithdrawalsTableItem = ({ item, view, isLoading }: Props) => { ...@@ -44,10 +42,11 @@ const WithdrawalsTableItem = ({ item, view, isLoading }: Props) => {
) } ) }
{ view !== 'address' && ( { view !== 'address' && (
<Td verticalAlign="middle"> <Td verticalAlign="middle">
<Address> <AddressEntity
<AddressIcon address={ item.receiver } isLoading={ isLoading }/> address={ item.receiver }
<AddressLink type="address" hash={ item.receiver.hash } truncation="constant" ml={ 2 } isLoading={ isLoading }/> isLoading={ isLoading }
</Address> truncation="constant"
/>
</Td> </Td>
) } ) }
{ view !== 'block' && ( { view !== 'block' && (
......
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